Merge pull request #793 from bnnm/xnb-acx-etc

xnb acx etc
This commit is contained in:
bnnm 2021-01-03 19:30:09 +01:00 committed by GitHub
commit 64ffe5ea0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 669 additions and 371 deletions

View File

@ -67,7 +67,7 @@ Available commands are printed when run with no flags. Note that you can also
achieve similar results for other plugins using TXTP, described later. achieve similar results for other plugins using TXTP, described later.
With files multiple subsongs you need to specify manually subsong (by design, to avoid With files multiple subsongs you need to specify manually subsong (by design, to avoid
massive data dumps since some formats have hundred of subsongs), but you could do massive data dumps since some formats have hundreds of subsongs), but you could do
some command line tricks: some command line tricks:
``` ```
: REM extracts from subsong 5 to 10 in file.fsb : REM extracts from subsong 5 to 10 in file.fsb
@ -259,6 +259,7 @@ which are hardcoded instead of being listed in the header file (e.g. `.mpf+.mus`
In these cases, you can use *TXTM* format to specify associated companion files. In these cases, you can use *TXTM* format to specify associated companion files.
See *Artificial files* below for more information. See *Artificial files* below for more information.
#### Dual stereo
A special case of the above is "dual file stereo", where 2 similarly named mono A special case of the above is "dual file stereo", where 2 similarly named mono
files are fused together to make 1 stereo song. files are fused together to make 1 stereo song.
- `(file)_L.dsp`+`(file)_R.dsp` - `(file)_L.dsp`+`(file)_R.dsp`
@ -267,16 +268,12 @@ files are fused together to make 1 stereo song.
- `(file)_0.dsp`+`(file)_1.dsp` - `(file)_0.dsp`+`(file)_1.dsp`
- `(file)_Left.dsp`+`(file)_Right.dsp` - `(file)_Left.dsp`+`(file)_Right.dsp`
- `(file).v0`+`(file).v1` - `(file).v0`+`(file).v1`
This is only allowed in a few formats (mainly `.dsp` and `.vag`). In those cases
you can open either `L` or `R` and you'll get the same stereo song. If you rename
one of the files the "pair" won't be found, and both will be played as mono.
`.pos` is a small file with 32 bit little endian values: loop start sample and vgmstream automatically detects these pairs and makes a stereo song from `L` + `R`.
loop end sample. This is a real format, but is sometimes reused to force loops. You can open either `L` or `R` and you'll get the same stereo. If you rename one
If you want to force looping files consider using *TXTP* instead, as it's much of the files the "pair" won't be found, and both will be played as mono. This
simpler to make and cleaner: for example create a text file named `bgm01-loop.txtp` is only done for a few choice formats (mainly `.dsp` and `.vag`) that commonly
and inside write `bgm01.mp3 #I 10.0 90.0`. Open the `.txtp` to play the `.mp3` split audio like that, though.
looping from 10 to 90 seconds.
#### OS case sensitiveness #### OS case sensitiveness
When using OS with case sensitive filesystem (mainly Linux), a known issue with When using OS with case sensitive filesystem (mainly Linux), a known issue with
@ -296,6 +293,16 @@ hex-editting), though only a few formats do this, mainly *Ubisoft* banks.
Regular formats without companion files should work fine in upper/lowercase. Regular formats without companion files should work fine in upper/lowercase.
#### .pos looping
`.pos` is a small file with 32 bit little endian values: loop start sample and
loop end sample. This is a real format, but is sometimes reused to force loops.
If you want to force looping consider using *TXTP* instead, as it's much simpler
to make and cleaner (plus doesn't hijack a real format). For example, make a text
file named `bgm01-loop.txtp` and inside write `bgm01.mp3 #I 10.0 90.0`. Open the
`.txtp` and vgmstream will loop that `.mp3` from 10 to 90 seconds.
### Decryption keys ### Decryption keys
Certain formats have encrypted data, and need a key to decrypt. vgmstream Certain formats have encrypted data, and need a key to decrypt. vgmstream
will try to find the correct key from a list, but it can be provided by will try to find the correct key from a list, but it can be provided by
@ -317,7 +324,7 @@ sample rate, channels, etc) is stored in the .exe or other hard to locate places
Those can be played using an artificial header with info vgmstream needs. Those can be played using an artificial header with info vgmstream needs.
**GENH**: a byte header placed right before the original data, modyfing it. **GENH**: a byte header placed right before the original data, modyfing it.
The resulting file must be (name).genh. Contains static header data. The resulting file must be `(name).genh`. Contains static header data.
Programs like VGMToolbox can help to create *GENH*. Programs like VGMToolbox can help to create *GENH*.
**TXTH**: a text header placed in an external file. The TXTH must be named **TXTH**: a text header placed in an external file. The TXTH must be named
@ -325,7 +332,7 @@ Programs like VGMToolbox can help to create *GENH*.
single file). Contains dynamic text commands to read data from the original single file). Contains dynamic text commands to read data from the original
file, or static values. file, or static values.
*TXTH* is recomended over *GENH* as it's far easier to create and has many *TXTH* is recommended over *GENH* as it's far easier to create and has many
more functions. more functions.
For files that already play, sometimes they are used by the game in various For files that already play, sometimes they are used by the game in various

View File

@ -39,6 +39,7 @@ static const char* extension_list[] = {
//"ac3", //common, FFmpeg/not parsed (AC3) //"ac3", //common, FFmpeg/not parsed (AC3)
"acb", "acb",
"acm", "acm",
"acx",
"ad", //txth/reserved [Xenosaga Freaks (PS2)] "ad", //txth/reserved [Xenosaga Freaks (PS2)]
"adc", //txth/reserved [Tomb Raider The Last Revelation (DC), Tomb Raider Chronicles (DC)] "adc", //txth/reserved [Tomb Raider The Last Revelation (DC), Tomb Raider Chronicles (DC)]
"adm", "adm",

View File

@ -500,6 +500,10 @@
RelativePath=".\meta\acm.c" RelativePath=".\meta\acm.c"
> >
</File> </File>
<File
RelativePath=".\meta\acx.c"
>
</File>
<File <File
RelativePath=".\meta\adp_konami.c" RelativePath=".\meta\adp_konami.c"
> >

View File

@ -282,6 +282,7 @@
<ClCompile Include="meta\aax.c" /> <ClCompile Include="meta\aax.c" />
<ClCompile Include="meta\acb.c" /> <ClCompile Include="meta\acb.c" />
<ClCompile Include="meta\acm.c" /> <ClCompile Include="meta\acm.c" />
<ClCompile Include="meta\acx.c" />
<ClCompile Include="meta\adp_konami.c" /> <ClCompile Include="meta\adp_konami.c" />
<ClCompile Include="meta\adpcm_capcom.c" /> <ClCompile Include="meta\adpcm_capcom.c" />
<ClCompile Include="meta\ads.c" /> <ClCompile Include="meta\ads.c" />

View File

@ -364,6 +364,9 @@
<ClCompile Include="meta\acm.c"> <ClCompile Include="meta\acm.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\acx.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\adp_konami.c"> <ClCompile Include="meta\adp_konami.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>

43
src/meta/acx.c Normal file
View File

@ -0,0 +1,43 @@
#include "meta.h"
#include "../coding/coding.h"
/* .acx - CRI container [Baroque (SAT), Persona 3 (PS2), THE iDOLM@STER: Live For You (X360)] */
VGMSTREAM* init_vgmstream_acx(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
off_t subfile_offset;
size_t subfile_size;
int total_subsongs, target_subsong = sf->stream_index;
/* checks */
if (!check_extensions(sf,"acx"))
goto fail;
if (read_u32be(0x00,sf) != 0x00000000)
goto fail;
/* simple container for sfx and rarely music [Burning Rangers (SAT)],
* mainly used until .csb was introduced */
total_subsongs = read_u32be(0x04,sf);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
subfile_offset = read_u32be(0x08 + (target_subsong-1) * 0x08 + 0x00,sf);
subfile_size = read_u32be(0x08 + (target_subsong-1) * 0x08 + 0x04,sf);
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "adx");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_adx(temp_sf);
if (!vgmstream) goto fail;
vgmstream->num_streams = total_subsongs;
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -20,7 +20,7 @@ static const adxkey_info adxkey8_list[] = {
{0x49e1,0x4a57,0x553d, "karaage",0}, {0x49e1,0x4a57,0x553d, "karaage",0},
/* Blood+ (PS2) [Grasshopper Manufacture] */ /* Blood+ (PS2) [Grasshopper Manufacture] */
{0x5f5d,0x58bd,0x55ed, NULL,0}, // keystring not in ELF? {0x5f5d,0x58bd,0x55ed, "LOVELOVE",0}, // obfuscated keystring is "KNUDKNUD", adds +1 to chars to get final key
/* Killer7 (PS2) [Grasshopper Manufacture] */ /* Killer7 (PS2) [Grasshopper Manufacture] */
{0x50fb,0x5803,0x5701, "GHM",0}, {0x50fb,0x5803,0x5701, "GHM",0},
@ -34,14 +34,14 @@ static const adxkey_info adxkey8_list[] = {
/* Phantasy Star Universe (PC), Phantasy Star Universe: Ambition of the Illuminus (PS2) [Sonic Team] */ /* Phantasy Star Universe (PC), Phantasy Star Universe: Ambition of the Illuminus (PS2) [Sonic Team] */
{0x5deb,0x5f27,0x673f, "3x5k62bg9ptbwy",0}, {0x5deb,0x5f27,0x673f, "3x5k62bg9ptbwy",0},
/* Senko no Ronde [G.rev] */ /* Senko no Ronde Rev.X (X360) [G.rev] */
{0x46d3,0x5ced,0x474d, "ranatus",0}, {0x46d3,0x5ced,0x474d, "ranatus",0},
/* NiGHTS: Journey of Dreams (Wii) [Sonic Team] */ /* NiGHTS: Journey of Dreams (Wii) [Sonic Team] */
{0x440b,0x6539,0x5723, "sakakit4649",0}, {0x440b,0x6539,0x5723, "sakakit4649",0},
/* unknown source */ /* The iDOLM@STER: Live For You (X360) [Bandai Namco] (.aix) */
{0x586d,0x5d65,0x63eb, NULL,0}, // from guessadx (unique?) {0x586d,0x5d65,0x63eb, "75_501NO_003B",0},
/* Shuffle! On the Stage (PS2) [Navel] */ /* Shuffle! On the Stage (PS2) [Navel] */
{0x4969,0x5deb,0x467f, "SHUF",0}, {0x4969,0x5deb,0x467f, "SHUF",0},
@ -64,8 +64,8 @@ static const adxkey_info adxkey8_list[] = {
/* Soulcalibur IV (PS3) [Namco] */ /* Soulcalibur IV (PS3) [Namco] */
{0x59ed,0x4679,0x46c9, "SC4Test",0}, {0x59ed,0x4679,0x46c9, "SC4Test",0},
/* Senko no Ronde DUO (X360) [G.rev] */ /* Senko no Ronde DUO (X360/AC) [G.rev] */
{0x6157,0x6809,0x4045, NULL,0}, // from guessadx {0x6157,0x6809,0x4045, "yomesokushitsu",0},
/* Nogizaka Haruka no Himitsu: Cosplay Hajimemashita (PS2) [Vridge] */ /* Nogizaka Haruka no Himitsu: Cosplay Hajimemashita (PS2) [Vridge] */
{0x45af,0x5f27,0x52b1, "SKFHSIA",0}, {0x45af,0x5f27,0x52b1, "SKFHSIA",0},
@ -100,16 +100,16 @@ static const adxkey_info adxkey8_list[] = {
/* Soshite Kono Uchuu ni Kirameku Kimi no Shi XXX (PS2) [Datam Polystar] */ /* Soshite Kono Uchuu ni Kirameku Kimi no Shi XXX (PS2) [Datam Polystar] */
{0x5f5d,0x552b,0x5507, "DATAM-KK2",0}, {0x5f5d,0x552b,0x5507, "DATAM-KK2",0},
/* Sakura Taisen: Atsuki Chishio Ni (PS2) [Sega] */ /* Sakura Taisen: Atsuki Chishio ni (PS2) [Sega] */
{0x645d,0x6011,0x5c29, NULL,0}, // confirmed unique with guessadx {0x645d,0x6011,0x5c29, NULL,0}, // possible key: "[Seq][ADX] illegal cri or libsd status."
/* Sakura Taisen 3 ~Paris wa Moeteiru ka~ (PS2) [Sega] */ /* Sakura Taisen Monogatari: Mysterious Paris (PS2) [Sega] */
{0x62ad,0x4b13,0x5957, NULL,0}, // confirmed unique with guessadx {0x62ad,0x4b13,0x5957, "inoue4126",0},
/* Sotsugyou 2nd Generation (PS2) [Jinx] */ /* Sotsugyou 2nd Generation (PS2) [Jinx] */
{0x6305,0x509f,0x4c01, NULL,0}, // First guess from guessadx, other was {0x6307,0x509f,0x2ac5} {0x6305,0x509f,0x4c01, NULL,0}, // First guess from guessadx, other was {0x6307,0x509f,0x2ac5}
/* La Corda d'Oro (PSP) [Koei] */ /* Kin'iro no Corda -La Corda d'Oro- (PSP) [Koei] */
{0x55b7,0x67e5,0x5387, NULL,0}, // keystring not in ELF? {0x55b7,0x67e5,0x5387, NULL,0}, // keystring not in ELF?
/* Nanatsuiro * Drops Pure!! (PS2) [Media Works] */ /* Nanatsuiro * Drops Pure!! (PS2) [Media Works] */
@ -127,7 +127,7 @@ static const adxkey_info adxkey8_list[] = {
/* Sora no Otoshimono: DokiDoki Summer Vacation (PSP) [Kadokawa Shoten] */ /* Sora no Otoshimono: DokiDoki Summer Vacation (PSP) [Kadokawa Shoten] */
{0x5e75,0x4a89,0x4c61, "funen-gomi",0}, {0x5e75,0x4a89,0x4c61, "funen-gomi",0},
/* Boku wa Koukuu Kanseikan: Airport Hero Naha (PSP) [Sonic Powered] */ /* Boku wa Koukuu Kanseikan: Airport Hero Naha/Narita/Shinchitose/Haneda/Kankuu (PSP) [Sonic Powered] */
{0x64ab,0x5297,0x632f, "sonic",0}, {0x64ab,0x5297,0x632f, "sonic",0},
/* Lucky Star: Net Idol Meister (PSP) [Vridge, Kadokawa Shoten] */ /* Lucky Star: Net Idol Meister (PSP) [Vridge, Kadokawa Shoten] */

View File

@ -162,7 +162,7 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
vgmstream->layout_data = build_layered_awc(sf, &awc); vgmstream->layout_data = build_layered_awc(sf, &awc);
if (!vgmstream->layout_data) goto fail; if (!vgmstream->layout_data) goto fail;
vgmstream->layout_type = layout_layered; vgmstream->layout_type = layout_layered;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_VORBIS_custom;
} }
else { else {
vorbis_custom_config cfg = {0}; vorbis_custom_config cfg = {0};

View File

@ -4,22 +4,22 @@
/* CSB (Cue Sheet Binary?) - CRI container of memory audio, often together with a .cpk wave bank */ /* CSB (Cue Sheet Binary?) - CRI container of memory audio, often together with a .cpk wave bank */
VGMSTREAM * init_vgmstream_csb(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_csb(STREAMFILE* sf) {
VGMSTREAM *vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
STREAMFILE *temp_sf = NULL; STREAMFILE* temp_sf = NULL;
off_t subfile_offset; off_t subfile_offset;
size_t subfile_size; size_t subfile_size;
utf_context *utf = NULL; utf_context *utf = NULL;
utf_context *utf_sdl = NULL; utf_context *utf_sdl = NULL;
int total_subsongs, target_subsong = streamFile->stream_index; int total_subsongs, target_subsong = sf->stream_index;
uint8_t fmt = 0; uint8_t fmt = 0;
const char *stream_name; const char* stream_name;
/* checks */ /* checks */
if (!check_extensions(streamFile, "csb")) if (!check_extensions(sf, "csb"))
goto fail; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x40555446) /* "@UTF" */ if (!is_id32be(0x00,sf, "@UTF"))
goto fail; goto fail;
/* .csb is an early, simpler version of .acb+awk (see acb.c) used until ~2013? /* .csb is an early, simpler version of .acb+awk (see acb.c) used until ~2013?
@ -35,32 +35,41 @@ VGMSTREAM * init_vgmstream_csb(STREAMFILE *streamFile) {
int found = 0; int found = 0;
utf = utf_open(streamFile, table_offset, &rows, &name); utf = utf_open(sf, table_offset, &rows, &name);
if (!utf) goto fail; if (!utf) goto fail;
if (strcmp(name, "TBLCSB") != 0) if (strcmp(name, "TBLCSB") != 0)
goto fail; goto fail;
/* each TBLCSB row has a name and subtable with actual things: /* each TBLCSB row has a name and subtable with actual things:
* - INFO (TBL_INFO): table type/version * - INFO (TBL_INFO): table type/version, rarely ommited [Nights: Journey of Dreams (Wii)-some csb]
* - CUE (TBLCUE): base cues * - CUE (TBLCUE): base cues
* - SYNTH (TBLSYN): cue configs * - SYNTH (TBLSYN): cue configs
* - SOUND_ELEMENT (TBLSDL): audio info/data (usually AAX) * - SOUND_ELEMENT (TBLSDL): audio info/data (usually AAX)
* - ISAAC (TBLISC): 3D config * - ISAAC (TBLISC): 3D config
* - VOICE_LIMIT_GROUP (TBLVLG): system info? * - VOICE_LIMIT_GROUP (TBLVLG): system info?
* Subtable can be empty but must still appear (0 rows). * Subtable can be empty but still appear (0 rows).
*/ */
sdl_row = 3; /* should use fixed order */ sdl_row = -1;
for (i = 0; i < rows; i++) {
/* get SOUND_ELEMENT and table */ if (!utf_query_string(utf, i, "name", &row_name))
if (!utf_query_string(utf, sdl_row, "name", &row_name) || strcmp(row_name, "SOUND_ELEMENT") != 0) goto fail;
if (strcmp(row_name, "SOUND_ELEMENT") == 0) {
sdl_row = i; /* usually 2 or 3 */
break;
}
}
if (sdl_row < 0)
goto fail; goto fail;
/* read SOUND_ELEMENT table */
if (!utf_query_u8(utf, sdl_row, "ttype", &ttype) || ttype != 4) if (!utf_query_u8(utf, sdl_row, "ttype", &ttype) || ttype != 4)
goto fail; goto fail;
if (!utf_query_data(utf, sdl_row, "utf", &sdl_offset, &sdl_size)) if (!utf_query_data(utf, sdl_row, "utf", &sdl_offset, &sdl_size))
goto fail; goto fail;
utf_sdl = utf_open(streamFile, sdl_offset, &sdl_rows, &sdl_name); utf_sdl = utf_open(sf, sdl_offset, &sdl_rows, &sdl_name);
if (!utf_sdl) goto fail; if (!utf_sdl) goto fail;
if (strcmp(sdl_name, "TBLSDL") != 0) if (strcmp(sdl_name, "TBLSDL") != 0)
@ -109,7 +118,7 @@ VGMSTREAM * init_vgmstream_csb(STREAMFILE *streamFile) {
//;VGM_LOG("CSB: subfile offset=%lx + %x\n", subfile_offset, subfile_size); //;VGM_LOG("CSB: subfile offset=%lx + %x\n", subfile_offset, subfile_size);
temp_sf = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, "aax"); temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "aax");
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
switch(fmt) { switch(fmt) {

View File

@ -80,6 +80,9 @@ static const uint8_t key_ghm[] = { 0x8C,0xFA,0xF3,0x14,0xB1,0x53,0xDA,0xAB,0x2B,
/* Worms Rumble Beta (PC) */ //"FXnTffGJ9LS855Gc" /* Worms Rumble Beta (PC) */ //"FXnTffGJ9LS855Gc"
static const uint8_t key_wrb[] = { 0x46,0x58,0x6E,0x54,0x66,0x66,0x47,0x4A,0x39,0x4C,0x53,0x38,0x35,0x35,0x47,0x63 }; static const uint8_t key_wrb[] = { 0x46,0x58,0x6E,0x54,0x66,0x66,0x47,0x4A,0x39,0x4C,0x53,0x38,0x35,0x35,0x47,0x63 };
/* Bubble Fighter (PC) */ //"qjvkeoqkrdhkdckd"
static const uint8_t key_bbf[] = { 0x71,0x6A,0x76,0x6B,0x65,0x6F,0x71,0x6B,0x72,0x64,0x68,0x6B,0x64,0x63,0x6B,0x64 };
// Unknown: // Unknown:
// - Battle: Los Angeles // - Battle: Los Angeles
// - Guitar Hero: Warriors of Rock, DJ hero FSB // - Guitar Hero: Warriors of Rock, DJ hero FSB
@ -90,7 +93,7 @@ typedef struct {
int is_fsb5; /* FSB5 or FSB4/3*/ int is_fsb5; /* FSB5 or FSB4/3*/
int is_alt; /* alt XOR mode (seemingly not tied to FSB version or anything) */ int is_alt; /* alt XOR mode (seemingly not tied to FSB version or anything) */
size_t fsbkey_size; size_t fsbkey_size;
const uint8_t *fsbkey; const uint8_t* fsbkey;
} fsbkey_info; } fsbkey_info;
static const fsbkey_info fsbkey_list[] = { static const fsbkey_info fsbkey_list[] = {
@ -153,6 +156,7 @@ static const fsbkey_info fsbkey_list[] = {
{ 1,0, sizeof(key_scp),key_scp },// FSB5 { 1,0, sizeof(key_scp),key_scp },// FSB5
{ 0,1, sizeof(key_ghm),key_ghm },// FSB4 { 0,1, sizeof(key_ghm),key_ghm },// FSB4
{ 1,0, sizeof(key_wrb),key_wrb },// FSB5 { 1,0, sizeof(key_wrb),key_wrb },// FSB5
{ 0,0, sizeof(key_bbf),key_bbf },// FSB4
}; };
static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]);

View File

@ -7,6 +7,7 @@ typedef struct {
int big_endian; int big_endian;
int total_subsongs; int total_subsongs;
int target_subsong; int target_subsong;
int found;
kwb_codec codec; kwb_codec codec;
int channels; int channels;
@ -26,6 +27,7 @@ typedef struct {
static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b); static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b);
static int parse_xws(kwb_header* kwb, STREAMFILE* sf); static int parse_xws(kwb_header* kwb, STREAMFILE* sf);
static VGMSTREAM* init_vgmstream_koei_wavebank(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b);
/* KWB - WaveBank from Koei games */ /* KWB - WaveBank from Koei games */
@ -33,7 +35,6 @@ VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
STREAMFILE *sf_h = NULL, *sf_b = NULL; STREAMFILE *sf_h = NULL, *sf_b = NULL;
kwb_header kwb = {0}; kwb_header kwb = {0};
int32_t (*read_s32)(off_t,STREAMFILE*) = NULL;
int target_subsong = sf->stream_index; int target_subsong = sf->stream_index;
@ -70,89 +71,11 @@ VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf) {
if (!parse_kwb(&kwb, sf_h, sf_b)) if (!parse_kwb(&kwb, sf_h, sf_b))
goto fail; goto fail;
read_s32 = kwb.big_endian ? read_s32be : read_s32le;
vgmstream = init_vgmstream_koei_wavebank(&kwb, sf_h, sf_b);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(kwb.channels, kwb.loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->meta_type = meta_KWB;
vgmstream->sample_rate = kwb.sample_rate;
vgmstream->num_samples = kwb.num_samples;
vgmstream->stream_size = kwb.stream_size;
vgmstream->num_streams = kwb.total_subsongs;
switch(kwb.codec) {
case PCM16: /* PCM */
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
break;
case MSADPCM:
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->frame_size = kwb.block_size;
break;
case DSP_HEAD:
case DSP_BODY:
if (kwb.channels > 1) goto fail;
vgmstream->coding_type = coding_NGC_DSP; /* subinterleave? */
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x08;
if (kwb.codec == DSP_HEAD) {
dsp_read_coefs(vgmstream, sf_h, kwb.dsp_offset + 0x1c, 0x60, kwb.big_endian);
dsp_read_hist (vgmstream, sf_h, kwb.dsp_offset + 0x40, 0x60, kwb.big_endian);
}
else {
/* typical DSP header + data */
vgmstream->num_samples = read_s32(kwb.stream_offset + 0x00, sf_b);
dsp_read_coefs(vgmstream, sf_b, kwb.stream_offset + 0x1c, 0x60, kwb.big_endian);
dsp_read_hist (vgmstream, sf_b, kwb.stream_offset + 0x40, 0x60, kwb.big_endian);
kwb.stream_offset += 0x60;
}
break;
#ifdef VGM_USE_ATRAC9
case AT9: {
atrac9_config cfg = {0};
{
size_t extra_size = read_u32le(kwb.stream_offset + 0x00, sf_b);
uint32_t config_data = read_u32be(kwb.stream_offset + 0x04, sf_b);
/* 0x0c: encoder delay? */
/* 0x0e: encoder padding? */
/* 0x10: samples per frame */
/* 0x12: frame size */
cfg.channels = vgmstream->channels;
cfg.config_data = config_data;
kwb.stream_offset += extra_size;
kwb.stream_size -= extra_size;
}
vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none;
//TODO: check encoder delay
vgmstream->num_samples = atrac9_bytes_to_samples_cfg(kwb.stream_size, cfg.config_data);
break;
}
#endif
default:
goto fail;
}
if (sf_h != sf) close_streamfile(sf_h); if (sf_h != sf) close_streamfile(sf_h);
if (!vgmstream_open_stream(vgmstream, sf_b, kwb.stream_offset))
goto fail;
return vgmstream; return vgmstream;
fail: fail:
@ -164,7 +87,6 @@ fail:
/* XWS - WaveStream? from Koei games */ /* XWS - WaveStream? from Koei games */
VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
kwb_header kwb = {0}; kwb_header kwb = {0};
int target_subsong = sf->stream_index; int target_subsong = sf->stream_index;
@ -179,37 +101,138 @@ VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf) {
if (!parse_xws(&kwb, sf)) if (!parse_xws(&kwb, sf))
goto fail; goto fail;
if (kwb.codec == MSF) { vgmstream = init_vgmstream_koei_wavebank(&kwb, sf, sf);
if (kwb.stream_offset == 0) { if (!vgmstream) goto fail;
vgmstream = init_vgmstream_silence(0,0,0); /* dummy, whatevs */
if (!vgmstream) goto fail;
}
else {
kwb.stream_size = read_u32be(kwb.stream_offset + 0x0c, sf) + 0x40;
temp_sf = setup_subfile_streamfile(sf, kwb.stream_offset, kwb.stream_size, "msf");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_msf(temp_sf);
if (!vgmstream) goto fail;
}
vgmstream->num_streams = kwb.total_subsongs;
}
else {
goto fail;
}
close_streamfile(temp_sf);
return vgmstream; return vgmstream;
fail: fail:
close_streamfile(temp_sf); close_vgmstream(vgmstream);
return NULL; return NULL;
} }
static VGMSTREAM* init_vgmstream_koei_wavebank(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
VGMSTREAM* vgmstream = NULL;
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
int32_t (*read_s32)(off_t,STREAMFILE*) = NULL;
static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
read_u32 = kwb->big_endian ? read_u32be : read_u32le;
read_s32 = kwb->big_endian ? read_s32be : read_s32le;
/* container */
if (kwb->codec == MSF) {
if (kwb->stream_offset == 0) {
vgmstream = init_vgmstream_silence(0,0,0); /* dummy, whatevs */
if (!vgmstream) goto fail;
}
else {
STREAMFILE* temp_sf = NULL;
kwb->stream_size = read_u32(kwb->stream_offset + 0x0c, sf_h) + 0x40;
temp_sf = setup_subfile_streamfile(sf_h, kwb->stream_offset, kwb->stream_size, "msf");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_msf(temp_sf);
close_streamfile(temp_sf);
if (!vgmstream) goto fail;
}
vgmstream->num_streams = kwb->total_subsongs;
return vgmstream;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(kwb->channels, kwb->loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_KWB;
vgmstream->sample_rate = kwb->sample_rate;
vgmstream->num_samples = kwb->num_samples;
vgmstream->stream_size = kwb->stream_size;
vgmstream->num_streams = kwb->total_subsongs;
switch(kwb->codec) {
case PCM16: /* PCM */
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
break;
case MSADPCM:
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->frame_size = kwb->block_size;
break;
case DSP_HEAD:
case DSP_BODY:
if (kwb->channels > 1) goto fail;
vgmstream->coding_type = coding_NGC_DSP; /* subinterleave? */
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x08;
if (kwb->codec == DSP_HEAD) {
dsp_read_coefs(vgmstream, sf_h, kwb->dsp_offset + 0x1c, 0x60, kwb->big_endian);
dsp_read_hist (vgmstream, sf_h, kwb->dsp_offset + 0x40, 0x60, kwb->big_endian);
}
else {
/* typical DSP header + data */
vgmstream->num_samples = read_s32(kwb->stream_offset + 0x00, sf_b);
dsp_read_coefs(vgmstream, sf_b, kwb->stream_offset + 0x1c, 0x60, kwb->big_endian);
dsp_read_hist (vgmstream, sf_b, kwb->stream_offset + 0x40, 0x60, kwb->big_endian);
kwb->stream_offset += 0x60;
}
break;
#ifdef VGM_USE_ATRAC9
case AT9: {
atrac9_config cfg = {0};
{
size_t extra_size = read_u32le(kwb->stream_offset + 0x00, sf_b);
uint32_t config_data = read_u32be(kwb->stream_offset + 0x04, sf_b);
/* 0x0c: encoder delay? */
/* 0x0e: encoder padding? */
/* 0x10: samples per frame */
/* 0x12: frame size */
cfg.channels = vgmstream->channels;
cfg.config_data = config_data;
kwb->stream_offset += extra_size;
kwb->stream_size -= extra_size;
}
vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none;
//TODO: check encoder delay
vgmstream->num_samples = atrac9_bytes_to_samples_cfg(kwb->stream_size, cfg.config_data);
break;
}
#endif
default:
goto fail;
}
if (!vgmstream_open_stream(vgmstream, sf_b, kwb->stream_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* ************************************************************************* */
static int parse_type_kwb2(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) {
int i, j, sounds; int i, j, sounds;
/* 00: KWB2/KWBN id */ /* 00: KWB2/KWBN id */
@ -220,12 +243,15 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
/* 10: null or 1 */ /* 10: null or 1 */
/* 14: offset to HDDB table (from type), can be null */ /* 14: offset to HDDB table (from type), can be null */
//;VGM_LOG("KWB2: sounds %i, o=%lx\n", sounds, offset);
/* offset table to entries */ /* offset table to entries */
for (i = 0; i < sounds; i++) { for (i = 0; i < sounds; i++) {
off_t sound_offset = read_u32le(offset + 0x18 + i*0x04, sf_h); off_t sound_offset = read_u32le(offset + 0x18 + i*0x04, sf_h);
int subsounds, subsound_start, subsound_size; int subsounds, subsound_start, subsound_size;
uint16_t version; uint16_t version;
//;VGM_LOG("KWB2: entry %i, o=%lx, so=%lx\n", i, offset + 0x18 + i*0x04, sound_offset);
if (sound_offset == 0) /* common... */ if (sound_offset == 0) /* common... */
continue; continue;
@ -258,6 +284,8 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
kwb->total_subsongs++; kwb->total_subsongs++;
if (kwb->total_subsongs != kwb->target_subsong) if (kwb->total_subsongs != kwb->target_subsong)
continue; continue;
kwb->found = 1;
subsound_offset = subsound_start + j*subsound_size; subsound_offset = subsound_start + j*subsound_size;
kwb->sample_rate = read_u16le(subsound_offset + 0x00, sf_h); kwb->sample_rate = read_u16le(subsound_offset + 0x00, sf_h);
@ -272,6 +300,8 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
/* when size > 0x48 */ /* when size > 0x48 */
/* 0x48: subsound entry size */ /* 0x48: subsound entry size */
/* rest: reserved per codec? (usually null) */ /* rest: reserved per codec? (usually null) */
kwb->stream_offset += body_offset;
switch(codec) { switch(codec) {
case 0x00: case 0x00:
@ -304,16 +334,14 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
then name table (null terminated and one after other) then name table (null terminated and one after other)
*/ */
if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail;
return 1; return 1;
fail: fail:
return 0; return 0;
} }
static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { static int parse_type_k4hd(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) {
off_t ppva_offset, header_offset; off_t ppva_offset, header_offset;
int entries; int entries, current_subsongs, relative_subsong;
size_t entry_size; size_t entry_size;
@ -344,10 +372,14 @@ static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
goto fail; goto fail;
} }
kwb->total_subsongs = entries; current_subsongs = kwb->total_subsongs;
if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail; kwb->total_subsongs += entries;
if (kwb->target_subsong - 1 < current_subsongs || kwb->target_subsong > kwb->total_subsongs)
return 1;
kwb->found = 1;
header_offset = ppva_offset + 0x20 + (kwb->target_subsong-1) * entry_size; relative_subsong = kwb->target_subsong - current_subsongs;
header_offset = ppva_offset + 0x20 + (relative_subsong-1) * entry_size;
kwb->stream_offset = read_u32le(header_offset + 0x00, sf_h); kwb->stream_offset = read_u32le(header_offset + 0x00, sf_h);
kwb->sample_rate = read_u32le(header_offset + 0x04, sf_h); kwb->sample_rate = read_u32le(header_offset + 0x04, sf_h);
@ -363,21 +395,22 @@ static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
kwb->codec = AT9; kwb->codec = AT9;
kwb->channels = 1; /* always, devs use dual subsongs to fake stereo (like as hd3+bd3) */ kwb->channels = 1; /* always, devs use dual subsongs to fake stereo (like as hd3+bd3) */
kwb->stream_offset += body_offset;
return 1; return 1;
fail: fail:
return 0; return 0;
} }
static int parse_type_sdsd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { static int parse_type_sdsd(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) {
/* has Vers, Head, Prog, Smpl sections (like Sony VABs) /* has Vers, Head, Prog, Smpl sections (like Sony VABs)
unknown codec, blocked with some common start, variable sized */ unknown codec, blocked with some common start, variable sized */
return 0; return 0;
} }
static int parse_type_sdwi(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) { static int parse_type_sdwi(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) {
off_t smpl_offset, header_offset; off_t smpl_offset, header_offset;
int entries; int entries, current_subsongs, relative_subsong;
size_t entry_size; size_t entry_size;
@ -404,10 +437,14 @@ static int parse_type_sdwi(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
entries = read_u32le(smpl_offset + 0x0c, sf_h); /* LE! */ entries = read_u32le(smpl_offset + 0x0c, sf_h); /* LE! */
entry_size = 0x40; entry_size = 0x40;
kwb->total_subsongs = entries; current_subsongs = kwb->total_subsongs;
if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail; kwb->total_subsongs += entries;
if (kwb->target_subsong - 1 < current_subsongs || kwb->target_subsong > kwb->total_subsongs)
return 1;
kwb->found = 1;
header_offset = smpl_offset + 0x10 + (kwb->target_subsong-1) * entry_size; relative_subsong = kwb->target_subsong - current_subsongs;
header_offset = smpl_offset + 0x10 + (relative_subsong-1) * entry_size;
/* 00: "SS" + ID (0..N) */ /* 00: "SS" + ID (0..N) */
kwb->stream_offset = read_u32be(header_offset + 0x04, sf_h); kwb->stream_offset = read_u32be(header_offset + 0x04, sf_h);
@ -427,11 +464,14 @@ static int parse_type_sdwi(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
kwb->codec = DSP_BODY; kwb->codec = DSP_BODY;
kwb->channels = 1; kwb->channels = 1;
kwb->stream_offset += body_offset;
return 1; return 1;
fail: fail:
return 0; return 0;
} }
static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) { static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
off_t head_offset, body_offset, start; off_t head_offset, body_offset, start;
uint32_t type; uint32_t type;
@ -485,22 +525,22 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
switch(type) { switch(type) {
case 0x4B574232: /* "KWB2" [Bladestorm Nightmare (PC), Dissidia NT (PC)] */ case 0x4B574232: /* "KWB2" [Bladestorm Nightmare (PC), Dissidia NT (PC)] */
case 0x4B57424E: /* "KWBN" [Fire Emblem Warriors (Switch)] */ case 0x4B57424E: /* "KWBN" [Fire Emblem Warriors (Switch)] */
if (!parse_type_kwb2(kwb, head_offset, sf_h)) if (!parse_type_kwb2(kwb, head_offset, body_offset, sf_h))
goto fail; goto fail;
break; break;
case 0x4B344844: /* "K4HD" [Dissidia NT (PS4), (Vita) */ case 0x4B344844: /* "K4HD" [Dissidia NT (PS4), (Vita) */
if (!parse_type_k4hd(kwb, head_offset, sf_h)) if (!parse_type_k4hd(kwb, head_offset, body_offset, sf_h))
goto fail; goto fail;
break; break;
case 0x53447364: /* "SDsd" (PS3? leftover files) */ case 0x53447364: /* "SDsd" (PS3? leftover files) */
if (!parse_type_sdsd(kwb, head_offset, sf_h)) if (!parse_type_sdsd(kwb, head_offset, body_offset, sf_h))
goto fail; goto fail;
break; break;
case 0x53445769: /* "SDWi" [Fatal Frame 5 (WiiU)] */ case 0x53445769: /* "SDWi" [Fatal Frame 5 (WiiU)] */
if (!parse_type_sdwi(kwb, head_offset, sf_h)) if (!parse_type_sdwi(kwb, head_offset, body_offset, sf_h))
goto fail; goto fail;
break; break;
@ -508,7 +548,8 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
goto fail; goto fail;
} }
kwb->stream_offset += body_offset; if (!kwb->found)
goto fail;
return 1; return 1;
fail: fail:
@ -517,17 +558,112 @@ fail:
static int parse_type_msfbank(kwb_header* kwb, off_t offset, STREAMFILE* sf) { static int parse_type_msfbank(kwb_header* kwb, off_t offset, STREAMFILE* sf) {
/* this is just like XWSF, abridged: */ /* this is just like XWSF, abridged: */
int entries, current_subsongs, relative_subsong;
off_t header_offset; off_t header_offset;
entries = read_u32be(offset + 0x14, sf);
kwb->total_subsongs = read_u32be(offset + 0x14, sf); current_subsongs = kwb->total_subsongs;
if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail; kwb->total_subsongs += entries;
if (kwb->target_subsong - 1 < current_subsongs || kwb->target_subsong > kwb->total_subsongs)
return 1;
kwb->found = 1;
header_offset = offset + 0x30 + (kwb->target_subsong-1) * 0x04; relative_subsong = kwb->target_subsong - current_subsongs;
header_offset = offset + 0x30 + (relative_subsong-1) * 0x04;
/* just a dumb table pointing to MSF, entries can be dummy */ /* just a dumb table pointing to MSF, entries can be dummy */
kwb->stream_offset = read_u32be(header_offset, sf); kwb->stream_offset = read_u32be(header_offset, sf);
kwb->codec = MSF; kwb->codec = MSF;
kwb->stream_offset += offset;
return 1;
//fail:
// return 0;
}
static int parse_type_xwsfile(kwb_header* kwb, off_t offset, STREAMFILE* sf) {
off_t table1_offset, table2_offset;
int i, chunks, chunks2;
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
if (!(is_id32be(offset + 0x00, sf, "XWSF") && is_id32be(offset + 0x04, sf, "ILE\0")) &&
!(is_id32be(offset + 0x00, sf, "tdpa") && is_id32be(offset + 0x04, sf, "ck\0\0")))
goto fail;
kwb->big_endian = read_u8(offset + 0x08, sf) == 0xFF;
/* 0x0a: version? (0100: NG2/NG3 PS3, 0101: DoA LR PC) */
read_u32 = kwb->big_endian ? read_u32be : read_u32le;
/* 0x0c: tables start */
/* 0x10: file size */
chunks = read_u32(offset + 0x14, sf);
chunks2 = read_u32(offset + 0x18, sf);
/* 0x1c: null */
if (chunks != chunks2)
goto fail;
table1_offset = read_u32(offset + 0x20, sf); /* offsets */
table2_offset = read_u32(offset + 0x24, sf); /* sizes */
/* 0x28: null */
/* 0x2c: null */
i = 0;
while (i < chunks) {
uint32_t entry_type, head_offset, body_offset, head_size;
//;VGM_LOG("XWS: entry %i/%i\n", i, chunks);
/* NG2/NG3 PS3 have table1+2, DoA LR PC removes table2 and includes body offset in entries */
if (table2_offset) {
head_offset = read_u32(offset + table1_offset + i * 0x04 + 0x00, sf);
head_size = read_u32(offset + table2_offset + i * 0x04 + 0x00, sf);
body_offset = head_offset;
i += 1;
/* sometimes has file end offset as entry with no size*/
if (!head_size)
continue;
}
else {
head_offset = read_u32(offset + table1_offset + i * 0x04 + 0x00, sf);
body_offset = read_u32(offset + table1_offset + i * 0x04 + 0x04, sf);
i += 2;
}
if (!head_offset) /* just in case */
continue;
head_offset += offset;
body_offset += offset;
entry_type = read_u32be(head_offset + 0x00, sf);
//;VGM_LOG("XWS: head=%x, body=%x\n", head_offset, body_offset);
if (entry_type == get_id32be("XWSF")) { /* + "ILE\0" */
if (!parse_type_xwsfile(kwb, head_offset, sf))
goto fail;
}
else if (entry_type == get_id32be("CUEB") || entry_type < 0x100) {
; /* CUE-like info (may start with 0 or a low number instead) */
}
else if (entry_type == get_id32be("MSFB")) { /* + "ANK\0" */
if (!parse_type_msfbank(kwb, head_offset, sf))
goto fail;
}
else if (entry_type == get_id32be("KWB2")) {
if (!parse_type_kwb2(kwb, head_offset, body_offset, sf))
goto fail;
}
else {
VGM_LOG("XWS: unknown type %x at head=%x, body=%x\n", entry_type, head_offset, body_offset);
goto fail;
}
}
return 1; return 1;
fail: fail:
return 0; return 0;
@ -535,59 +671,23 @@ fail:
static int parse_xws(kwb_header* kwb, STREAMFILE* sf) { static int parse_xws(kwb_header* kwb, STREAMFILE* sf) {
off_t head_offset, body_offset, start;
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
int chunks, chunks2;
off_t msfb_offset;
/* format is similar to WHD1 with some annoyances of its own /* Format is similar to WHD1 with some annoyances of its own. Variations:
* variations: * - XWSFILE w/ N chunks: CUE offsets + 1 MSFBANK offset
* - tdpack: points to N XWSFILE
* - XWSFILE w/ 4 chunks: CUEBANK offset, ? offset, MSFBANK offset, end offset (PS3)
* [Ninja Gaiden Sigma 2 (PS3), Ninja Gaiden 3 Razor's Edge (PS3)] * [Ninja Gaiden Sigma 2 (PS3), Ninja Gaiden 3 Razor's Edge (PS3)]
* - XWSFILE w/ 2*N chunks: KWB2 offset + data offset * N (ex. 3 pairs = 6 chunks) * - XWSFILE w/ 2*N chunks: KWB2 offset + data offset * N (ex. 3 pairs = 6 chunks)
* [Dead or Alive 5 Last Round (PC)] * [Dead or Alive 5 Last Round (PC)]
* - tdpack: same but points to N XWSFILE
* [Ninja Gaiden 3 Razor's Edge (PS3)]
* *
* for now basic support for the second case, others we'd have to map subsong N to internal bank M * Needs to call sub-parts multiple times to fill total subsongs when parsing xwsfile.
*/ */
if (!parse_type_xwsfile(kwb, 0x00, sf))
if (read_u32be(0x00, sf) != 0x58575346 || /* "XWSF" */
read_u32be(0x04, sf) != 0x494C4500) /* "ILE\0" */
goto fail; goto fail;
kwb->big_endian = read_u8(0x08, sf) == 0xFF; if (!kwb->found)
/* 0x0a: version? */
read_u32 = kwb->big_endian ? read_u32be : read_u32le;
start = read_u32(0x0c, sf);
/* 0x10: file size */
chunks = read_u32(0x14, sf);
chunks2 = read_u32(0x18, sf);
/* 0x1c: null */
/* 0x20: some size? */
/* 0x24: some size? */
if (chunks != chunks2)
goto fail; goto fail;
if (chunks != 4)
goto fail;
msfb_offset = read_u32(start + 0x08, sf);
if (read_u32be(msfb_offset, sf) == 0x4D534642) { /* "MSFB" + "ANK\0" */
head_offset = msfb_offset;
body_offset = msfb_offset; /* relative to start */
if (!parse_type_msfbank(kwb, head_offset, sf))
goto fail;
}
else {
goto fail;
}
kwb->stream_offset += body_offset;
return 1; return 1;
fail: fail:
return 0; return 0;

View File

@ -934,4 +934,6 @@ VGMSTREAM *init_vgmstream_sbk(STREAMFILE *sf);
VGMSTREAM* init_vgmstream_ifs(STREAMFILE* sf); VGMSTREAM* init_vgmstream_ifs(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_acx(STREAMFILE* sf);
#endif /*_META_H*/ #endif /*_META_H*/

View File

@ -660,6 +660,8 @@ VGMSTREAM* init_vgmstream_sadb(STREAMFILE* sf) {
dspm.start_offset = read_32bitBE(0x48,sf); dspm.start_offset = read_32bitBE(0x48,sf);
dspm.interleave = 0x10; dspm.interleave = 0x10;
dspm.ignore_loop_ps = 1; /* does something strange with offsets/etc, ignore */
dspm.meta_type = meta_DSP_SADB; dspm.meta_type = meta_DSP_SADB;
return init_vgmstream_dsp_common(sf, &dspm); return init_vgmstream_dsp_common(sf, &dspm);
fail: fail:

View File

@ -3,66 +3,71 @@
/* EXST - from Sony games [Shadow of the Colossus (PS2), Gacha Mecha Stadium Saru Battle (PS2)] */ /* EXST - from Sony games [Shadow of the Colossus (PS2), Gacha Mecha Stadium Saru Battle (PS2)] */
VGMSTREAM * init_vgmstream_ps2_exst(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_ps2_exst(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
STREAMFILE * streamBody = NULL; STREAMFILE* sf_body = NULL;
off_t start_offset; off_t start_offset;
int loop_flag, channel_count; int loop_flag, channels, sample_rate;
size_t block_size, num_blocks, loop_start_block; size_t block_size, num_blocks, loop_start_block;
/* checks */ /* checks */
/* .sts+int: main [Shadow of the Colossus (PS2)] (some .sts have manually joined header+body) /* .sts+int: standard [Shadow of the Colossus (PS2)] (some fake .sts have manually joined header+body)
* .x: header+body [Ape Escape 3 (PS2)] */ * .x: header+body [Ape Escape 3 (PS2)] */
if (!check_extensions(streamFile, "sts,x")) if (!check_extensions(sf, "sts,x"))
goto fail; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x45585354) /* "EXST" */ if (!is_id32be(0x00,sf, "EXST"))
goto fail; goto fail;
streamBody = open_streamfile_by_ext(streamFile,"int"); sf_body = open_streamfile_by_ext(sf,"int");
if (!streamBody) { if (sf_body) {
/* data+body joined */ /* separate header+body (header is 0x78) */
start_offset = 0x78;
if (get_streamfile_size(streamFile) < start_offset)
goto fail;
}
else {
/* body is separate */
start_offset = 0x00; start_offset = 0x00;
} }
else {
/* joint header+body */
start_offset = 0x78;
/* Gacharoku 2 has header+data but padded header (ELF has pointers + size to SOUND.PCK, and
* treats them as single files, no extension but there are Sg2ExStAdpcm* calls in the ELF) */
if ((get_streamfile_size(sf) % 0x10) == 0)
start_offset = 0x80;
if (get_streamfile_size(sf) < start_offset)
goto fail;
}
channel_count = read_16bitLE(0x06,streamFile); channels = read_u16le(0x06,sf);
loop_flag = read_32bitLE(0x0C,streamFile) == 1; sample_rate = read_u32le(0x08,sf);
loop_start_block = read_32bitLE(0x10,streamFile); loop_flag = read_u32le(0x0C,sf) == 1;
num_blocks = read_32bitLE(0x14,streamFile); loop_start_block = read_u32le(0x10,sf);
num_blocks = read_u32le(0x14,sf);
/* 0x18: 0x24 config per channel? (volume+panning+etc?) */
/* rest is padding up to 0x78 */
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x08,streamFile);
vgmstream->meta_type = meta_PS2_EXST; vgmstream->meta_type = meta_PS2_EXST;
vgmstream->sample_rate = sample_rate;
vgmstream->coding_type = coding_PSX; vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x400; vgmstream->interleave_block_size = 0x400;
block_size = vgmstream->interleave_block_size * vgmstream->channels; block_size = vgmstream->interleave_block_size * vgmstream->channels;
vgmstream->num_samples = ps_bytes_to_samples(num_blocks*block_size, channel_count); vgmstream->num_samples = ps_bytes_to_samples(num_blocks * block_size, channels);
if (vgmstream->loop_flag) { vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start_block * block_size, channels);
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start_block*block_size, channel_count);; vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->loop_end_sample = vgmstream->num_samples;
}
if (!vgmstream_open_stream(vgmstream,streamBody ? streamBody : streamFile,start_offset)) if (!vgmstream_open_stream(vgmstream, sf_body ? sf_body : sf, start_offset))
goto fail; goto fail;
close_streamfile(streamBody); close_streamfile(sf_body);
return vgmstream; return vgmstream;
fail: fail:
close_streamfile(streamBody); close_streamfile(sf_body);
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

@ -9,7 +9,7 @@
typedef enum { CODEC_NONE = 0, UBI_IMA, RAW_PCM, RAW_PSX, RAW_XMA1, RAW_XMA2_OLD, RAW_XMA2_NEW, RAW_AT3, RAW_AT3_105, FMT_AT3, RAW_DSP, FMT_OGG } ubi_bao_codec; typedef enum { CODEC_NONE = 0, UBI_IMA, RAW_PCM, RAW_PSX, RAW_XMA1, RAW_XMA2_OLD, RAW_XMA2_NEW, RAW_AT3, RAW_AT3_105, FMT_AT3, RAW_DSP, FMT_OGG } ubi_bao_codec;
typedef enum { TYPE_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE, UBI_SILENCE } ubi_bao_type; typedef enum { TYPE_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE, UBI_SILENCE } ubi_bao_type;
typedef enum { FILE_NONE = 0, UBI_FORGE, UBI_FORGE_b } ubi_bao_file; typedef enum { FILE_NONE = 0, UBI_FORGE, UBI_FORGE_b, UBI_FAT } ubi_bao_file;
typedef struct { typedef struct {
size_t bao_class; size_t bao_class;
@ -1403,6 +1403,13 @@ static STREAMFILE* open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int
goto fail; goto fail;
case UBI_FAT:
snprintf(buf,buf_size, "%08x.bao", file_id);
sf_bao = open_streamfile_by_filename(sf, buf);
if (sf_bao) return sf_bao;
goto fail;
default: default:
goto fail; goto fail;
} }
@ -1747,6 +1754,30 @@ static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf) {
bao->cfg.file_type = UBI_FORGE; bao->cfg.file_type = UBI_FORGE;
return 1; return 1;
case 0x001B0200: /* Beowulf (PS3/X360)-atomic-bin+fat */
config_bao_entry(bao, 0xA0, 0x24);
config_bao_audio_b(bao, 0x08, 0x1c, 0x28, 0x34, 1, 1); /* 0x2c: prefetch flag? */
config_bao_audio_m(bao, 0x44, 0x48, 0x50, 0x58, 0x64, 0x74);
bao->cfg.audio_interleave = 0x10;
bao->cfg.audio_fix_psx_samples = 1;
config_bao_sequence(bao, 0x2c, 0x20, 0x1c, 0x14);
config_bao_layer_m(bao, 0x4c, 0x20, 0x2c, 0x44, 0x00, 0x50, 0x00, 0x00, 1); /* stream size: 0x48? */
config_bao_layer_e(bao, 0x30, 0x00, 0x04, 0x08, 0x10);
config_bao_silence_f(bao, 0x1c);
bao->cfg.codec_map[0x00] = RAW_XMA1;
bao->cfg.codec_map[0x02] = RAW_PSX;
bao->cfg.codec_map[0x03] = UBI_IMA;
bao->cfg.codec_map[0x04] = FMT_OGG;
bao->cfg.codec_map[0x07] = RAW_AT3_105;
bao->cfg.file_type = UBI_FAT;
return 1;
case 0x001F0008: /* Rayman Raving Rabbids: TV Party (Wii)-package */ case 0x001F0008: /* Rayman Raving Rabbids: TV Party (Wii)-package */
case 0x001F0010: /* Prince of Persia 2008 (PC/PS3/X360)-atomic-forge, Far Cry 2 (PS3)-atomic-dunia? */ case 0x001F0010: /* Prince of Persia 2008 (PC/PS3/X360)-atomic-forge, Far Cry 2 (PS3)-atomic-dunia? */
case 0x001F0011: /* Naruto: The Broken Bond (X360)-package */ case 0x001F0011: /* Naruto: The Broken Bond (X360)-package */
@ -1879,9 +1910,6 @@ static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf) {
bao->cfg.file_type = UBI_FORGE_b; bao->cfg.file_type = UBI_FORGE_b;
return 1; return 1;
case 0x001B0200: /* Beowulf (PS3)-atomic-bin+fat */
/* same as 0x001B0100 except:
* - base 0xA0, skip 0x24, name style %08x (.bao/sbao?) */
case 0x001C0000: /* Lost: Via Domus (PS3)-atomic-gear */ case 0x001C0000: /* Lost: Via Domus (PS3)-atomic-gear */
/* same as 0x001B0100 except: /* same as 0x001B0100 except:
* - base 0xA0, skip 0x24, name style %08x.bao (not .sbao?) */ * - base 0xA0, skip 0x24, name style %08x.bao (not .sbao?) */

View File

@ -3988,9 +3988,11 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
return 1; return 1;
} }
/* Naruto: Rise of a Ninja (2007)(X360)-bank */
/* Rainbow Six Vegas 2 (2008)(PS3)-bank */ /* Rainbow Six Vegas 2 (2008)(PS3)-bank */
/* Rainbow Six Vegas 2 (2008)(X360)-bank */ /* Rainbow Six Vegas 2 (2008)(X360)-bank */
if ((sb->version == 0x001C0000 && sb->platform == UBI_PS3) || if ((sb->version == 0x001B0001 && sb->platform == UBI_X360) ||
(sb->version == 0x001C0000 && sb->platform == UBI_PS3) ||
(sb->version == 0x001C0000 && sb->platform == UBI_X360)) { (sb->version == 0x001C0000 && sb->platform == UBI_X360)) {
config_sb_entry(sb, 0x64, 0x7c); config_sb_entry(sb, 0x64, 0x7c);

View File

@ -96,7 +96,8 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
switch(ww.codec) { switch(ww.codec) {
case PCM: /* common */ case PCM: /* common */
/* normally riff.c has priority but it's needed when .wem is used */ /* normally riff.c has priority but it's needed when .wem is used */
if (ww.fmt_size != 0x10 && ww.fmt_size != 0x18 && ww.fmt_size != 0x28) goto fail; /* old, new/Limbo (PC) */ /* old=0x10, 0x12=Army of Two: the 40th Day (PS3), new/Limbo (PC) */
if (ww.fmt_size != 0x10 && ww.fmt_size != 0x12 && ww.fmt_size != 0x18 && ww.fmt_size != 0x28) goto fail;
if (ww.bits_per_sample != 16) goto fail; if (ww.bits_per_sample != 16) goto fail;
vgmstream->coding_type = (ww.big_endian ? coding_PCM16BE : coding_PCM16LE); vgmstream->coding_type = (ww.big_endian ? coding_PCM16BE : coding_PCM16LE);

View File

@ -12,13 +12,14 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
int big_endian, flags, codec, sample_rate, block_align, bps; int big_endian, flags, codec, sample_rate, block_align, bps;
size_t data_size; size_t data_size;
char platform; char platform;
int is_ogg = 0, is_at9 = 0; int is_sound = 0, is_ogg = 0, is_at9 = 0, is_song = 0;
char song_name[255+1];
/* checks */ /* checks */
if (!check_extensions(sf,"xnb")) if (!check_extensions(sf,"xnb"))
goto fail; goto fail;
if ((read_32bitBE(0x00, sf) & 0xFFFFFF00) != 0x584E4200) /* "XNB" */ if ((read_u32be(0x00, sf) & 0xFFFFFF00) != get_id32be("XNB\0"))
goto fail; goto fail;
/* XNA Studio platforms: 'w' = Windows, 'm' = Windows Phone 7, 'x' = X360 /* XNA Studio platforms: 'w' = Windows, 'm' = Windows Phone 7, 'x' = X360
@ -32,9 +33,9 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
flags = read_u8(0x05, sf); flags = read_u8(0x05, sf);
//if (flags & 0x01) goto fail; /* "HiDef profile" content (no actual difference) */ //if (flags & 0x01) goto fail; /* "HiDef profile" content (no actual difference) */
/* full size */ /* full size */
if (read_32bitLE(0x06, sf) != get_streamfile_size(sf)) { if (read_u32le(0x06, sf) != get_streamfile_size(sf)) {
goto fail; goto fail;
} }
@ -59,49 +60,73 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
/* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */ /* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */
{ {
char reader_name[255+1]; char reader_name[255+1];
size_t reader_string_len; size_t string_len;
uint32_t fmt_chunk_size; uint8_t type_count;
const char* type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */ const static char* type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */
const char* type_ogg = "SoundEffectFromOggReader"; /* has extra text info after base part */ const static char* type_ogg = "SoundEffectFromOggReader"; /* has extra text info after base part */
//const char* type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* references a companion .wma */ const static char* type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* references a companion .wma */
const static char* type_int32 = "Microsoft.Xna.Framework.Content.Int32Reader"; /* extra crap */
/* type reader count, accept only one for now */ type_count = read_u8(offset++, sf_h);
if (read_u8(offset++, sf_h) != 1)
/* check type reader string */
string_len = read_u8(offset++, sf_h); /* doesn't count null */
if (read_string(reader_name, string_len+1, offset, sf_h) != string_len)
goto fail; goto fail;
reader_string_len = read_u8(offset++, sf_h); /* doesn't count null */ if (strcmp(reader_name, type_sound) == 0) {
if (reader_string_len > 255) goto fail; if (type_count != 1) goto fail;
is_sound = 1;
/* check SoundEffect type string */ }
if (read_string(reader_name, reader_string_len+1, offset, sf_h) != reader_string_len) else if (strncmp(reader_name, type_ogg, strlen(type_ogg)) == 0) { /* has extra info after base string */
if (type_count != 1) goto fail;
is_ogg = 1;
}
else if (strcmp(reader_name, type_song) == 0) {
if (type_count != 2) goto fail;
is_song = 1;
}
else {
goto fail; goto fail;
if (strcmp(reader_name, type_sound) != 0) {
is_ogg = strncmp(reader_name, type_ogg, strlen(type_ogg)) == 0;
if (!is_ogg)
goto fail;
} }
offset += reader_string_len + 1; offset += string_len + 1;
if (is_song) {
offset += 3;
string_len = read_u8(offset++, sf_h);
if (read_string(reader_name, string_len+1, offset, sf_h) != string_len)
goto fail;
if (strcmp(reader_name, type_int32) != 0)
goto fail;
offset += string_len + 1;
}
offset += 0x04; /* reader version, 0 */ offset += 0x04; /* reader version, 0 */
/* shared resource count */ /* shared resource number 1 */
if (read_u8(offset++, sf_h) != 1) if (read_u8(offset++, sf_h) != 1)
goto fail; goto fail;
/* shared resource: partial "fmt" chunk */ /* read shared resource */
fmt_chunk_size = read_32bitLE(offset, sf_h); if (is_sound || is_ogg) {
offset += 0x04; /* partial "fmt" chunk */
uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le;
uint16_t (*read_u16)(off_t,STREAMFILE*) = big_endian ? read_u16be : read_u16le;
uint32_t fmt_chunk_size;
{ fmt_chunk_size = read_u32le(offset, sf_h);
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; offset += 0x04;
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE;
codec = (uint16_t)read_16bit(offset+0x00, sf_h); codec = read_u16(offset+0x00, sf_h);
channel_count = read_16bit(offset+0x02, sf_h); channel_count = read_u16(offset+0x02, sf_h);
sample_rate = read_32bit(offset+0x04, sf_h); sample_rate = read_u32(offset+0x04, sf_h);
/* 0x08: byte rate */ /* 0x08: byte rate */
block_align = read_16bit(offset+0x0c, sf_h); block_align = read_u16(offset+0x0c, sf_h);
bps = read_16bit(offset+0x0e, sf_h); bps = read_u16(offset+0x0e, sf_h);
if (codec == 0x0002) { if (codec == 0x0002) {
if (!msadpcm_check_coefs(sf_h, offset + 0x14)) if (!msadpcm_check_coefs(sf_h, offset + 0x14))
@ -115,30 +140,44 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
if (codec == 0xFFFF) { if (codec == 0xFFFF) {
if (platform != 'S') goto fail; if (platform != 'S') goto fail;
sample_rate = read_32bit(offset+fmt_chunk_size+0x04+0x08, sf_h); sample_rate = read_u32(offset+fmt_chunk_size+0x04+0x08, sf_h);
} }
/* mini-fmt has AT9 stuff then a regular RIFF [Square Heroes (PS4)] */ /* mini-fmt has AT9 stuff then a regular RIFF [Square Heroes (PS4)] */
if (codec == 0xFFFE) { if (codec == 0xFFFE) {
is_at9 = 1; is_at9 = 1;
} }
/* regular (with loop tags) Ogg poses as PCM [Little Savior (PC)] */ /* Ogg (with loop tags) poses as PCM [Little Savior (PC)] */
offset += fmt_chunk_size;
data_size = read_u32le(offset, sf_h);
offset += 0x04;
start_offset = offset;
} }
else if (is_song) {
/* filename (typically same as .xnb but .wma) */
string_len = read_u8(offset++, sf_h);
offset += fmt_chunk_size; if (read_string(song_name, string_len+1, offset, sf_h) != string_len + 1)
goto fail;
data_size = read_32bitLE(offset, sf_h); start_offset = 0;
offset += 0x04; data_size = 0;
/* after name is shared resource number 1 + 32b int (durationMs?) */
start_offset = offset; }
else {
goto fail;
}
} }
/* container handling */ /* container handling */
if (is_ogg || is_at9) { if (is_ogg || is_at9) {
STREAMFILE* temp_sf = NULL; STREAMFILE* temp_sf = NULL;
const char* fake_ext = is_ogg ? "ogg" : "at9"; const char* fake_ext = is_ogg ? "ogg" : "at9";
/* after data_size is loop start + loop length and offset? (same as loop tags), 0 if not enabled */ /* after data_size is loop start + loop length and offset? (same as loop tags), 0 if not enabled */
temp_sf = setup_subfile_streamfile(sf_h, start_offset, data_size, fake_ext); temp_sf = setup_subfile_streamfile(sf_h, start_offset, data_size, fake_ext);
@ -158,6 +197,31 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
if (sf_h != sf) close_streamfile(sf_h); if (sf_h != sf) close_streamfile(sf_h);
return vgmstream; return vgmstream;
} }
else if (is_song) {
STREAMFILE* sf_body = open_streamfile_by_filename(sf, song_name);
if (!sf_body) goto fail;
if (read_u32be(0x00, sf_body) == 0x01000080) {
STREAMFILE* temp_sf = setup_subfile_streamfile(sf_body, 0x00, get_streamfile_size(sf_body), "opus");
if (!temp_sf) goto fail;
/* MonoGame with NXOpus [Clan N (Switch)] */
vgmstream = init_vgmstream_opus_std(temp_sf);
close_streamfile(temp_sf);
}
else {
#ifdef VGM_USE_FFMPEG
/* XNA with WMA [Guncraft: Blocked and Loaded (X360)] */
vgmstream = init_vgmstream_ffmpeg(sf_body);
#endif
}
close_streamfile(sf_body);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XNB;
if (sf_h != sf) close_streamfile(sf_h);
return vgmstream;
}
/* build the VGMSTREAM */ /* build the VGMSTREAM */
@ -221,7 +285,7 @@ VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
vgmstream->coding_type = coding_NGC_DSP; vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = data_size / channel_count; vgmstream->interleave_block_size = data_size / channel_count;
vgmstream->num_samples = read_32bitLE(start_offset + 0x00, sf_h); vgmstream->num_samples = read_s32le(start_offset + 0x00, sf_h);
//vgmstream->num_samples = dsp_bytes_to_samples(data_size - 0x60*channel_count, channel_count); //vgmstream->num_samples = dsp_bytes_to_samples(data_size - 0x60*channel_count, channel_count);
dsp_read_coefs(vgmstream, sf_h, start_offset + 0x1c, vgmstream->interleave_block_size, big_endian); dsp_read_coefs(vgmstream, sf_h, start_offset + 0x1c, vgmstream->interleave_block_size, big_endian);

View File

@ -861,15 +861,15 @@ STREAMFILE* open_multifile_streamfile_f(STREAMFILE **streamfiles, size_t streamf
/* **************************************************** */ /* **************************************************** */
STREAMFILE* open_streamfile(STREAMFILE *streamfile, const char *pathname) { STREAMFILE* open_streamfile(STREAMFILE* sf, const char* pathname) {
return streamfile->open(streamfile, pathname, STREAMFILE_DEFAULT_BUFFER_SIZE); return sf->open(sf, pathname, STREAMFILE_DEFAULT_BUFFER_SIZE);
} }
STREAMFILE* open_streamfile_by_ext(STREAMFILE *streamfile, const char *ext) { STREAMFILE* open_streamfile_by_ext(STREAMFILE* sf, const char* ext) {
char filename[PATH_LIMIT]; char filename[PATH_LIMIT];
int filename_len, fileext_len; int filename_len, fileext_len;
streamfile->get_name(streamfile, filename, sizeof(filename)); sf->get_name(sf, filename, sizeof(filename));
filename_len = strlen(filename); filename_len = strlen(filename);
fileext_len = strlen(filename_extension(filename)); fileext_len = strlen(filename_extension(filename));
@ -882,17 +882,17 @@ STREAMFILE* open_streamfile_by_ext(STREAMFILE *streamfile, const char *ext) {
strcpy(filename + filename_len - fileext_len, ext); strcpy(filename + filename_len - fileext_len, ext);
} }
return streamfile->open(streamfile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); return sf->open(sf, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
} }
STREAMFILE* open_streamfile_by_filename(STREAMFILE *streamfile, const char * filename) { STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename) {
char fullname[PATH_LIMIT]; char fullname[PATH_LIMIT];
char partname[PATH_LIMIT]; char partname[PATH_LIMIT];
char *path, *name; char *path, *name;
if (!streamfile || !filename || !filename[0]) return NULL; if (!sf || !filename || !filename[0]) return NULL;
streamfile->get_name(streamfile, fullname, sizeof(fullname)); sf->get_name(sf, fullname, sizeof(fullname));
//todo normalize separators in a better way, safeops, improve copying //todo normalize separators in a better way, safeops, improve copying
path = strrchr(fullname,DIR_SEPARATOR); path = strrchr(fullname,DIR_SEPARATOR);
@ -903,10 +903,10 @@ STREAMFILE* open_streamfile_by_filename(STREAMFILE *streamfile, const char * fil
fix_dir_separators(partname); fix_dir_separators(partname);
/* normalize relative paths as don't work ok in some plugins */ /* normalize relative paths as don't work ok in some plugins */
if (partname[0]=='.' && partname[1] == DIR_SEPARATOR) { /* './name' */ if (partname[0] == '.' && partname[1] == DIR_SEPARATOR) { /* './name' */
name = partname + 2; /* ignore './' */ name = partname + 2; /* ignore './' */
} }
else if (partname[0]=='.' && partname[1]=='.' && partname[2] == DIR_SEPARATOR) { /* '../name' */ else if (partname[0] == '.' && partname[1] == '.' && partname[2] == DIR_SEPARATOR) { /* '../name' */
char *pathprev; char *pathprev;
path[0] = '\0'; /* remove last separator so next call works */ path[0] = '\0'; /* remove last separator so next call works */
@ -931,23 +931,23 @@ STREAMFILE* open_streamfile_by_filename(STREAMFILE *streamfile, const char * fil
strcpy(fullname, filename); strcpy(fullname, filename);
} }
return streamfile->open(streamfile, fullname, STREAMFILE_DEFAULT_BUFFER_SIZE); return sf->open(sf, fullname, STREAMFILE_DEFAULT_BUFFER_SIZE);
} }
STREAMFILE* reopen_streamfile(STREAMFILE *streamfile, size_t buffer_size) { STREAMFILE* reopen_streamfile(STREAMFILE* sf, size_t buffer_size) {
char pathname[PATH_LIMIT]; char pathname[PATH_LIMIT];
if (!streamfile) return NULL; if (!sf) return NULL;
if (buffer_size == 0) if (buffer_size == 0)
buffer_size = STREAMFILE_DEFAULT_BUFFER_SIZE; buffer_size = STREAMFILE_DEFAULT_BUFFER_SIZE;
streamfile->get_name(streamfile,pathname,sizeof(pathname)); sf->get_name(sf, pathname,sizeof(pathname));
return streamfile->open(streamfile,pathname,buffer_size); return sf->open(sf, pathname, buffer_size);
} }
/* **************************************************** */ /* **************************************************** */
size_t read_line(char *buf, int buf_size, off_t offset, STREAMFILE *sf, int *p_line_ok) { size_t read_line(char* buf, int buf_size, off_t offset, STREAMFILE* sf, int* p_line_ok) {
int i; int i;
off_t file_size = get_streamfile_size(sf); off_t file_size = get_streamfile_size(sf);
int extra_bytes = 0; /* how many bytes over those put in the buffer were read */ int extra_bytes = 0; /* how many bytes over those put in the buffer were read */
@ -996,7 +996,7 @@ size_t read_line(char *buf, int buf_size, off_t offset, STREAMFILE *sf, int *p_l
return i + extra_bytes; return i + extra_bytes;
} }
size_t read_string(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf) { size_t read_string(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf) {
size_t pos; size_t pos;
for (pos = 0; pos < buf_size; pos++) { for (pos = 0; pos < buf_size; pos++) {
@ -1019,7 +1019,7 @@ fail:
return 0; return 0;
} }
size_t read_string_utf16(char *buf, size_t buf_size, off_t offset, STREAMFILE *sf, int big_endian) { size_t read_string_utf16(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf, int big_endian) {
size_t pos, offpos; size_t pos, offpos;
uint16_t (*read_u16)(off_t,STREAMFILE*) = big_endian ? read_u16be : read_u16le; uint16_t (*read_u16)(off_t,STREAMFILE*) = big_endian ? read_u16be : read_u16le;
@ -1049,16 +1049,16 @@ size_t read_string_utf16be(char* buf, size_t buf_size, off_t offset, STREAMFILE*
return read_string_utf16(buf, buf_size, offset, sf, 1); return read_string_utf16(buf, buf_size, offset, sf, 1);
} }
/* ************************************************************************* */
size_t read_key_file(uint8_t* buf, size_t buf_size, STREAMFILE* sf) {
size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf) {
char keyname[PATH_LIMIT]; char keyname[PATH_LIMIT];
char filename[PATH_LIMIT]; char filename[PATH_LIMIT];
const char *path, *ext; const char *path, *ext;
STREAMFILE * streamFileKey = NULL; STREAMFILE* sf_key = NULL;
size_t keysize; size_t keysize;
sf->get_name(sf,filename,sizeof(filename)); get_streamfile_name(sf, filename, sizeof(filename));
if (strlen(filename)+4 > sizeof(keyname)) goto fail; if (strlen(filename)+4 > sizeof(keyname)) goto fail;
@ -1067,20 +1067,20 @@ size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf) {
ext = strrchr(filename,'.'); ext = strrchr(filename,'.');
if (ext!=NULL) ext = ext+1; if (ext!=NULL) ext = ext+1;
path = strrchr(filename,DIR_SEPARATOR); path = strrchr(filename, DIR_SEPARATOR);
if (path!=NULL) path = path+1; if (path!=NULL) path = path+1;
/* "(name.ext)key" */ /* "(name.ext)key" */
strcpy(keyname, filename); strcpy(keyname, filename);
strcat(keyname, "key"); strcat(keyname, "key");
streamFileKey = sf->open(sf,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); sf_key = sf->open(sf, keyname, STREAMFILE_DEFAULT_BUFFER_SIZE);
if (streamFileKey) goto found; if (sf_key) goto found;
/* "(name.ext)KEY" */ /* "(name.ext)KEY" */
/* /*
strcpy(keyname+strlen(keyname)-3,"KEY"); strcpy(keyname+strlen(keyname)-3,"KEY");
streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); sf_key = sf->open(sf, keyname, STREAMFILE_DEFAULT_BUFFER_SIZE);
if (streamFileKey) goto found; if (sf_key) goto found;
*/ */
@ -1094,38 +1094,38 @@ size_t read_key_file(uint8_t *buf, size_t buf_size, STREAMFILE *sf) {
} }
if (ext) strcat(keyname, ext); if (ext) strcat(keyname, ext);
strcat(keyname, "key"); strcat(keyname, "key");
streamFileKey = sf->open(sf,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); sf_key = sf->open(sf, keyname, STREAMFILE_DEFAULT_BUFFER_SIZE);
if (streamFileKey) goto found; if (sf_key) goto found;
/* "(.ext)KEY" */ /* "(.ext)KEY" */
/* /*
strcpy(keyname+strlen(keyname)-3,"KEY"); strcpy(keyname+strlen(keyname)-3,"KEY");
streamFileKey = streamFile->open(streamFile,keyname,STREAMFILE_DEFAULT_BUFFER_SIZE); sf_key = sf->open(sf, keyname, STREAMFILE_DEFAULT_BUFFER_SIZE);
if (streamFileKey) goto found; if (sf_key) goto found;
*/ */
goto fail; goto fail;
} }
found: found:
keysize = get_streamfile_size(streamFileKey); keysize = get_streamfile_size(sf_key);
if (keysize > buf_size) goto fail; if (keysize > buf_size) goto fail;
if (read_streamfile(buf, 0, keysize, streamFileKey) != keysize) if (read_streamfile(buf, 0, keysize, sf_key) != keysize)
goto fail; goto fail;
close_streamfile(streamFileKey); close_streamfile(sf_key);
return keysize; return keysize;
fail: fail:
close_streamfile(streamFileKey); close_streamfile(sf_key);
return 0; return 0;
} }
STREAMFILE *read_filemap_file(STREAMFILE *sf, int file_num) { STREAMFILE* read_filemap_file(STREAMFILE* sf, int file_num) {
char filename[PATH_LIMIT]; char filename[PATH_LIMIT];
off_t txt_offset, file_size; off_t txt_offset, file_size;
STREAMFILE *sf_map = NULL; STREAMFILE* sf_map = NULL;
sf_map = open_streamfile_by_filename(sf, ".txtm"); sf_map = open_streamfile_by_filename(sf, ".txtm");
if (!sf_map) goto fail; if (!sf_map) goto fail;
@ -1163,7 +1163,7 @@ STREAMFILE *read_filemap_file(STREAMFILE *sf, int file_num) {
if (strcmp(key, filename) == 0) { if (strcmp(key, filename) == 0) {
int n; int n;
char subval[PATH_LIMIT]; char subval[PATH_LIMIT];
const char *current = val; const char* current = val;
int i; int i;
for (i = 0; i <= file_num; i++) { for (i = 0; i <= file_num; i++) {
@ -1192,7 +1192,7 @@ fail:
return NULL; return NULL;
} }
void fix_dir_separators(char * filename) { void fix_dir_separators(char* filename) {
char c; char c;
int i = 0; int i = 0;
while ((c = filename[i]) != '\0') { while ((c = filename[i]) != '\0') {
@ -1202,11 +1202,13 @@ void fix_dir_separators(char * filename) {
} }
} }
int check_extensions(STREAMFILE *sf, const char * cmp_exts) { /* ************************************************************************* */
int check_extensions(STREAMFILE* sf, const char* cmp_exts) {
char filename[PATH_LIMIT]; char filename[PATH_LIMIT];
const char * ext = NULL; const char* ext = NULL;
const char * cmp_ext = NULL; const char* cmp_ext = NULL;
const char * ststr_res = NULL; const char* ststr_res = NULL;
size_t ext_len, cmp_len; size_t ext_len, cmp_len;
sf->get_name(sf,filename,sizeof(filename)); sf->get_name(sf,filename,sizeof(filename));
@ -1232,6 +1234,7 @@ int check_extensions(STREAMFILE *sf, const char * cmp_exts) {
return 0; return 0;
} }
/* ************************************************************************* */
/** /**
* Find a chunk starting from an offset, and save its offset/size (if not NULL), with offset after id/size. * Find a chunk starting from an offset, and save its offset/size (if not NULL), with offset after id/size.
@ -1241,11 +1244,11 @@ int check_extensions(STREAMFILE *sf, const char * cmp_exts) {
* *
* returns 0 on failure * returns 0 on failure
*/ */
static int find_chunk_internal(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_type, int big_endian_size, int zero_size_end) { static int find_chunk_internal(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_type, int big_endian_size, int zero_size_end) {
int32_t (*read_32bit_type)(off_t,STREAMFILE*) = big_endian_type ? read_32bitBE : read_32bitLE; int32_t (*read_32bit_type)(off_t,STREAMFILE*) = big_endian_type ? read_32bitBE : read_32bitLE;
int32_t (*read_32bit_size)(off_t,STREAMFILE*) = big_endian_size ? read_32bitBE : read_32bitLE; int32_t (*read_32bit_size)(off_t,STREAMFILE*) = big_endian_size ? read_32bitBE : read_32bitLE;
off_t offset, max_offset; off_t offset, max_offset;
size_t file_size = get_streamfile_size(streamFile); size_t file_size = get_streamfile_size(sf);
if (max_size == 0) if (max_size == 0)
max_size = file_size; max_size = file_size;
@ -1258,8 +1261,8 @@ static int find_chunk_internal(STREAMFILE *streamFile, uint32_t chunk_id, off_t
/* read chunks */ /* read chunks */
while (offset < max_offset) { while (offset < max_offset) {
uint32_t chunk_type = read_32bit_type(offset + 0x00,streamFile); uint32_t chunk_type = read_32bit_type(offset + 0x00,sf);
uint32_t chunk_size = read_32bit_size(offset + 0x04,streamFile); uint32_t chunk_size = read_32bit_size(offset + 0x04,sf);
//;VGM_LOG("CHUNK: type=%x, size=%x at %lx\n", chunk_type, chunk_size, offset); //;VGM_LOG("CHUNK: type=%x, size=%x at %lx\n", chunk_type, chunk_size, offset);
if (chunk_type == 0xFFFFFFFF || chunk_size == 0xFFFFFFFF) if (chunk_type == 0xFFFFFFFF || chunk_size == 0xFFFFFFFF)
@ -1280,36 +1283,39 @@ static int find_chunk_internal(STREAMFILE *streamFile, uint32_t chunk_id, off_t
return 0; return 0;
} }
int find_chunk_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) { int find_chunk_be(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) {
return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 1, 0); return find_chunk(sf, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 1, 0);
} }
int find_chunk_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) { int find_chunk_le(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size) {
return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 0, 0); return find_chunk(sf, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 0, 0);
} }
int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_size, int zero_size_end) { int find_chunk(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, int full_chunk_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian_size, int zero_size_end) {
return find_chunk_internal(streamFile, chunk_id, start_offset, 0, full_chunk_size, out_chunk_offset, out_chunk_size, 1, big_endian_size, zero_size_end); return find_chunk_internal(sf, chunk_id, start_offset, 0, full_chunk_size, out_chunk_offset, out_chunk_size, 1, big_endian_size, zero_size_end);
} }
int find_chunk_riff_le(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size) { int find_chunk_riff_le(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size) {
return find_chunk_internal(streamFile, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, 1, 0, 0); return find_chunk_internal(sf, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, 1, 0, 0);
} }
int find_chunk_riff_be(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size) { int find_chunk_riff_be(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size) {
return find_chunk_internal(streamFile, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, 1, 1, 0); return find_chunk_internal(sf, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, 1, 1, 0);
} }
int find_chunk_riff_ve(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian) { int find_chunk_riff_ve(STREAMFILE* sf, uint32_t chunk_id, off_t start_offset, size_t max_size, off_t *out_chunk_offset, size_t *out_chunk_size, int big_endian) {
return find_chunk_internal(streamFile, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, big_endian, big_endian, 0); return find_chunk_internal(sf, chunk_id, start_offset, max_size, 0, out_chunk_offset, out_chunk_size, big_endian, big_endian, 0);
} }
/* ************************************************************************* */
/* copies name as-is (may include full path included) */ /* copies name as-is (may include full path included) */
void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) { void get_streamfile_name(STREAMFILE* sf, char* buffer, size_t size) {
streamFile->get_name(streamFile,buffer,size); sf->get_name(sf, buffer, size);
} }
/* copies the filename without path */ /* copies the filename without path */
void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size) { void get_streamfile_filename(STREAMFILE* sf, char* buffer, size_t size) {
char foldername[PATH_LIMIT]; char foldername[PATH_LIMIT];
const char *path; const char* path;
streamFile->get_name(streamFile,foldername,sizeof(foldername)); get_streamfile_name(sf, foldername, sizeof(foldername));
//todo Windows CMD accepts both \\ and /, better way to handle this? //todo Windows CMD accepts both \\ and /, better way to handle this?
path = strrchr(foldername,'\\'); path = strrchr(foldername,'\\');
@ -1325,22 +1331,24 @@ void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size)
strcpy(buffer, foldername); strcpy(buffer, foldername);
} }
} }
/* copies the filename without path or extension */
void get_streamfile_basename(STREAMFILE *streamFile, char * buffer, size_t size) {
char *ext;
get_streamfile_filename(streamFile,buffer,size); /* copies the filename without path or extension */
void get_streamfile_basename(STREAMFILE* sf, char* buffer, size_t size) {
char* ext;
get_streamfile_filename(sf, buffer, size);
ext = strrchr(buffer,'.'); ext = strrchr(buffer,'.');
if (ext) { if (ext) {
ext[0] = '\0'; /* remove .ext from buffer */ ext[0] = '\0'; /* remove .ext from buffer */
} }
} }
/* copies path removing name (NULL when if filename has no path) */
void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) {
const char *path;
streamFile->get_name(streamFile,buffer,size); /* copies path removing name (NULL when if filename has no path) */
void get_streamfile_path(STREAMFILE* sf, char* buffer, size_t size) {
const char* path;
get_streamfile_name(sf, buffer, size);
path = strrchr(buffer,DIR_SEPARATOR); path = strrchr(buffer,DIR_SEPARATOR);
if (path!=NULL) path = path+1; /* includes "/" */ if (path!=NULL) path = path+1; /* includes "/" */
@ -1351,34 +1359,47 @@ void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) {
buffer[0] = '\0'; buffer[0] = '\0';
} }
} }
void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size) {
streamFile->get_name(streamFile,filename,size); /* copies extension only */
strcpy(filename, filename_extension(filename)); void get_streamfile_ext(STREAMFILE* sf, char* buffer, size_t size) {
char filename[PATH_LIMIT];
const char* extension = NULL;
get_streamfile_name(sf, filename, sizeof(filename));
extension = filename_extension(filename);
if (!extension) {
buffer[0] = '\n';
}
else {
strncpy(buffer, extension, size); //todo use something better
}
} }
/* ************************************************************************* */
/* debug util, mainly for custom IO testing */ /* debug util, mainly for custom IO testing */
void dump_streamfile(STREAMFILE *streamFile, int num) { void dump_streamfile(STREAMFILE* sf, int num) {
#ifdef VGM_DEBUG_OUTPUT #ifdef VGM_DEBUG_OUTPUT
off_t offset = 0; off_t offset = 0;
FILE *f = NULL; FILE* f = NULL;
if (num >= 0) { if (num >= 0) {
char filename[PATH_LIMIT]; char filename[PATH_LIMIT];
char dumpname[PATH_LIMIT]; char dumpname[PATH_LIMIT];
get_streamfile_filename(streamFile, filename, PATH_LIMIT); get_streamfile_filename(sf, filename, PATH_LIMIT);
snprintf(dumpname,PATH_LIMIT, "%s_%02i.dump", filename, num); snprintf(dumpname,PATH_LIMIT, "%s_%02i.dump", filename, num);
f = fopen(dumpname,"wb"); f = fopen(dumpname,"wb");
if (!f) return; if (!f) return;
} }
VGM_LOG("dump streamfile: size %x\n", get_streamfile_size(streamFile)); VGM_LOG("dump streamfile: size %x\n", get_streamfile_size(sf));
while (offset < get_streamfile_size(streamFile)) { while (offset < get_streamfile_size(sf)) {
uint8_t buffer[0x8000]; uint8_t buffer[0x8000];
size_t read; size_t read;
read = read_streamfile(buffer,offset,0x8000,streamFile); read = read_streamfile(buffer,offset,0x8000,sf);
if(!read) { if(!read) {
VGM_LOG("dump streamfile: can't read at %lx\n", offset); VGM_LOG("dump streamfile: can't read at %lx\n", offset);
break; break;

View File

@ -516,6 +516,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_dsp_wiiadpcm, init_vgmstream_dsp_wiiadpcm,
init_vgmstream_dsp_cwac, init_vgmstream_dsp_cwac,
init_vgmstream_ifs, init_vgmstream_ifs,
init_vgmstream_acx,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */