diff --git a/doc/FORMATS.md b/doc/FORMATS.md index bf83d470..fe3e1390 100644 --- a/doc/FORMATS.md +++ b/doc/FORMATS.md @@ -264,10 +264,11 @@ different internally (encrypted, different versions, etc) and not always can be - Codecs: PSX - **riff.c** - RIFF WAVE header [*RIFF_WAVE*] - - RIFF WAVE header (labl looping) [*RIFF_WAVE_labl*] - RIFF WAVE header (smpl looping) [*RIFF_WAVE_smpl*] + - RIFF WAVE header (cue looping) [*RIFF_WAVE_cue*] + - RIFF WAVE header (labl looping) [*RIFF_WAVE_labl*] + - RIFF WAVE header (ctrl looping) [*RIFF_WAVE_ctrl*] - RIFF WAVE header (wsmp looping) [*RIFF_WAVE_wsmp*] - - RIFF WAVE header (ctrl looping) [*RIFF_WAVE_MWV*] - RIFX WAVE header [*RIFX_WAVE*] - RIFX WAVE header (smpl looping) [*RIFX_WAVE_smpl*] - *riff*: `.wav .lwav .xwav .mwv .da .dax .cd .med .snd .adx .adp .xss .xsew .adpcm .adw .wd .(extensionless) .sbv .wvx .str .at3 .rws .aud .at9 .ckd .saf .ima .nsa .pcm .xvag .ogg .logg .p1d .xms .mus .dat .ldat .wma .lwma .caf .wax .voi .se .v` @@ -1075,7 +1076,7 @@ different internally (encrypted, different versions, etc) and not always can be - Rockstar AWC header [*AWC*] - *awc*: `.awc` - Subfiles: *riff* - - Codecs: PCM16BE PCM16LE AWC_IMA XMA2 MPEG_custom MPEG VORBIS_custom ATRAC9 NGC_DSP + - Codecs: PCM16BE PCM16LE AWC_IMA XMA2 MPEG_custom MPEG VORBIS_custom ATRAC9 NGC_DSP Opus - **opus.c** - Nintendo Switch OPUS header [*OPUS*] - *opus_std*: `.opus .lopus .bgm .opu .ogg .logg + .psi` @@ -1115,6 +1116,10 @@ different internally (encrypted, different versions, etc) and not always can be - *ubi_sb_sequence*: `(base) + .(external)` - *ubi_sb_header*: `(base) + .(external) .kat` - Codecs: PCM16LE AICA_int UBI_IMA UBI_SCE_IMA UBI_ADPCM PSX XBOX_IMA NGC_DSP ATRAC3 XMA OGG_VORBIS DVI_IMA_int +- **ubi_apm.c** + - Ubisoft APM header [*UBI_APM*] + - *ubi_apm*: `.apm` + - Codecs: DVI_IMA_int - **ezw.c** - EZ2DJ EZWAVE header [*EZW*] - *ezw*: `.ezw` @@ -1195,7 +1200,7 @@ different internally (encrypted, different versions, etc) and not always can be - **wave.c** - EngineBlack .WAVE header [*WAVE*] - *wave*: `.wave` - - Codecs: IMA_int NGC_DSP + - Codecs: PCM8 PCM16BE NGC_DSP IMA_int - **wave_segmented.c** - EngineBlack .WAVE header [*WAVE*] - EngineBlack .WAVE header (segmented) [*WAVE_segmented*] @@ -1598,9 +1603,10 @@ different internally (encrypted, different versions, etc) and not always can be - Koei Tecmo KTSR header [*KTSR*] - *ktsr*: `.ktsl2asbin .asbin` - *asrs*: `.srsa` + - *sdbs*: `.k2sb` - *ktsr_internal* - - Subfiles: *riff ogg_vorbis ktss ktac* - - Codecs: MSADPCM_mono NGC_DSP ATRAC9 + - Subfiles: *riff ogg_vorbis ktss ktac ka1a kma9* + - Codecs: MSADPCM_mono KA1A NGC_DSP ATRAC9 - **mups.c** - (container) - *mups*: `.mups .(extensionless)` @@ -1816,6 +1822,30 @@ different internally (encrypted, different versions, etc) and not always can be - Ongakukan RIFF WAVE header [*ONGAKUKAN_RIFF_ADP*] - *adp_ongakukan*: `.adp` - Codecs: ONGAKUKAN_ADPCM +- **sdd.c** + - Doki Denki DSBH header [*SDD*] + - *sdd*: `.sdd` + - Codecs: NGC_DSP PCM16LE PSX +- **ka1a.c** + - Koei Tecmo KA1A header [*KA1A*] + - *ka1a*: `.ka1a` + - Codecs: KA1A +- **hd_bd.c** + - Sony HD+BD header [*HD_BD*] + - *hd_bd*: `.hd .hbd + .bd` + - Codecs: PSX +- **pphd.c** + - Sony PPHD header [*PPHD*] + - *pphd*: `.phd + .pbd` + - Codecs: PSX +- **xabp.c** + - cavia XABp header [*XABP*] + - *xabp*: `.hd2 + .bd` + - Codecs: PSX +- **i3ds.c** + - Codemasters i3DS header [*I3DS*] + - *i3ds*: `.3ds` + - Codecs: NGC_DSP - **agsc.c** - Retro Studios AGSC header [*AGSC*] - *agsc*: `.agsc` diff --git a/src/formats.c b/src/formats.c index 7c99d037..b0b5b3d2 100644 --- a/src/formats.c +++ b/src/formats.c @@ -274,6 +274,7 @@ static const char* extension_list[] = { "joe", "jstm", + "k2sb", "ka1a", "kat", "kces", diff --git a/src/meta/ktsr.c b/src/meta/ktsr.c index b54306eb..d41a8c3e 100644 --- a/src/meta/ktsr.c +++ b/src/meta/ktsr.c @@ -9,8 +9,18 @@ typedef enum { NONE, MSADPCM, DSP, GCADPCM, ATRAC9, RIFF_ATRAC9, KMA9, AT9_KM9, #define MAX_CHANNELS 8 typedef struct { - uint32_t base_offset; bool is_srsa; + bool is_sdbs; + uint32_t as_offset; + uint32_t as_size; + uint32_t st_offset; + uint32_t st_size; +} ktsr_meta_t; + +typedef struct { + uint32_t as_offset; + uint32_t as_size; + int total_subsongs; int target_subsong; ktsr_codec codec; @@ -37,12 +47,12 @@ typedef struct { uint32_t sound_name_offset; uint32_t config_name_offset; char name[255+1]; -} ktsr_header; +} ktsr_header_t; -static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa); -static bool parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf); -static layered_layout_data* build_layered_atrac9(ktsr_header* ktsr, STREAMFILE *sf, uint32_t config_data); -static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf), const char* ext); +static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, ktsr_meta_t* info); +static bool parse_ktsr(ktsr_header_t* ktsr, STREAMFILE* sf); +static layered_layout_data* build_layered_atrac9(ktsr_header_t* ktsr, STREAMFILE *sf, uint32_t config_data); +static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, uint32_t st_offset, ktsr_header_t* ktsr, VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf), const char* ext); /* KTSR - Koei Tecmo sound resource container */ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { @@ -57,7 +67,10 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { if (!check_extensions(sf, "ktsl2asbin,asbin")) return NULL; - return init_vgmstream_ktsr_internal(sf, false) ; + ktsr_meta_t info = { + .as_size = get_streamfile_size(sf), + }; + return init_vgmstream_ktsr_internal(sf, &info); } /* ASRS - container of KTSR found in newer games */ @@ -70,7 +83,7 @@ VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf) { /* 0x08: file size */ /* 0x0c: null */ - /* .srsa: header id (as generated by common tools, probably "(something)asbin") */ + /* .srsa: header id and 'class' in hashed names */ if (!check_extensions(sf, "srsa")) return NULL; @@ -78,29 +91,100 @@ VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf) { * .srsa/srst usually have hashed filenames, so it isn't easy to match them, so this * is mainly useful for .srsa with internal streams. */ - return init_vgmstream_ktsr_internal(sf, true); + ktsr_meta_t info = { + .is_srsa = true, + .as_offset = 0x10, + .as_size = get_streamfile_size(sf) - 0x10, + .st_offset = 0x10, + }; + return init_vgmstream_ktsr_internal(sf, &info); +} + +/* sdbs - container of KTSR found in newer games */ +VGMSTREAM* init_vgmstream_sdbs(STREAMFILE* sf) { + + /* checks */ + if (!is_id32be(0x00, sf, "sdbs")) + return NULL; + + // .srsa: actual extension + if (!check_extensions(sf, "k2sb")) + return NULL; + + // mini-container of memory + stream KTSR + ktsr_meta_t info = { + .is_sdbs = true, + .as_offset = read_u32le(0x04, sf), + .as_size = read_u32le(0x08, sf), + .st_offset = read_u32le(0x0c, sf), + .st_size = read_u32le(0x10, sf), + }; + return init_vgmstream_ktsr_internal(sf, &info); } -static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) { - VGMSTREAM* vgmstream = NULL; - STREAMFILE* sf_b = NULL; - ktsr_header ktsr = {0}; - int target_subsong = sf->stream_index; - bool separate_offsets = false; +static STREAMFILE* setup_sf_body(STREAMFILE* sf, ktsr_header_t* ktsr, ktsr_meta_t* info) { - ktsr.is_srsa = is_srsa; - if (ktsr.is_srsa) { - ktsr.base_offset = 0x10; + // use current + if (!ktsr->is_external) + return sf; + + // skip extra header (internals are pre-adjusted) */ + if (ktsr->is_external && info->st_offset) { + for (int i = 0; i < ktsr->channels; i++) { + ktsr->stream_offsets[i] += info->st_offset; + } + + //ktsr->extra_offset += ktsr->st_offset; // ? + } + + if (info->is_sdbs) { + // .k2sb have data pasted together + return sf; } + /* open companion body */ + STREAMFILE* sf_b = NULL; + if (info->is_srsa) { + // try parsing TXTM if present, since .srsa+srst have hashed names and don't match unless renamed + sf_b = read_filemap_file(sf, 0); + } + + if (!sf_b) { + // try (name).(ext), as seen in older games + const char* companion_ext = check_extensions(sf, "asbin") ? "stbin" : "ktsl2stbin"; + if (info->is_srsa) + companion_ext = "srst"; + + sf_b = open_streamfile_by_ext(sf, companion_ext); + if (!sf_b) { + vgm_logi("KTSR: companion file '*.%s' not found\n", companion_ext); + return NULL; + } + } + + return sf_b; +} + + +static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, ktsr_meta_t* info) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* sf_b = NULL; + ktsr_header_t ktsr = {0}; + + + ktsr.as_offset = info->as_offset; + ktsr.as_size = info->as_size; + /* checks */ - if (!is_id32be(ktsr.base_offset + 0x00, sf, "KTSR")) + if (!is_id32be(ktsr.as_offset + 0x00, sf, "KTSR")) return NULL; - if (read_u32be(ktsr.base_offset + 0x04, sf) != 0x777B481A) /* hash id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */ + if (read_u32be(ktsr.as_offset + 0x04, sf) != 0x777B481A) /* hash id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */ return NULL; + bool separate_offsets = false; + int target_subsong = sf->stream_index; /* KTSR can be a memory file (ktsl2asbin), streams (ktsl2stbin) and global config (ktsl2gcbin) * This accepts .ktsl2asbin with internal data or external streams as subsongs. @@ -111,37 +195,15 @@ static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) { ktsr.target_subsong = target_subsong; if (!parse_ktsr(&ktsr, sf)) - goto fail; + return NULL; if (ktsr.total_subsongs == 0) { vgm_logi("KTSR: file has no subsongs\n"); return NULL; } - /* open companion body */ - if (ktsr.is_external) { - if (ktsr.is_srsa) { - /* try parsing TXTM if present, since .srsa+srst have hashed names and don't match unless renamed */ - sf_b = read_filemap_file(sf, 0); - } - - if (!sf_b) { - /* try (name).(ext), as seen in older games */ - const char* companion_ext = check_extensions(sf, "asbin") ? "stbin" : "ktsl2stbin"; - if (ktsr.is_srsa) - companion_ext = "srst"; - - sf_b = open_streamfile_by_ext(sf, companion_ext); - if (!sf_b) { - vgm_logi("KTSR: companion file '*.%s' not found\n", companion_ext); - goto fail; - } - } - } - else { - sf_b = sf; - } + sf_b = setup_sf_body(sf, &ktsr, info); /* subfiles */ { @@ -166,7 +228,7 @@ static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) { } if (init_vgmstream) { - vgmstream = init_vgmstream_ktsr_sub(sf_b, &ktsr, init_vgmstream, ext); + vgmstream = init_vgmstream_ktsr_sub(sf_b, info->st_offset, &ktsr, init_vgmstream, ext); if (!vgmstream) goto fail; if (sf_b != sf) close_streamfile(sf_b); @@ -253,8 +315,7 @@ static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) { /* data offset per channel is absolute (not actual interleave since there is padding) in some cases */ if (separate_offsets) { - int i; - for (i = 0; i < ktsr.channels; i++) { + for (int i = 0; i < ktsr.channels; i++) { vgmstream->ch[i].offset = ktsr.stream_offsets[i]; } } @@ -269,11 +330,11 @@ fail: } // TODO improve, unify with other metas that do similar stuff -static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf), const char* ext) { +static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, uint32_t st_offset, ktsr_header_t* ktsr, VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf), const char* ext) { VGMSTREAM* sub_vgmstream = NULL; STREAMFILE* temp_sf = NULL; - temp_sf = setup_ktsr_streamfile(sf_b, ktsr->is_external, ktsr->stream_offsets[0], ktsr->stream_sizes[0], ext); + temp_sf = setup_ktsr_streamfile(sf_b, st_offset, ktsr->is_external, ktsr->stream_offsets[0], ktsr->stream_sizes[0], ext); if (!temp_sf) return NULL; sub_vgmstream = init_vgmstream(temp_sf); @@ -293,18 +354,17 @@ static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, V } -static layered_layout_data* build_layered_atrac9(ktsr_header* ktsr, STREAMFILE* sf, uint32_t config_data) { +static layered_layout_data* build_layered_atrac9(ktsr_header_t* ktsr, STREAMFILE* sf, uint32_t config_data) { STREAMFILE* temp_sf = NULL; layered_layout_data* data = NULL; int layers = ktsr->channels; - int i; /* init layout */ data = init_layout_layered(layers); if (!data) goto fail; - for (i = 0; i < layers; i++) { + for (int i = 0; i < layers; i++) { data->layers[i] = allocate_vgmstream(1, 0); if (!data->layers[i]) goto fail; @@ -349,7 +409,7 @@ fail: } -static int parse_codec(ktsr_header* ktsr) { +static int parse_codec(ktsr_header_t* ktsr) { /* platform + format to codec, simplified until more codec combos are found */ switch(ktsr->platform) { @@ -399,12 +459,10 @@ fail: return 0; } -static bool parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offset) { +static bool parse_ktsr_subfile(ktsr_header_t* ktsr, STREAMFILE* sf, uint32_t offset) { uint32_t suboffset, starts_offset, sizes_offset; - int i; - uint32_t type; - type = read_u32be(offset + 0x00, sf); /* hash-id? */ + uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */ //size = read_u32le(offset + 0x04, sf); // probably could check the flags in sound header, but the format is kinda messy @@ -496,7 +554,7 @@ static bool parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offse starts_offset = read_u32le(suboffset + 0x00, sf) + offset; sizes_offset = read_u32le(suboffset + 0x04, sf) + offset; - for (i = 0; i < ktsr->channels; i++) { + for (int i = 0; i < ktsr->channels; i++) { ktsr->stream_offsets[i] = read_u32le(starts_offset + 0x04*i, sf) + offset; ktsr->stream_sizes[i] = read_u32le(sizes_offset + 0x04*i, sf); } @@ -513,7 +571,6 @@ static bool parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offse if (!parse_codec(ktsr)) goto fail; - return true; fail: VGM_LOG("ktsr: error parsing subheader\n"); @@ -553,7 +610,7 @@ static size_t read_string_ktsr(char* buf, size_t buf_size, off_t offset, STREAMF return 0; } -static void build_name(ktsr_header* ktsr, STREAMFILE* sf) { +static void build_name(ktsr_header_t* ktsr, STREAMFILE* sf) { char sound_name[255] = {0}; char config_name[255] = {0}; @@ -583,13 +640,13 @@ static void build_name(ktsr_header* ktsr, STREAMFILE* sf) { } -static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf) { +static void parse_longname(ktsr_header_t* ktsr, STREAMFILE* sf) { /* more configs than sounds is possible so we need target_id first */ uint32_t offset, end, name_offset; uint32_t stream_id; - offset = 0x40 + ktsr->base_offset; - end = get_streamfile_size(sf) - ktsr->base_offset; + offset = 0x40 + ktsr->as_offset; + end = ktsr->as_offset + ktsr->as_size; while (offset < end) { uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */ uint32_t size = read_u32le(offset + 0x04, sf); @@ -614,7 +671,7 @@ static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf) { } } -static bool parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { +static bool parse_ktsr(ktsr_header_t* ktsr, STREAMFILE* sf) { uint32_t offset, end, header_offset, name_offset; uint32_t stream_count; @@ -631,18 +688,18 @@ static bool parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { * up to 40: reserved * until end: entries (totals not defined) */ - ktsr->platform = read_u8(ktsr->base_offset + 0x0b,sf); - ktsr->audio_id = read_u32le(ktsr->base_offset + 0x0c,sf); + ktsr->platform = read_u8 (ktsr->as_offset + 0x0b,sf); + ktsr->audio_id = read_u32le(ktsr->as_offset + 0x0c,sf); - if (read_u32le(ktsr->base_offset + 0x18, sf) != read_u32le(ktsr->base_offset + 0x1c, sf)) + if (read_u32le(ktsr->as_offset + 0x18, sf) != read_u32le(ktsr->as_offset + 0x1c, sf)) goto fail; - if (read_u32le(ktsr->base_offset + 0x1c, sf) != get_streamfile_size(sf) - ktsr->base_offset) { + if (read_u32le(ktsr->as_offset + 0x1c, sf) != ktsr->as_size) { vgm_logi("KTSR: incorrect file size (bad rip?)\n"); goto fail; } - offset = 0x40 + ktsr->base_offset; - end = get_streamfile_size(sf) - ktsr->base_offset; + offset = 0x40 + ktsr->as_offset; + end = ktsr->as_offset + ktsr->as_size; while (offset < end) { uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */ uint32_t size = read_u32le(offset + 0x04, sf); @@ -714,15 +771,6 @@ static bool parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { parse_longname(ktsr, sf); build_name(ktsr, sf); - /* skip TSRS header (internals are pre-adjusted) */ - if (ktsr->is_external && ktsr->base_offset) { - for (int i = 0; i < ktsr->channels; i++) { - ktsr->stream_offsets[i] += ktsr->base_offset; - } - - ktsr->extra_offset += ktsr->base_offset; /* ? */ - } - return true; fail: vgm_logi("KTSR: unknown variation (report)\n"); diff --git a/src/meta/ktsr_streamfile.h b/src/meta/ktsr_streamfile.h index b0cc027d..a61ae667 100644 --- a/src/meta/ktsr_streamfile.h +++ b/src/meta/ktsr_streamfile.h @@ -119,18 +119,14 @@ static size_t ktsr_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t l /* Decrypts blowfish KTSR streams */ -static STREAMFILE* setup_ktsr_streamfile(STREAMFILE* sf, bool is_external, uint32_t subfile_offset, uint32_t subfile_size, const char* extension) { +static STREAMFILE* setup_ktsr_streamfile(STREAMFILE* sf, uint32_t st_offset, bool is_external, uint32_t subfile_offset, uint32_t subfile_size, const char* extension) { STREAMFILE* new_sf = NULL; ktsr_io_data io_data = {0}; if (is_external) { - uint32_t offset = 0x00; - if (is_id32be(0x00, sf, "TSRS")) - offset += 0x10; - if (!is_id32be(offset + 0x00, sf, "KTSR")) - goto fail; - - read_streamfile(io_data.key, offset + 0x20, sizeof(io_data.key), sf); + if (!is_id32be(st_offset + 0x00, sf, "KTSR")) + return NULL; + read_streamfile(io_data.key, st_offset + 0x20, sizeof(io_data.key), sf); } /* setup subfile */ @@ -143,10 +139,7 @@ static STREAMFILE* setup_ktsr_streamfile(STREAMFILE* sf, bool is_external, uint3 new_sf = open_clamp_streamfile_f(new_sf, subfile_offset, subfile_size); if (extension) new_sf = open_fakename_streamfile_f(new_sf, NULL, extension); - return new_sf; -fail: - return NULL; } #endif diff --git a/src/meta/meta.h b/src/meta/meta.h index 556eff51..08898d4a 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -898,6 +898,7 @@ VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf); VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf); VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_sdbs(STREAMFILE* sf); VGMSTREAM* init_vgmstream_mups(STREAMFILE* sf); diff --git a/src/vgmstream_init.c b/src/vgmstream_init.c index 6ac3c928..60b01442 100644 --- a/src/vgmstream_init.c +++ b/src/vgmstream_init.c @@ -515,6 +515,7 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_pphd, init_vgmstream_xabp, init_vgmstream_i3ds, + init_vgmstream_sdbs, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ init_vgmstream_agsc,