-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial implementation with original Delphi source code.
- Loading branch information
0 parents
commit 0c1b61e
Showing
3 changed files
with
342 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
Wwise *.BNK File Extractor | ||
========================== | ||
|
||
This is a C++ rewrite of **bnkextr** originally written by CTPAX-X in Delphi. | ||
It extracts `WEM` files from the ever more popular Wwise `BNK` format. | ||
Use [ww2ogg](https://github.com/hcs64/ww2ogg) to convert `WEM` files to the `OGG` format. | ||
|
||
|
||
``` | ||
Usage: bnkextr filename.bnk [/swap] | ||
/swap - swap byte order (use it for unpacking 'Army of Two') | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
/* | ||
http://www.geocities.jp/aoyoume/aotuv/index.html | ||
http://rarewares.org/ogg-oggenc.php#oggenc-aotuv | ||
http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1018956 | ||
http://forum.xentax.com/viewtopic.php?f=17&t=3477 | ||
http://wiki.xentax.com/index.php?title=Wwise_SoundBank_(*.bnk) | ||
.BNK Format specifications | ||
char {4} - header (BKHD) // BanK HeaDer | ||
uint32 {4} - size of BKHD | ||
uint32 {4} - unknown (version?) | ||
uint32 {4} - unknown | ||
uint32 {4} - unknown | ||
uint32 {4} - unknown | ||
byte {x} - zero padding (if any) | ||
char {4} - header (DIDX) // Data InDeX | ||
uint32 {4} - size of DIDX | ||
following by records 12 bytes each: | ||
uint32 {4} - unknown | ||
uint32 {4} - relative file offset from start of DATA, 16 bytes aligned | ||
uint32 {4} - file size | ||
char {4} - header (DATA) | ||
uint32 {4} - size of DATA | ||
char {4} - header (HIRC) // ??? | ||
uint32 {4} - size of HIRC | ||
char {4} - header (STID) // Sound Type ID | ||
uint32 {4} - size of STID | ||
uint32 {4} - Always 1? | ||
uint32 {4} - Always 1? | ||
uint32 {4} - unknown | ||
byte {1} - TID Length (TL) | ||
char {TL} - TID string (usually same as filename, but without extension) | ||
Init.bnk | ||
STMG | ||
HIRC | ||
FXPR | ||
ENVS | ||
*/ | ||
|
||
#include <cstring> | ||
#include <fstream> | ||
#include <iostream> | ||
#include <vector> | ||
|
||
struct Index; | ||
struct Section; | ||
|
||
#pragma pack(push, 1) | ||
struct Index | ||
{ | ||
int unknown; | ||
int offset; | ||
unsigned int size; | ||
}; | ||
|
||
#pragma pack(push, 1) | ||
struct Section | ||
{ | ||
char sign[4]; | ||
unsigned int size; | ||
}; | ||
#pragma pack(pop) | ||
#pragma pack(pop) | ||
|
||
int swap32(const int dw) | ||
{ | ||
#ifdef __GNUC__ | ||
return __builtin_bswap32(dw); | ||
#elif _MSC_VER | ||
return _byteswap_ulong(dw); | ||
#endif | ||
} | ||
|
||
std::string zero_padding(unsigned int number) | ||
{ | ||
if(number < 10) | ||
return "00" + std::to_string(number); | ||
else if(number < 100) | ||
return "0" + std::to_string(number); | ||
else | ||
return std::to_string(number); | ||
} | ||
|
||
int main(int argc, char* argv[]) | ||
{ | ||
std::cout << "Wwise *.BNK File Extractor" << std::endl; | ||
std::cout << "(c) CTPAX-X Team 2009-2010 - http://www.CTPAX-X.org" << std::endl; | ||
std::cout << "(c) RAWR 2015 - http://www.rawr4firefall.com" << std::endl; | ||
std::cout << std::endl; | ||
|
||
// Has no argument(s) | ||
if((argc < 2) || (argc > 3)) | ||
{ | ||
std::cout << "Usage: bnkextr filename.bnk [/swap]" << std::endl; | ||
std::cout << "/swap - swap byte order (use it for unpacking 'Army of Two')" << std::endl; | ||
return 0; | ||
} | ||
|
||
std::fstream bnkfile; | ||
bnkfile.open(argv[1], std::ios::binary | std::ios::in); | ||
|
||
// Could not open BNK file | ||
if(!bnkfile.is_open()) | ||
{ | ||
std::cout << "Can't open input file: " << argv[1] << std::endl; | ||
return 0; | ||
} | ||
|
||
unsigned int data_pos = 0; | ||
std::vector<Index> files; | ||
Section content_section; | ||
Index content_index; | ||
|
||
while(bnkfile.read(reinterpret_cast<char*>(&content_section), sizeof(content_section))) | ||
{ | ||
unsigned int section_pos = bnkfile.tellg(); | ||
|
||
// Was the /swap command used? | ||
if(argc > 3) | ||
content_section.size = swap32(content_section.size); | ||
|
||
if(std::strncmp(content_section.sign, "DIDX", 4) == 0) | ||
{ | ||
// Read files | ||
for(unsigned int i = 0; i < content_section.size; i += sizeof(content_index)) | ||
{ | ||
bnkfile.read(reinterpret_cast<char*>(&content_index), sizeof(content_index)); | ||
files.push_back(content_index); | ||
} | ||
} | ||
else if(std::strncmp(content_section.sign, "STID", 4) == 0) | ||
{ | ||
// To be implemented | ||
} | ||
else if(std::strncmp(content_section.sign, "DATA", 4) == 0) | ||
{ | ||
// Get DATA offset | ||
data_pos = bnkfile.tellg(); | ||
} | ||
|
||
// Seek to the end of the section | ||
bnkfile.seekg(section_pos + content_section.size); | ||
} | ||
|
||
// Reset EOF | ||
bnkfile.clear(); | ||
|
||
// Extract files | ||
if((data_pos > 0) && (files.size() > 0)) | ||
{ | ||
for(std::size_t i = 0; i < files.size(); ++i) | ||
{ | ||
std::string filename = zero_padding(i + 1) + ".wem"; | ||
|
||
std::fstream wemfile; | ||
wemfile.open(filename, std::ios::out | std::ios::binary); | ||
|
||
// Was the /swap command used? | ||
if(argc > 3) | ||
{ | ||
files[i].size = swap32(files[i].size); | ||
files[i].offset = swap32(files[i].offset); | ||
} | ||
|
||
if(wemfile.is_open()) | ||
{ | ||
std::vector<char> data(files[i].size, 0); | ||
|
||
bnkfile.seekg(data_pos + files[i].offset); | ||
bnkfile.read(static_cast<char*>(data.data()), files[i].size); | ||
wemfile.write(static_cast<char*>(data.data()), files[i].size); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
Program bnktest; | ||
{$APPTYPE CONSOLE} | ||
(* | ||
http://www.geocities.jp/aoyoume/aotuv/index.html | ||
http://rarewares.org/ogg-oggenc.php#oggenc-aotuv | ||
http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1018956 | ||
http://forum.xentax.com/viewtopic.php?f=17&t=3477 | ||
.BNK Format specifications | ||
char {4} - header (BKHD) // BanK HeaDer | ||
uint32 {4} - size of BKHD | ||
uint32 {4} - unknow (version?) | ||
uint32 {4} - unknow | ||
uint32 {4} - unknow | ||
uint32 {4} - unknow | ||
byte {x} - zero padding (if any) | ||
char {4} - header (DIDX) // Data InDeX | ||
uint32 {4} - size of DIDX | ||
following by records 12 bytes each: | ||
uint32 {4} - unknow | ||
uint32 {4} - relative file offset from start of DATA, 16 bytes aligned | ||
uint32 {4} - file size | ||
char {4} - header (DATA) | ||
uint32 {4} - size of DATA | ||
char {4} - header (HIRC) // ??? | ||
uint32 {4} - size of HIRC | ||
char {4} - header (STID) // Sound Type ID | ||
uint32 {4} - size of STID | ||
uint32 {4} - Always 1? | ||
uint32 {4} - Always 1? | ||
uint32 {4} - unknow | ||
byte {1} - TID Length (TL) | ||
char {TL} - TID string (usually same as filename, but without extension) | ||
Init.bnk | ||
STMG | ||
HIRC | ||
FXPR | ||
ENVS | ||
*) | ||
Type | ||
TSect = Packed Record | ||
Sign: Array[0..3] Of Char; | ||
Size: Integer; | ||
End; | ||
|
||
TIDX = Packed Record | ||
Unkn: Integer; | ||
Offs: Integer; | ||
Size: Integer; | ||
End; | ||
|
||
Var | ||
I, DT: Integer; | ||
S, SI: String; | ||
Fl, F: File; | ||
FR: Array Of TIDX; | ||
CS: TSect; | ||
P: Pointer; | ||
|
||
function swap32(const dw: longint): longint; assembler; | ||
asm | ||
bswap eax | ||
end; | ||
|
||
Function Int03Str(N: Integer): String; | ||
Begin | ||
Str(N, result); | ||
While Length(result) < 3 Do result:='0' + result; | ||
End; | ||
|
||
Begin | ||
WriteLn('Divinity 2: Ego Draconis / Army of Two .BNK extractor'); | ||
WriteLn('(c) CTPAX-X Team 2009-2010'); | ||
WriteLn('http://www.CTPAX-X.org'); | ||
WriteLn; | ||
If ((ParamCount < 1) Or (ParamCount > 2)) Then | ||
Begin | ||
WriteLn('Usage: bnkextr filename.bnk [/swap]'); | ||
WriteLn('/swap - swap byte order (use it for unpacking AoT)'); | ||
Exit; | ||
End; | ||
AssignFile(Fl, ParamStr(1)); | ||
FileMode:=0; | ||
{$I-} | ||
Reset(Fl, 1); | ||
{$I+} | ||
FileMode:=2; | ||
If IOResult <> 0 Then | ||
Begin | ||
WriteLn('Can''t open input file: ' + ParamStr(1)); | ||
Exit; | ||
End; | ||
// parse file structure | ||
SetLength(FR, 0); | ||
DT:=0; | ||
SI:=''; | ||
While Not EOF(Fl) Do | ||
Begin | ||
BlockRead(Fl, CS, SizeOf(TSect)); | ||
If ParamCount > 1 Then CS.Size:=swap32(CS.Size); | ||
// WriteLn(CS.Sign, ': ', CS.Size); | ||
If CS.Sign = 'DIDX' Then | ||
Begin | ||
SetLength(FR, CS.Size Div SizeOf(FR[0])); | ||
BlockRead(Fl, FR[0], CS.Size); | ||
Continue; | ||
End; | ||
If CS.Sign = 'STID' Then | ||
Begin | ||
Seek(Fl, FilePos(Fl) + 12); | ||
I:=0; | ||
BlockRead(Fl, I, 1); | ||
SetLength(SI, I); | ||
BlockRead(Fl, SI[1], I); | ||
Continue; | ||
End; | ||
If CS.Sign = 'DATA' Then DT:=FilePos(Fl); | ||
Seek(Fl, FilePos(Fl) + CS.Size); | ||
End; | ||
// extract files | ||
If ((DT > 0) And (Length(FR) > 0)) Then | ||
For I:=0 To Length(FR)-1 Do | ||
Begin | ||
S:=SI + '.' + Int03Str(I + 1) + '.wav'; | ||
Write(S); | ||
If ParamCount > 1 Then | ||
Begin | ||
FR[I].Size:=swap32(FR[I].Size); | ||
FR[I].Offs:=swap32(FR[I].Offs); | ||
End; | ||
Seek(Fl, DT + FR[I].Offs); | ||
GetMem(P, FR[I].Size); | ||
BlockRead(Fl, P^, FR[I].Size); | ||
AssignFile(F, S); | ||
ReWrite(F, 1); | ||
BlockWrite(F, P^, FR[I].Size); | ||
CloseFile(F); | ||
FreeMem(P, FR[I].Size); | ||
WriteLn; | ||
End; | ||
SetLength(FR, 0); | ||
CloseFile(Fl); | ||
End. |