From ed00ccb1a8c195ad8d2d9277be5e27eee8a51e5b Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 22 Oct 2022 15:04:30 +0200 Subject: [PATCH 1/4] Fix some CPK .awb [Puyo Puyo 20th Anniv. (3DS)] --- src/meta/cpk.c | 83 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/src/meta/cpk.c b/src/meta/cpk.c index 1dd2ffaf..8ebe4a26 100644 --- a/src/meta/cpk.c +++ b/src/meta/cpk.c @@ -18,6 +18,8 @@ VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { off_t subfile_offset = 0; size_t subfile_size = 0; utf_context* utf = NULL; + utf_context* utf_h = NULL; + utf_context* utf_l = NULL; int total_subsongs, target_subsong = sf->stream_index; int subfile_id = 0; cpk_type_t type; @@ -40,14 +42,17 @@ VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { /* CPK .cpk is CRI's generic file container, but here we only support CPK .awb used as * early audio bank, that like standard AFS2 .awb comes with .acb */ { - int rows, i; + int rows, rows_l, rows_h, i; const char* name; + const char* name_l; + const char* name_h; const char* Tvers; uint32_t table_offset = 0, offset; uint32_t Files = 0, FilesL = 0, FilesH = 0; uint64_t ContentOffset = 0, ItocOffset = 0; uint16_t Align = 0; uint32_t DataL_offset = 0, DataL_size = 0, DataH_offset = 0, DataH_size = 0; + int id_align; /* base header */ table_offset = 0x10; @@ -65,7 +70,7 @@ VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { utf_close(utf); utf = NULL; - if (strncmp(Tvers, "awb", 3) != 0) /* starts with "awb" + ".(version)" (SFvTK, MGS3D) or " for (version)" (ACI) */ + if (strncmp(Tvers, "awb", 3) != 0) /* starts with "awb" + ".(version)" (SFvTK, MGS3D) or " for (version)" (ACI, Puyo) */ goto fail; if (Files <= 0) goto fail; @@ -104,51 +109,77 @@ VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { /* DataL header */ table_offset = DataL_offset; - utf = utf_open(sf, table_offset, &rows, &name); - if (!utf || strcmp(name, "CpkItocL") != 0 || rows != FilesL) + utf_l = utf_open(sf, table_offset, &rows_l, &name_l); + if (!utf_l || strcmp(name_l, "CpkItocL") != 0 || rows_l != FilesL) goto fail; - for (i = 0; i < rows; i++) { + /* DataH header */ + table_offset = DataH_offset; + utf_h = utf_open(sf, table_offset, &rows_h, &name_h); + if (!utf_h || strcmp(name_h, "CpkItocH") != 0 || rows_h != FilesH) + goto fail; + + + /* rarely ID doesn't start at 0, adjust values [Puyo Puyo 20th Anniversary (3DS)-3DS_manzai_voice] */ + id_align = 0; + { + uint16_t ID_l = 0; + uint16_t ID_h = 0; + + /* use lower as base, one table may not exist */ + utf_query_u16(utf_l, 0, "ID", &ID_l); + utf_query_u16(utf_h, 0, "ID", &ID_h); + if (rows_l > 0 && rows_h > 0) { + if (ID_l > 0 && ID_h > 0) /* one is 0 = no adjust needed */ + id_align = ID_l < ID_h ? ID_l : ID_h; + } + else if (rows_l) { + id_align = ID_l; + } + else if (rows_h) { + id_align = ID_h; + } + } + + /* save DataL sizes */ + for (i = 0; i < rows_l; i++) { uint16_t ID = 0; uint16_t FileSize, ExtractSize; - if (!utf_query_u16(utf, i, "ID", &ID) || - !utf_query_u16(utf, i, "FileSize", &FileSize) || - !utf_query_u16(utf, i, "ExtractSize", &ExtractSize)) + if (!utf_query_u16(utf_l, i, "ID", &ID) || + !utf_query_u16(utf_l, i, "FileSize", &FileSize) || + !utf_query_u16(utf_l, i, "ExtractSize", &ExtractSize)) goto fail; + ID -= id_align; if (ID >= Files || FileSize != ExtractSize || sizes[ID]) goto fail; sizes[ID] = FileSize; } - utf_close(utf); - utf = NULL; - - /* DataR header */ - table_offset = DataH_offset; - utf = utf_open(sf, table_offset, &rows, &name); - if (!utf || strcmp(name, "CpkItocH") != 0 || rows != FilesH) - goto fail; - - for (i = 0; i < rows; i++) { + /* save DataH sizes */ + for (i = 0; i < rows_h; i++) { uint16_t ID = 0; uint32_t FileSize, ExtractSize; - if (!utf_query_u16(utf, i, "ID", &ID) || - !utf_query_u32(utf, i, "FileSize", &FileSize) || - !utf_query_u32(utf, i, "ExtractSize", &ExtractSize)) + if (!utf_query_u16(utf_h, i, "ID", &ID) || + !utf_query_u32(utf_h, i, "FileSize", &FileSize) || + !utf_query_u32(utf_h, i, "ExtractSize", &ExtractSize)) goto fail; + ID -= id_align; if (ID >= Files || FileSize != ExtractSize || sizes[ID]) goto fail; sizes[ID] = FileSize; } - utf_close(utf); - utf = NULL; + utf_close(utf_l); + utf_l = NULL; + + utf_close(utf_h); + utf_h = NULL; /* find actual offset */ @@ -156,7 +187,7 @@ VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { for (i = 0; i < Files; i++) { uint32_t size = sizes[i]; if (i + 1 == target_subsong) { - subfile_id = i; + subfile_id = i + id_align; subfile_offset = offset; subfile_size = size; break; @@ -166,7 +197,7 @@ VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { if (Align && (offset % Align)) offset += Align - (offset % Align); } - + free(sizes); sizes = NULL; } @@ -224,6 +255,8 @@ VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { fail: free(sizes); utf_close(utf); + utf_close(utf_l); + utf_close(utf_h); close_streamfile(temp_sf); close_vgmstream(vgmstream); return NULL; From f601ed4ed6abadc561623cb31695f949a5078175 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 22 Oct 2022 15:04:47 +0200 Subject: [PATCH 2/4] doc --- src/meta/adx_keys.h | 2 +- src/meta/hca_keys.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/meta/adx_keys.h b/src/meta/adx_keys.h index f306f747..14d2fb0d 100644 --- a/src/meta/adx_keys.h +++ b/src/meta/adx_keys.h @@ -264,7 +264,7 @@ static const adxkey_info adxkey9_list[] = { /* Shin Megami Tensei V (Switch) */ {0x0000,0x0000,0x0000, NULL,1731948526}, // 00000000673B6FEE - /* Persona 5 Royal (multi) */ + /* Persona 5 Royal (PC) */ {0x0000,0x0000,0x0000, NULL,9923540143823782}, // 002341683D2FDBA6 }; diff --git a/src/meta/hca_keys.h b/src/meta/hca_keys.h index 31c78aea..662b2f0b 100644 --- a/src/meta/hca_keys.h +++ b/src/meta/hca_keys.h @@ -1055,10 +1055,10 @@ static const hcakey_info hcakey_list[] = { // Echoes of Mana (Android) {1090221945250803295}, // 0F213F153D026E5F - // Persona 5 Royal (multi) + // Persona 5 Royal (Switch) {9923540143823782}, // 002341683D2FDBA6 - // P Sengoku Otome 6 ~Akatsuki no Sekigahahara~ (Android) + // P Sengoku Otome 6 ~Akatsuki no Sekigahara~ (Android) {97648135}, // 0000000005d1fe07 // CHUNITHM International Version (AC) From edaac2c28907543786df5a97436d23bcf7584654 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 22 Oct 2022 15:09:20 +0200 Subject: [PATCH 3/4] cleanup --- src/coding/coding.h | 2 +- src/coding/ima_decoder.c | 8 ++++---- src/decode.c | 8 ++++---- src/formats.c | 2 +- src/meta/bcstm.c | 2 +- src/meta/bfwav.c | 2 +- src/vgmstream.h | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/coding/coding.h b/src/coding/coding.h index 0cf10959..70e59ef3 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -17,7 +17,7 @@ void g72x_init_state(struct g72x_state* state_ptr); /* ima_decoder */ void decode_standard_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo, int is_high_first); -void decode_3ds_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_nw_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_snds_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_otns_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_wv6_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index ae136a96..76c6f49f 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -108,8 +108,8 @@ static void std_ima_expand_nibble_mul(VGMSTREAMCHANNEL * stream, off_t byte_offs if (*step_index > 88) *step_index=88; } -/* 3DS IMA (Mario Golf, Mario Tennis; maybe other Camelot games) */ -static void n3ds_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { +/* NintendoWare IMA (Mario Golf, Mario Tennis; maybe other Camelot games) */ +static void nw_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { int sample_nibble, sample_decoded, step, delta; sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; @@ -402,7 +402,7 @@ void decode_mtf_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa stream->adpcm_step_index = step_index; } -void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_nw_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i, sample_count; int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; @@ -415,7 +415,7 @@ void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa off_t byte_offset = stream->offset + i/2; int nibble_shift = (i&1?4:0); //low nibble order - n3ds_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + nw_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); outbuf[sample_count] = (short)(hist1); } diff --git a/src/decode.c b/src/decode.c index 55b4bc5c..ad7e2046 100644 --- a/src/decode.c +++ b/src/decode.c @@ -425,7 +425,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { case coding_PCM4_U: case coding_IMA_int: case coding_DVI_IMA_int: - case coding_3DS_IMA: + case coding_NW_IMA: case coding_WV6_IMA: case coding_HV_IMA: case coding_FFTA2_IMA: @@ -638,7 +638,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { case coding_IMA_int: case coding_DVI_IMA: case coding_DVI_IMA_int: - case coding_3DS_IMA: + case coding_NW_IMA: case coding_WV6_IMA: case coding_HV_IMA: case coding_FFTA2_IMA: @@ -1173,9 +1173,9 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ } break; } - case coding_3DS_IMA: + case coding_NW_IMA: for (ch = 0; ch < vgmstream->channels; ch++) { - decode_3ds_ima(&vgmstream->ch[ch], buffer+ch, + decode_nw_ima(&vgmstream->ch[ch], buffer+ch, vgmstream->channels, vgmstream->samples_into_block, samples_to_do); } break; diff --git a/src/formats.c b/src/formats.c index 85d76a9a..37993b78 100644 --- a/src/formats.c +++ b/src/formats.c @@ -790,7 +790,7 @@ static const coding_info coding_info_list[] = { {coding_IMA_int, "IMA 4-bit ADPCM (mono/interleave)"}, {coding_DVI_IMA, "Intel DVI 4-bit IMA ADPCM"}, {coding_DVI_IMA_int, "Intel DVI 4-bit IMA ADPCM (mono/interleave)"}, - {coding_3DS_IMA, "3DS IMA 4-bit ADPCM"}, + {coding_NW_IMA, "NintendoWare IMA 4-bit ADPCM"}, {coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"}, {coding_QD_IMA, "Quantic Dream 4-bit IMA ADPCM"}, {coding_WV6_IMA, "Gorilla Systems WV6 4-bit IMA ADPCM"}, diff --git a/src/meta/bcstm.c b/src/meta/bcstm.c index 87603d29..2ad97a8a 100644 --- a/src/meta/bcstm.c +++ b/src/meta/bcstm.c @@ -104,7 +104,7 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) { vgmstream->coding_type = coding_NGC_DSP; if (is_camelot_ima) { - vgmstream->coding_type = coding_3DS_IMA; + vgmstream->coding_type = coding_NW_IMA; } else { int i, c; diff --git a/src/meta/bfwav.c b/src/meta/bfwav.c index a7298dc0..e348367e 100644 --- a/src/meta/bfwav.c +++ b/src/meta/bfwav.c @@ -227,7 +227,7 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) { break; case 0x03: - vgmstream->coding_type = coding_3DS_IMA; + vgmstream->coding_type = coding_NW_IMA; /* hist is read below */ break; diff --git a/src/vgmstream.h b/src/vgmstream.h index 962db34e..91925e19 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -108,7 +108,7 @@ typedef enum { coding_IMA_int, /* IMA ADPCM (mono/interleave, low nibble first) */ coding_DVI_IMA, /* DVI IMA ADPCM (stereo or mono, high nibble first) */ coding_DVI_IMA_int, /* DVI IMA ADPCM (mono/interleave, high nibble first) */ - coding_3DS_IMA, /* 3DS IMA ADPCM */ + coding_NW_IMA, coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */ coding_QD_IMA, coding_WV6_IMA, /* Gorilla Systems WV6 4-bit IMA ADPCM */ From b947f9b9e1177c66935709386e480ec5e8782121 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 22 Oct 2022 15:14:57 +0200 Subject: [PATCH 4/4] cleanup --- src/meta/scd_pcm.c | 130 +++++++------- src/meta/sqex_scd_sscf.c | 347 ++++++++++++++++++------------------ src/util/text_reader.c | 374 +++++++++++++++++++-------------------- src/vgmstream.c | 2 +- 4 files changed, 426 insertions(+), 427 deletions(-) diff --git a/src/meta/scd_pcm.c b/src/meta/scd_pcm.c index d28c669a..87648f2c 100644 --- a/src/meta/scd_pcm.c +++ b/src/meta/scd_pcm.c @@ -1,65 +1,65 @@ -#include "meta.h" -#include "../coding/coding.h" - - -/* .PCM - from Lunar: Eternal Blue (Sega CD) */ -VGMSTREAM * init_vgmstream_scd_pcm(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - - - /* checks */ - if (!check_extensions(streamFile, "pcm")) - goto fail; - - if (read_16bitBE(0x00,streamFile) == 0x0002) { - channel_count = 1; - } - else if (read_16bitBE(0x00,streamFile) == 0x0001) { - channel_count = 2; /* RP025.PCM, RP039.PCM */ - } - else { - goto fail; - } - - start_offset = 0x800; - - - /* extra validations since .pcm it's kinda generic */ - { - off_t i; - /* should be empty up to start (~0x0a/~0x10 sometimes has unknown values) */ - for (i = 0x20; i < start_offset; i++) { - if (read_8bit(i, streamFile) != 0) - goto fail; - } - } - - /* loops start 0 is possible, plus all? files loops - * (even sfx/voices loop, but those set loop start in silence near the end) */ - loop_flag = (read_32bitBE(0x06,streamFile) != 0); - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_SCD_PCM; - vgmstream->sample_rate = 32500; /* looks correct compared to emu/recordings */ - vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count, 8); - vgmstream->loop_start_sample = read_32bitBE(0x02,streamFile)*0x400*2; - vgmstream->loop_end_sample = read_32bitBE(0x06,streamFile)*2; - - vgmstream->coding_type = coding_PCM8_SB; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x800; - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* .PCM - from Lunar: Eternal Blue (Sega CD) */ +VGMSTREAM* init_vgmstream_scd_pcm(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels; + + + /* checks */ + if (!check_extensions(sf, "pcm")) + goto fail; + + if (read_u16be(0x00,sf) == 0x0002) { + channels = 1; + } + else if (read_u16be(0x00,sf) == 0x0001) { + channels = 2; /* RP025.PCM, RP039.PCM */ + } + else { + goto fail; + } + + start_offset = 0x800; + + + /* extra validations since .pcm it's kinda generic */ + { + off_t i; + /* should be empty up to start (~0x0a/~0x10 sometimes has unknown values) */ + for (i = 0x20; i < start_offset; i++) { + if (read_u8(i, sf) != 0) + goto fail; + } + } + + /* loops start 0 is possible, plus all? files loops + * (even sfx/voices loop, but those set loop start in silence near the end) */ + loop_flag = (read_u32be(0x06,sf) != 0); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SCD_PCM; + vgmstream->sample_rate = 32500; /* looks correct compared to emu/recordings */ + vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(sf) - start_offset, channels, 8); + vgmstream->loop_start_sample = read_s32be(0x02,sf)*0x400*2; + vgmstream->loop_end_sample = read_s32be(0x06,sf)*2; + + vgmstream->coding_type = coding_PCM8_SB; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x800; + + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/sqex_scd_sscf.c b/src/meta/sqex_scd_sscf.c index 2bcb4178..c887cb9d 100644 --- a/src/meta/sqex_scd_sscf.c +++ b/src/meta/sqex_scd_sscf.c @@ -1,174 +1,173 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* SSCF - Square-Enix games, older version of .scd [Crisis Core -Final Fantasy VII- (PSP), Dissidia 012 (PSP)] */ -VGMSTREAM * init_vgmstream_scd_sscf(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset, meta_offset, stream_offset; - size_t stream_size; - int loop_flag, channel_count, sample_rate; - int total_subsongs, target_subsong = streamFile->stream_index; - - - /* checks */ - if (!check_extensions(streamFile, "scd")) - goto fail; - - if (read_32bitBE(0x00,streamFile) != 0x53534346) /* "SSCF" "*/ - goto fail; - if (!(read_32bitBE(0x04,streamFile) == 0x002070210 || /* version? [Crisis Core (PSP)] */ - read_32bitBE(0x04,streamFile) == 0x10020702)) /* inverted version? [Dissidia (PSP)] */ - goto fail; - /* 0x08: file size, except for a few files that with a weird value */ - /* 0x10: file id? */ - - - /* find total subsongs (entries can be dummy) and target subsong */ - { - int i,j, is_dupe; - int entries = read_32bitLE(0x0c,streamFile); - off_t stream_offsets[0x800]; - - if (entries > 0x800) /* meh */ - goto fail; - - - if (target_subsong == 0) target_subsong = 1; - - meta_offset = 0; - total_subsongs = 0; - for (i = 0; i < entries; i++) { - off_t entry_offset = 0x20 + (0x20*i); - off_t entry_stream_offset; - - /* skip dummies */ - if (read_32bitLE(entry_offset+0x08,streamFile) == 0) /* size 0 */ - continue; - if (read_16bitLE(entry_offset+0x0c,streamFile) == 0) /* no sample rate */ - continue; - - /* skip repeated sounds */ - is_dupe = 0; - entry_stream_offset = read_32bitLE(entry_offset+0x04,streamFile); - for (j = 0; j < total_subsongs; j++) { - if (entry_stream_offset == stream_offsets[j]) { - is_dupe = 1; - break; - } - } - if (is_dupe) - continue; - stream_offsets[total_subsongs] = entry_stream_offset; - - /* ok */ - total_subsongs++; - if (total_subsongs == target_subsong) { - meta_offset = entry_offset; - } - } - - if (meta_offset == 0) - goto fail; - } - - - /* 0x00(2): config? usually 0x00/0x01 */ - /* 0x02(2): loop config */ //todo when 1 uses PS-ADPCM uses SPU loop flags to set start/end - stream_offset = read_32bitLE(meta_offset+0x04,streamFile); /* absolute */ - stream_size = read_32bitLE(meta_offset+0x08,streamFile); - sample_rate = (uint16_t)read_16bitLE(meta_offset+0x0c,streamFile); - /* 0x0e: config? */ - /* 0x10: config? */ - /* 0x14: 0xCA5F or 0x5FCA */ - /* 0x18: config? */ - /* 0x1c: null / some id? */ - - loop_flag = 0; - channel_count = 1; - start_offset = stream_offset; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = sample_rate; - vgmstream->num_streams = total_subsongs; - vgmstream->stream_size = stream_size; - vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count); - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - - vgmstream->meta_type = meta_SCD_SSCF; - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_none; - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - -#if 0 -/* SSCF X360 - updated SCD with encrypted data [Final Fantasy XI (360), PlayOnline Viewer (X360)] */ -VGMSTREAM * init_vgmstream_scd_sscf_x360(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset, meta_offset, stream_offset; - size_t stream_size; - int loop_flag, channel_count, sample_rate; - int total_subsongs, target_subsong = streamFile->stream_index; - - - /* checks */ - if (!check_extensions(streamFile, "scd")) - goto fail; - - if (read_32bitBE(0x00,streamFile) != 0x53534346) /* "SSCF" "*/ - goto fail; - if (read_32bitBE(0x04,streamFile) != 0x20050300)/* version? */ - goto fail; - /* 0x08: file size, except for a few files that with a weird value */ - /* 0x0c: null */ - /* 0x10: file id? */ - /* 0x14: encryption key (different files with the same value encrypt the same) */ - - /* 0x1c: entry count */ - - /* ~0x20: entries start? */ - /* 0x40: num samples? */ - /* 0x44: loop start? */ - /* 0x50: channels */ - /* 0x54: sample rate */ - - /* 0x80: encrypted RIFF data */ - - - loop_flag = 0; - channel_count = 1; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = ...; - - vgmstream->meta_type = meta_SCD_SSCF; - vgmstream->coding_type = coding_...; - vgmstream->layout_type = layout_none; - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} -#endif +#include "meta.h" +#include "../coding/coding.h" + +/* SSCF - Square-Enix games, older version of .scd [Crisis Core -Final Fantasy VII- (PSP), Dissidia 012 (PSP)] */ +VGMSTREAM* init_vgmstream_scd_sscf(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t start_offset, meta_offset, stream_offset, stream_size; + int loop_flag, channels, sample_rate; + int total_subsongs, target_subsong = sf->stream_index; + + + /* checks */ + if (!is_id32be(0x00,sf, "SSCF")) + goto fail; + if (!check_extensions(sf, "scd")) + goto fail; + + if (!(read_u32be(0x04,sf) == 0x02070210 || /* version? [Crisis Core (PSP)] */ + read_u32be(0x04,sf) == 0x10020702)) /* inverted version? [Dissidia (PSP)] */ + goto fail; + /* 0x08: file size, except for a few files that with a weird value */ + /* 0x10: file id? */ + + + /* find total subsongs (entries can be dummy) and target subsong */ + { + int i,j, is_dupe; + int entries = read_s32le(0x0c,sf); + uint32_t stream_offsets[0x800]; + + if (entries > 0x800) /* meh */ + goto fail; + + + if (target_subsong == 0) target_subsong = 1; + + meta_offset = 0; + total_subsongs = 0; + for (i = 0; i < entries; i++) { + uint32_t entry_offset = 0x20 + (0x20*i); + uint32_t entry_stream_offset; + + /* skip dummies */ + if (read_u32le(entry_offset+0x08,sf) == 0) /* size 0 */ + continue; + if (read_u16le(entry_offset+0x0c,sf) == 0) /* no sample rate */ + continue; + + /* skip repeated sounds */ + is_dupe = 0; + entry_stream_offset = read_u32le(entry_offset+0x04,sf); + for (j = 0; j < total_subsongs; j++) { + if (entry_stream_offset == stream_offsets[j]) { + is_dupe = 1; + break; + } + } + if (is_dupe) + continue; + stream_offsets[total_subsongs] = entry_stream_offset; + + /* ok */ + total_subsongs++; + if (total_subsongs == target_subsong) { + meta_offset = entry_offset; + } + } + + if (meta_offset == 0) + goto fail; + } + + + /* 0x00(2): config? usually 0x00/0x01 */ + /* 0x02(2): loop config */ //todo when 1 uses PS-ADPCM uses SPU loop flags to set start/end + stream_offset = read_u32le(meta_offset+0x04,sf); /* absolute */ + stream_size = read_u32le(meta_offset+0x08,sf); + sample_rate = read_u16le(meta_offset+0x0c,sf); + /* 0x0e: config? */ + /* 0x10: config? */ + /* 0x14: 0xCA5F or 0x5FCA */ + /* 0x18: config? */ + /* 0x1c: null / some id? */ + + loop_flag = 0; + channels = 1; + start_offset = stream_offset; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; + vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels); + if (loop_flag) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + + vgmstream->meta_type = meta_SCD_SSCF; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_none; + + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + +#if 0 +/* SSCF X360 - updated SCD with encrypted data [Final Fantasy XI (360), PlayOnline Viewer (X360)] */ +VGMSTREAM * init_vgmstream_scd_sscf_x360(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, meta_offset, stream_offset; + size_t stream_size; + int loop_flag, channel_count, sample_rate; + int total_subsongs, target_subsong = streamFile->stream_index; + + + /* checks */ + if (!check_extensions(streamFile, "scd")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x53534346) /* "SSCF" "*/ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0x20050300)/* version? */ + goto fail; + /* 0x08: file size, except for a few files that with a weird value */ + /* 0x0c: null */ + /* 0x10: file id? */ + /* 0x14: encryption key (different files with the same value encrypt the same) */ + + /* 0x1c: entry count */ + + /* ~0x20: entries start? */ + /* 0x40: num samples? */ + /* 0x44: loop start? */ + /* 0x50: channels */ + /* 0x54: sample rate */ + + /* 0x80: encrypted RIFF data */ + + + loop_flag = 0; + channel_count = 1; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ...; + + vgmstream->meta_type = meta_SCD_SSCF; + vgmstream->coding_type = coding_...; + vgmstream->layout_type = layout_none; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} +#endif diff --git a/src/util/text_reader.c b/src/util/text_reader.c index 294762fc..c359ae95 100644 --- a/src/util/text_reader.c +++ b/src/util/text_reader.c @@ -1,187 +1,187 @@ -#include -#include "text_reader.h" -#include "log.h" - - -/* convenience function to init the above struct */ -int text_reader_init(text_reader_t* tr, uint8_t* buf, int buf_size, STREAMFILE* sf, uint32_t offset, uint32_t max_offset) { - memset(tr, 0, sizeof(text_reader_t)); - - if (buf_size <= 1 || !buf || !sf) - return 0; - - tr->buf = buf; - tr->buf_size = buf_size; - tr->sf = sf; - tr->offset = offset; - - if (!max_offset) - max_offset = get_streamfile_size(sf); - tr->max_offset = max_offset; - - return 1; -} - - -/* reads more data into buf and adjust values */ -static void prepare_buf(text_reader_t* tr) { - - /* since we may read N lines in the same buffer, move starting pos each call */ - tr->pos = tr->next_pos; - - /* not more data (but may still read lines so not an error) */ - if (tr->offset >= tr->max_offset) { - return; - } - - /* request more data */ - if (tr->pos >= tr->filled) { - tr->pos = 0; - tr->filled = 0; - } - - /* partially filled, move buffer */ - if (tr->pos > 0) { - int move_size = tr->filled - tr->pos; - - memmove(tr->buf, &tr->buf[tr->pos], move_size); /* memmove = may overlap */ - tr->filled -= tr->pos; /* now less filled */ - tr->pos = 0; - } - - /* has enough data */ - if (tr->filled >= tr->buf_size) { - return; - } - - /* read buf up to max */ - { - int bytes; - int read_size = tr->buf_size - tr->filled; - if (read_size + tr->offset > tr->max_offset) - read_size = tr->max_offset - tr->offset; - - if (read_size <= 0) { /* ??? */ - bytes = 0; - } - else { - if (tr->filled + read_size >= tr->buf_size) - read_size -= 1; /* always leave an extra byte for c-string null */ - - bytes = read_streamfile(tr->buf + tr->filled, tr->offset, read_size, tr->sf); - tr->offset += bytes; - tr->filled += bytes; - } - - /* maybe some internal issue, force EOF */ - if (bytes == 0) { - tr->offset = tr->max_offset; - } - - /* ensure no old data is used as valid (simplifies some checks during parse) */ - tr->buf[tr->filled] = '\0'; - } -} - -static void parse_buf(text_reader_t* tr) { - int i; - - tr->line = (char*)&tr->buf[tr->pos]; - tr->line_len = 0; - tr->line_ok = 0; - - /* detect EOF (this should only happen if no more data was loaded) */ - if (tr->pos == tr->filled) { - tr->line = NULL; - tr->line_ok = 1; - tr->line_len = 0; - return; - } - - /* assumes filled doesn't reach buf_size (to allow trailing \0 after filled) */ - for (i = tr->pos; i < tr->filled; i++) { - char c = (char)tr->buf[i]; - - if (c == '\0') { - i++; - break; /* not a valid file? (line_ok=0) */ - } - - if (c == '\r' && tr->buf[i+1] == '\n') { /* CRLF (0x0d0a) */ - /* i+1 may read past filled but it's pre-set to \0 */ - i += 2; //todo check that i < buf_size-1 - tr->line_ok = 1; - break; - } - else if (c == '\n') { /* LF (0x0a) */ - i++; - tr->line_ok = 1; - break; - } - else if (c == '\r') { /* CR (0x0d) */ - i++; - tr->line_ok = (i < tr->buf_size - 1); - /* if buf ends with a CR, next buf may start be a LF (single CRLF), so line is not ok near buf end - * (old Macs use single \r as lines, but using only that and reaching buf end should happen rarely) */ - break; - } - - tr->line_len++; - } - - /* when lines are small may read up to filled smaller than buf, with no more data */ - if (!tr->line_ok && i == tr->filled) - tr->line_ok = (tr->filled < tr->buf_size - 1); - - /* added after proper line (a \n) or after buf end, so we aren't changing valid data */ - tr->buf[tr->pos + tr->line_len] = '\0'; - tr->next_pos = i; -} - -int text_reader_get_line(text_reader_t* tr, char** p_line) { - - if (!tr->buf) /* no init */ - return 0; - - /* how it works: - * - fills buffer up to max or buf_len, from pos 0 - * - counts from 0 to next '\n' or EOF - * - nulls \n or after EOF to make a proper c-string - * - returns from string from pos 0 to len - * - on next call rather than re-reading continues from pos N (after \n) - * - a buf will likely contain multiple lines - * - if read chars reach buf_end (no proper line found): - * - pos = 0: buf isn't big enough, error - * - pos > 0: move data to pos=0, fill rest of buf, fill rest of buf - * - * ex. - * - parse buf: read chunk full [aaaaa\nbbbb] (pos = 0) - * - get line: returns "aaaaa\0" (next_pos points to first 'b') - * - get line: from 'b', but reaches buf end before \n or EOF: must readjust - * - parse buf: move chunk part [bbbb*******] ('b' to beginning, * is garbage) - * - parse buf: read chunk part [bbbbbb\ncc_] (reaches EOF) - * - get line: returns "bbbbbb\0" (pos points to first c) - * - get line: returns "cc\0" - * - get line: returns NULL (reached EOF, no more bytes) - * - (there is an implicit \0 reserved in buf) - * - * ex. - * - start: read chunk [aaaaaaaaaaa] - * - get line: reaches buf end, but didn't reach EOF nor \n: error, can't store line - */ - - prepare_buf(tr); /* may not do anything */ - parse_buf(tr); /* next line */ - - /* if we are reading a partial line there may be more data */ - if (!tr->line_ok && tr->pos > 0) { - prepare_buf(tr); - parse_buf(tr); /* could continue from prev parse but makes logic more complex for little gain */ - } - - /* always output line even if truncated */ - if (p_line) *p_line = tr->line; - return !tr->line_ok ? - -(tr->line_len + 1) : /* -0 also is possible, force -1 */ - tr->line_len; -} +#include +#include "text_reader.h" +#include "log.h" + + +/* convenience function to init the above struct */ +int text_reader_init(text_reader_t* tr, uint8_t* buf, int buf_size, STREAMFILE* sf, uint32_t offset, uint32_t max_offset) { + memset(tr, 0, sizeof(text_reader_t)); + + if (buf_size <= 1 || !buf || !sf) + return 0; + + tr->buf = buf; + tr->buf_size = buf_size; + tr->sf = sf; + tr->offset = offset; + + if (!max_offset) + max_offset = get_streamfile_size(sf); + tr->max_offset = max_offset; + + return 1; +} + + +/* reads more data into buf and adjust values */ +static void prepare_buf(text_reader_t* tr) { + + /* since we may read N lines in the same buffer, move starting pos each call */ + tr->pos = tr->next_pos; + + /* not more data (but may still read lines so not an error) */ + if (tr->offset >= tr->max_offset) { + return; + } + + /* request more data */ + if (tr->pos >= tr->filled) { + tr->pos = 0; + tr->filled = 0; + } + + /* partially filled, move buffer */ + if (tr->pos > 0) { + int move_size = tr->filled - tr->pos; + + memmove(tr->buf, &tr->buf[tr->pos], move_size); /* memmove = may overlap */ + tr->filled -= tr->pos; /* now less filled */ + tr->pos = 0; + } + + /* has enough data */ + if (tr->filled >= tr->buf_size) { + return; + } + + /* read buf up to max */ + { + int bytes; + int read_size = tr->buf_size - tr->filled; + if (read_size + tr->offset > tr->max_offset) + read_size = tr->max_offset - tr->offset; + + if (read_size <= 0) { /* ??? */ + bytes = 0; + } + else { + if (tr->filled + read_size >= tr->buf_size) + read_size -= 1; /* always leave an extra byte for c-string null */ + + bytes = read_streamfile(tr->buf + tr->filled, tr->offset, read_size, tr->sf); + tr->offset += bytes; + tr->filled += bytes; + } + + /* maybe some internal issue, force EOF */ + if (bytes == 0) { + tr->offset = tr->max_offset; + } + + /* ensure no old data is used as valid (simplifies some checks during parse) */ + tr->buf[tr->filled] = '\0'; + } +} + +static void parse_buf(text_reader_t* tr) { + int i; + + tr->line = (char*)&tr->buf[tr->pos]; + tr->line_len = 0; + tr->line_ok = 0; + + /* detect EOF (this should only happen if no more data was loaded) */ + if (tr->pos == tr->filled) { + tr->line = NULL; + tr->line_ok = 1; + tr->line_len = 0; + return; + } + + /* assumes filled doesn't reach buf_size (to allow trailing \0 after filled) */ + for (i = tr->pos; i < tr->filled; i++) { + char c = (char)tr->buf[i]; + + if (c == '\0') { + i++; + break; /* not a valid file? (line_ok=0) */ + } + + if (c == '\r' && tr->buf[i+1] == '\n') { /* CRLF (0x0d0a) */ + /* i+1 may read past filled but it's pre-set to \0 */ + i += 2; //todo check that i < buf_size-1 + tr->line_ok = 1; + break; + } + else if (c == '\n') { /* LF (0x0a) */ + i++; + tr->line_ok = 1; + break; + } + else if (c == '\r') { /* CR (0x0d) */ + i++; + tr->line_ok = (i < tr->buf_size - 1); + /* if buf ends with a CR, next buf may start be a LF (single CRLF), so line is not ok near buf end + * (old Macs use single \r as lines, but using only that and reaching buf end should happen rarely) */ + break; + } + + tr->line_len++; + } + + /* when lines are small may read up to filled smaller than buf, with no more data */ + if (!tr->line_ok && i == tr->filled) + tr->line_ok = (tr->filled < tr->buf_size - 1); + + /* added after proper line (a \n) or after buf end, so we aren't changing valid data */ + tr->buf[tr->pos + tr->line_len] = '\0'; + tr->next_pos = i; +} + +int text_reader_get_line(text_reader_t* tr, char** p_line) { + + if (!tr->buf) /* no init */ + return 0; + + /* how it works: + * - fills buffer up to max or buf_len, from pos 0 + * - counts from 0 to next '\n' or EOF + * - nulls \n or after EOF to make a proper c-string + * - returns from string from pos 0 to len + * - on next call rather than re-reading continues from pos N (after \n) + * - a buf will likely contain multiple lines + * - if read chars reach buf_end (no proper line found): + * - pos = 0: buf isn't big enough, error + * - pos > 0: move data to pos=0, fill rest of buf, fill rest of buf + * + * ex. + * - parse buf: read chunk full [aaaaa\nbbbb] (pos = 0) + * - get line: returns "aaaaa\0" (next_pos points to first 'b') + * - get line: from 'b', but reaches buf end before \n or EOF: must readjust + * - parse buf: move chunk part [bbbb*******] ('b' to beginning, * is garbage) + * - parse buf: read chunk part [bbbbbb\ncc_] (reaches EOF) + * - get line: returns "bbbbbb\0" (pos points to first c) + * - get line: returns "cc\0" + * - get line: returns NULL (reached EOF, no more bytes) + * - (there is an implicit \0 reserved in buf) + * + * ex. + * - start: read chunk [aaaaaaaaaaa] + * - get line: reaches buf end, but didn't reach EOF nor \n: error, can't store line + */ + + prepare_buf(tr); /* may not do anything */ + parse_buf(tr); /* next line */ + + /* if we are reading a partial line there may be more data */ + if (!tr->line_ok && tr->pos > 0) { + prepare_buf(tr); + parse_buf(tr); /* could continue from prev parse but makes logic more complex for little gain */ + } + + /* always output line even if truncated */ + if (p_line) *p_line = tr->line; + return !tr->line_ok ? + -(tr->line_len + 1) : /* -0 also is possible, force -1 */ + tr->line_len; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index 723b50d1..a2bec821 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -97,7 +97,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_ps2_kces, init_vgmstream_hxd, init_vgmstream_vsv, - init_vgmstream_scd_pcm, init_vgmstream_ps2_pcm, init_vgmstream_ps2_rkv, init_vgmstream_ps2_vas, @@ -528,6 +527,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_bigrp, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ + init_vgmstream_scd_pcm, init_vgmstream_agsc, init_vgmstream_rsf, init_vgmstream_ps2_wmus,