Skip to content

Commit

Permalink
Initial implementation with original Delphi source code.
Browse files Browse the repository at this point in the history
  • Loading branch information
eXpl0it3r committed Mar 17, 2015
0 parents commit 0c1b61e
Show file tree
Hide file tree
Showing 3 changed files with 342 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
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')
```
181 changes: 181 additions & 0 deletions bnkextr.cpp
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);
}
}
}
}
149 changes: 149 additions & 0 deletions bnkextr.dpr
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.

0 comments on commit 0c1b61e

Please sign in to comment.