From 15bad97db5b9082163de0498c11a21c7e4b00019 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 24 Jun 2021 22:26:49 +0200 Subject: [PATCH 1/7] Renames --- src/libvgmstream.vcproj | 6 +++--- src/libvgmstream.vcxproj | 6 +++--- src/libvgmstream.vcxproj.filters | 6 +++--- src/meta/{ps2_ads.c => ads.c} | 0 src/meta/{ps2_spm.c => spm.c} | 0 src/meta/{ps2_vgs.c => vgs_ps.c} | 0 6 files changed, 9 insertions(+), 9 deletions(-) rename src/meta/{ps2_ads.c => ads.c} (100%) rename src/meta/{ps2_spm.c => spm.c} (100%) rename src/meta/{ps2_vgs.c => vgs_ps.c} (100%) diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index fc302e20..1de32156 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1231,7 +1231,7 @@ > - + @@ -435,7 +435,7 @@ - + @@ -485,7 +485,7 @@ - + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 51db1e09..e32b1700 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -805,7 +805,7 @@ meta\Source Files - + meta\Source Files @@ -955,7 +955,7 @@ meta\Source Files - + meta\Source Files @@ -1588,7 +1588,7 @@ meta\Source Files - + meta\Source Files diff --git a/src/meta/ps2_ads.c b/src/meta/ads.c similarity index 100% rename from src/meta/ps2_ads.c rename to src/meta/ads.c diff --git a/src/meta/ps2_spm.c b/src/meta/spm.c similarity index 100% rename from src/meta/ps2_spm.c rename to src/meta/spm.c diff --git a/src/meta/ps2_vgs.c b/src/meta/vgs_ps.c similarity index 100% rename from src/meta/ps2_vgs.c rename to src/meta/vgs_ps.c From 8c92b600fd6f63966f6413f53a725cecb150ddfd Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 24 Jun 2021 22:32:44 +0200 Subject: [PATCH 2/7] Cleanup --- src/formats.c | 2 +- src/meta/ahv.c | 99 ++++++++++++++++---------------- src/meta/meta.h | 2 +- src/meta/opus.c | 72 ++++++++++++----------- src/meta/spm.c | 119 ++++++++++++++++++-------------------- src/meta/vs_square.c | 132 +++++++++++++++++++++---------------------- src/vgmstream.c | 2 +- src/vgmstream.h | 2 +- 8 files changed, 213 insertions(+), 217 deletions(-) diff --git a/src/formats.c b/src/formats.c index 040c4e0d..946a16a9 100644 --- a/src/formats.c +++ b/src/formats.c @@ -1151,7 +1151,7 @@ static const meta_info meta_info_list[] = { {meta_PS3_PAST, "SNDP header"}, {meta_SGXD, "Sony SGXD header"}, {meta_WII_RAS, "RAS header"}, - {meta_PS2_SPM, "SPM header"}, + {meta_SPM, "Square SPM header"}, {meta_X360_TRA, "Terminal Reality .TRA raw header"}, {meta_PS2_VGS, "Princess Soft VGS header"}, {meta_PS2_IAB, "Runtime .IAB header"}, diff --git a/src/meta/ahv.c b/src/meta/ahv.c index 6de9ad70..73afbbc2 100644 --- a/src/meta/ahv.c +++ b/src/meta/ahv.c @@ -1,48 +1,51 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* AHV - from Amuze games [Headhunter (PS2)] */ -VGMSTREAM * init_vgmstream_ahv(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - size_t data_size, channel_size, interleave; - int loop_flag, channel_count; - - - /* checks (.ahv: from names in bigfile) */ - if ( !check_extensions(streamFile,"ahv") ) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x41485600) /* "AHV\0" */ - goto fail; - - start_offset = 0x800; - data_size = get_streamfile_size(streamFile) - start_offset; - interleave = read_32bitLE(0x10,streamFile); - channel_count = (interleave != 0) ? 2 : 1; - channel_size = read_32bitLE(0x08,streamFile); - loop_flag = 0; - /* VAGp header after 0x14 */ - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_AHV; - vgmstream->sample_rate = read_32bitLE(0x0c,streamFile); - vgmstream->num_samples = ps_bytes_to_samples(channel_size,1); - - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - if (interleave) - vgmstream->interleave_last_block_size = (data_size % (interleave*channel_count)) / channel_count; - - 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" + +/* AHV - from Amuze games [Headhunter (PS2)] */ +VGMSTREAM* init_vgmstream_ahv(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + size_t data_size, channel_size, interleave, sample_rate; + int loop_flag, channels; + + + /* checks */ + /* .ahv: from names in bigfile */ + if (!check_extensions(sf,"ahv")) + goto fail; + if (!is_id32be(0x00,sf, "AHV\0")) + goto fail; + + start_offset = 0x800; + data_size = get_streamfile_size(sf) - start_offset; + + channel_size = read_u32le(0x08,sf); + sample_rate = read_32bitLE(0x0c,sf); + interleave = read_u32le(0x10,sf); + channels = (interleave != 0) ? 2 : 1; + loop_flag = 0; + /* VAGp header after 0x14 */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_AHV; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1); + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + if (interleave) + vgmstream->interleave_last_block_size = (data_size % (interleave*channels)) / channels; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index 8c7077f6..f27a70bb 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -510,7 +510,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_wii_ras(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_spm(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_spm(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_x360_tra(STREAMFILE* streamFile); diff --git a/src/meta/opus.c b/src/meta/opus.c index 7edf7459..e7e28a30 100644 --- a/src/meta/opus.c +++ b/src/meta/opus.c @@ -8,33 +8,37 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t offset, int32_t num_samples, int32_t loop_start, int32_t loop_end) { VGMSTREAM* vgmstream = NULL; off_t start_offset; - int loop_flag = 0, channel_count; - off_t data_offset, samples_offset, multichannel_offset = 0; + int loop_flag = 0, channels, sample_rate; + off_t data_offset, context_offset, multistream_offset = 0; size_t data_size, skip = 0; /* header chunk */ - if (read_u32le(offset + 0x00,sf) != 0x80000001) + if (read_u32le(offset + 0x00,sf) != 0x80000001) /* 'basic info' chunk */ goto fail; - /* 0x04: chunk size */ + /* 0x04: chunk size (should be 0x24) */ - /* 0x08: null */ - channel_count = read_u8(offset + 0x09, sf); - /* 0x0a: packet size if CBR, 0 if VBR */ + /* 0x08: version (0) */ + channels = read_u8(offset + 0x09, sf); + /* 0x0a: frame size if CBR, 0 if VBR */ + sample_rate = read_u32le(offset + 0x0c,sf); data_offset = read_u32le(offset + 0x10, sf); - /* 0x14: null/reserved? */ - samples_offset = read_u32le(offset + 0x18, sf); - skip = read_u16le(offset + 0x1c, sf); - /* 0x1e: ? (seen in Lego Movie 2 (Switch)) */ + /* 0x14: 'frame data offset' (seek table? not seen) */ + context_offset = read_u32le(offset + 0x18, sf); + skip = read_u16le(offset + 0x1c, sf); /* pre-skip sample count */ + /* 0x1e: officially padding (non-zero in Lego Movie 2 (Switch)) */ + /* (no offset to multistream chunk, maybe meant to go after seek/context chunks?) */ - /* samples chunk, rare [Famicom Detective Club (Switch)] */ - if (samples_offset && read_u32le(offset + samples_offset, sf) == 0x80000003) { + /* 0x80000002: 'offset info' chunk (seek table?), not seen */ + + /* 'context info' chunk, rare [Famicom Detective Club (Switch)] */ + if (context_offset && read_u32le(offset + context_offset, sf) == 0x80000003) { /* maybe should give priority to external info? */ - samples_offset += offset; + context_offset += offset; /* 0x08: null*/ - loop_flag = read_u8 (samples_offset + 0x09, sf); - num_samples = read_s32le(samples_offset + 0x0c, sf); /* slightly smaller than manual count */ - loop_start = read_s32le(samples_offset + 0x10, sf); - loop_end = read_s32le(samples_offset + 0x14, sf); + loop_flag = read_u8 (context_offset + 0x09, sf); + num_samples = read_s32le(context_offset + 0x0c, sf); /* slightly smaller than manual count */ + loop_start = read_s32le(context_offset + 0x10, sf); + loop_end = read_s32le(context_offset + 0x14, sf); /* rest (~0x38) reserved/alignment? */ /* values seem to take encoder delay into account */ } @@ -43,13 +47,13 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of } - /* multichannel chunk, rare [Clannad (Switch)] */ + /* 'multistream info' chunk, rare [Clannad (Switch)] */ if (read_u32le(offset + 0x20, sf) == 0x80000005) { - multichannel_offset = offset + 0x20; + multistream_offset = offset + 0x20; } - /* data chunk */ + /* 'data info' chunk */ data_offset += offset; if (read_u32le(data_offset, sf) != 0x80000004) goto fail; @@ -60,13 +64,13 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels,loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_type; - vgmstream->sample_rate = read_u32le(offset + 0x0c,sf); + vgmstream->sample_rate = sample_rate; if (vgmstream->sample_rate == 16000) - vgmstream->sample_rate = 48000; // Grandia HD Collection contains a false sample_rate in header + vgmstream->sample_rate = 48000; // Grandia HD Collection contains a false sample_rate in header vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; @@ -80,12 +84,12 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of cfg.skip = skip; cfg.sample_rate = vgmstream->sample_rate; - if (multichannel_offset && vgmstream->channels <= 8) { + if (multistream_offset && vgmstream->channels <= 8) { int i; - cfg.stream_count = read_u8(multichannel_offset + 0x08,sf); - cfg.coupled_count = read_u8(multichannel_offset + 0x09,sf); + cfg.stream_count = read_u8(multistream_offset + 0x08,sf); + cfg.coupled_count = read_u8(multistream_offset + 0x09,sf); /* stereo streams */ for (i = 0; i < vgmstream->channels; i++) { - cfg.channel_mapping[i] = read_u8(multichannel_offset + 0x0a + i,sf); + cfg.channel_mapping[i] = read_u8(multistream_offset + 0x0a + i,sf); } } @@ -174,14 +178,14 @@ VGMSTREAM* init_vgmstream_opus_capcom(STREAMFILE* sf) { VGMSTREAM *vgmstream = NULL; off_t offset; int num_samples, loop_start, loop_end; - int channel_count; + int channels; /* checks */ if ( !check_extensions(sf,"opus,lopus")) goto fail; - channel_count = read_32bitLE(0x04,sf); - if (channel_count != 1 && channel_count != 2 && channel_count != 6) + channels = read_32bitLE(0x04,sf); + if (channels != 1 && channels != 2 && channels != 6) goto fail; /* unknown stream layout */ num_samples = read_32bitLE(0x00,sf); @@ -196,17 +200,17 @@ VGMSTREAM* init_vgmstream_opus_capcom(STREAMFILE* sf) { /* 0x2c: some size? */ /* 0x30+: extra chunks (0x00: 0x7f, 0x04: num_sample), alt loop starts/regions? */ - if (channel_count == 6) { + if (channels == 6) { /* 2ch multistream hacky-hacks in RE:RE, don't try this at home. We'll end up with: * main vgmstream > N vgmstream layers > substream IO deinterleaver > opus meta > Opus IO transmogrifier (phew) */ layered_layout_data* data = NULL; - int layers = channel_count / 2; + int layers = channels / 2; int i; int loop_flag = (loop_end > 0); /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels,loop_flag); if (!vgmstream) goto fail; vgmstream->layout_type = layout_layered; diff --git a/src/meta/spm.c b/src/meta/spm.c index b184407d..1a9f0612 100644 --- a/src/meta/spm.c +++ b/src/meta/spm.c @@ -1,65 +1,54 @@ -#include "meta.h" -#include "../util.h" - -/* SPM (from Lethal Skies Elite Pilot: Team SW) */ -VGMSTREAM * init_vgmstream_ps2_spm(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - - int loop_flag; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("spm",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x53504D00) /* "SPM" */ - goto fail; - - loop_flag = 1; - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = 48000; - vgmstream->coding_type = coding_PCM16LE; - vgmstream->num_samples = read_32bitLE(0x4,streamFile)/4; - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x8,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0xC,streamFile); - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 2; - vgmstream->meta_type = meta_PS2_SPM; - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* SPM - Seq-PCM stream Square Sounds Co. games [Lethal Skies Elite Pilot: Team SW (PS2)] */ +VGMSTREAM* init_vgmstream_spm(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels; + size_t data_size; + int32_t loop_start, loop_end; + + + /* checks */ + /* .spm: extension from debug strings */ + if (!check_extensions(sf, "spm")) + goto fail; + if (!is_id32be(0x00,sf,"SPM\0")) + goto fail; + + data_size = read_u32le(0x04,sf); + loop_start = read_s32le(0x08,sf); + loop_end = read_s32le(0x0c,sf); + /* 0x10: volume? */ + /* rest: null */ + start_offset = 0x20; + + channels = 2; + loop_flag = 1; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_SPM; + vgmstream->sample_rate = 48000; + + vgmstream->num_samples = pcm16_bytes_to_samples(data_size - start_offset, channels); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/vs_square.c b/src/meta/vs_square.c index 79c42995..468617ea 100644 --- a/src/meta/vs_square.c +++ b/src/meta/vs_square.c @@ -1,66 +1,66 @@ -#include "meta.h" -#include "../layout/layout.h" -#include "../coding/coding.h" - - -/* VS - VagStream from Square games [Final Fantasy X (PS2) voices, Unlimited Saga (PS2) voices, All Star Pro-Wrestling 2/3 (PS2) music] */ -VGMSTREAM * init_vgmstream_vs_square(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - int channel_count, loop_flag, pitch, flags; - off_t start_offset; - - - /* checks */ - /* .vs: header id (probably ok like The Bouncer's .vs, very similar) */ - if (!check_extensions(streamFile, "vs")) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x56530000) /* "VS\0\0" */ - goto fail; - - flags = read_32bitLE(0x04,streamFile); - /* 0x08: block number */ - /* 0x0c: blocks left in the subfile */ - pitch = read_32bitLE(0x10,streamFile); /* usually 0x1000 = 48000 */ - /* 0x14: volume, usually 0x64 = 100 but may be bigger/smaller (up to 128?) */ - /* 0x18: null */ - /* 0x1c: null */ - - /* some Front Mission 4 voices have flag 0x100, no idea */ - if (flags != 0x00 && flags != 0x01) { - VGM_LOG("VS: unknown flags %x\n", flags); - } - - loop_flag = 0; - channel_count = (flags & 1) ? 2 : 1; - start_offset = 0x00; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_VS_SQUARE; - vgmstream->sample_rate = round10((48000 * pitch) / 4096); /* needed for rare files */ - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_blocked_vs_square; - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - - /* calc num_samples */ - { - vgmstream->next_block_offset = start_offset; - do { - block_update(vgmstream->next_block_offset,vgmstream); - vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); - } - while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); - block_update(start_offset, vgmstream); - } - - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + + +/* VS - VagStream from Square Sounds Co. games [Final Fantasy X (PS2) voices, Unlimited Saga (PS2) voices, All Star Pro-Wrestling 2/3 (PS2) music] */ +VGMSTREAM* init_vgmstream_vs_square(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + int channels, loop_flag, pitch, flags; + off_t start_offset; + + + /* checks */ + /* .vs: extension from debug strings (probably like The Bouncer's .vs, very similar) */ + if (!check_extensions(sf, "vs")) + goto fail; + if (!is_id32be(0x00,sf,"VS\0\0")) + goto fail; + + flags = read_u32le(0x04,sf); + /* 0x08: block number */ + /* 0x0c: blocks left in the subfile */ + pitch = read_u32le(0x10,sf); /* usually 0x1000 = 48000 */ + /* 0x14: volume, usually 0x64 = 100, up to 128 [Lethal Skies / Sidewinder F (PS2)] */ + /* 0x18: null */ + /* 0x1c: null */ + + /* some Front Mission 4 voices have flag 0x100, no idea */ + if (flags != 0x00 && flags != 0x01) { + VGM_LOG("VS: unknown flags %x\n", flags); + } + + loop_flag = 0; + channels = (flags & 1) ? 2 : 1; + start_offset = 0x00; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_VS_SQUARE; + vgmstream->sample_rate = round10((48000 * pitch) / 4096); /* needed for rare files */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_blocked_vs_square; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + + /* calc num_samples */ + { + vgmstream->next_block_offset = start_offset; + do { + block_update(vgmstream->next_block_offset,vgmstream); + vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); + } + while (vgmstream->next_block_offset < get_streamfile_size(sf)); + block_update(start_offset, vgmstream); + } + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index 105c6bee..5ff88c29 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -265,7 +265,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_ps3_past, init_vgmstream_sgxd, init_vgmstream_wii_ras, - init_vgmstream_ps2_spm, + init_vgmstream_spm, init_vgmstream_x360_tra, init_vgmstream_ps2_iab, init_vgmstream_vs_str, diff --git a/src/vgmstream.h b/src/vgmstream.h index 36e582c7..e5d97216 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -564,7 +564,7 @@ typedef enum { meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */ meta_SGXD, /* Sony: Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */ meta_WII_RAS, /* Donkey Kong Country Returns (Wii) */ - meta_PS2_SPM, /* Lethal Skies Elite Pilot: Team SW */ + meta_SPM, meta_X360_TRA, /* Def Jam Rapstar */ meta_PS2_VGS, /* Princess Soft PS2 games */ meta_PS2_IAB, /* Ueki no Housoku - Taosu ze Robert Juudan!! (PS2) */ From c4d8853ff6c5890ea09edcd40fdf5e001c6d90f7 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 24 Jun 2021 22:34:41 +0200 Subject: [PATCH 3/7] Fix some .vgs [Ishikura Noboru no Igo Kouza: Chuukyuuhen (PS2)] --- src/formats.c | 2 +- src/meta/meta.h | 2 +- src/meta/vgs_ps.c | 114 +++++++++++++++++++++++++--------------------- src/vgmstream.c | 2 +- src/vgmstream.h | 2 +- 5 files changed, 67 insertions(+), 55 deletions(-) diff --git a/src/formats.c b/src/formats.c index 946a16a9..b6b0e4d5 100644 --- a/src/formats.c +++ b/src/formats.c @@ -1153,7 +1153,7 @@ static const meta_info meta_info_list[] = { {meta_WII_RAS, "RAS header"}, {meta_SPM, "Square SPM header"}, {meta_X360_TRA, "Terminal Reality .TRA raw header"}, - {meta_PS2_VGS, "Princess Soft VGS header"}, + {meta_VGS_PS, "Princess Soft VGS header"}, {meta_PS2_IAB, "Runtime .IAB header"}, {meta_VS_STR, "Square .VS STR* header"}, {meta_LSF_N1NJ4N, ".lsf !n1nj4n header"}, diff --git a/src/meta/meta.h b/src/meta/meta.h index f27a70bb..38066974 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -390,7 +390,7 @@ VGMSTREAM * init_vgmstream_ps2_mcg(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_zsd(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ps2_vgs(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_vgs_ps(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_redspark(STREAMFILE *streamFile); diff --git a/src/meta/vgs_ps.c b/src/meta/vgs_ps.c index 9af59737..221213bc 100644 --- a/src/meta/vgs_ps.c +++ b/src/meta/vgs_ps.c @@ -1,51 +1,63 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* VGS - from Princess Soft games [Gin no Eclipse (PS2), Metal Wolf REV (PS2)] */ -VGMSTREAM * init_vgmstream_ps2_vgs(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - size_t data_size, channel_size, interleave; - int loop_flag, channel_count; - int32_t loop_start = 0, loop_end = 0; - - - /* check */ - if ( !check_extensions(streamFile,"vgs") ) - goto fail; - if (read_32bitBE(0x00,streamFile) != 0x56475300) /* "VGS\0" ('VAG stereo', presumably) */ - goto fail; - - start_offset = 0x30; - data_size = get_streamfile_size(streamFile) - start_offset; - interleave = 0x20000; - channel_count = 2; - channel_size = read_32bitBE(0x0c,streamFile); - loop_flag = 0; /* all files have loop flags but simply fade out normally and repeat */ - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_PS2_VGS; - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - vgmstream->num_samples = ps_bytes_to_samples(channel_size,1); - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_end; - - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave; - if (vgmstream->interleave_block_size) - vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; - read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); /* always, can be null */ - - 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" + +/* VGS - from Princess Soft games [Gin no Eclipse (PS2), Metal Wolf REV (PS2)] */ +VGMSTREAM* init_vgmstream_vgs_ps(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + size_t data_size, channel_size, interleave, sample_rate; + int loop_flag, channels; + int32_t loop_start = 0, loop_end = 0; + + + /* check */ + if (!check_extensions(sf,"vgs")) + goto fail; + if (!is_id32be(0x00,sf, "VGS\0")) /* 'VAG stereo', presumably (simple VAG clone) */ + goto fail; + + start_offset = 0x30; + data_size = get_streamfile_size(sf) - start_offset; + + /* test PS-ADPCM null frame for 2nd channel to detect interleave */ + if (read_u32be(0x20000 + start_offset,sf) == 0) { + interleave = 0x20000; /* common */ + } + else if (read_u32be(0x8000 + start_offset,sf) == 0) { + interleave = 0x8000; /* Ishikura Noboru no Igo Kouza: Chuukyuuhen (PS2) */ + } + else { + goto fail; + } + + channels = 2; + channel_size = read_u32be(0x0c,sf); + sample_rate = read_s32be(0x10,sf); + loop_flag = 0; /* all files have loop flags but simply fade out normally and repeat */ + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_VGS_PS; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size * channels)) / channels; + read_string(vgmstream->stream_name,0x10+1, 0x20,sf); /* always, can be null */ + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index 5ff88c29..54c23e02 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -196,7 +196,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_ads_midway, init_vgmstream_ps2_mcg, init_vgmstream_zsd, - init_vgmstream_ps2_vgs, + init_vgmstream_vgs_ps, init_vgmstream_redspark, init_vgmstream_ivaud, init_vgmstream_wii_wsd, diff --git a/src/vgmstream.h b/src/vgmstream.h index e5d97216..05259b50 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -566,7 +566,7 @@ typedef enum { meta_WII_RAS, /* Donkey Kong Country Returns (Wii) */ meta_SPM, meta_X360_TRA, /* Def Jam Rapstar */ - meta_PS2_VGS, /* Princess Soft PS2 games */ + meta_VGS_PS, meta_PS2_IAB, /* Ueki no Housoku - Taosu ze Robert Juudan!! (PS2) */ meta_VS_STR, /* The Bouncer */ meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */ From ea40d2adda1bc2506d6a7981bc4d02c42d15acf2 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 24 Jun 2021 22:38:28 +0200 Subject: [PATCH 4/7] Fix .wvs glitches [Metal Arms (GC)] --- src/formats.c | 3 +- src/meta/meta.h | 4 +- src/meta/wvs.c | 244 ++++++++++++++++++++++-------------------------- src/vgmstream.c | 4 +- src/vgmstream.h | 3 +- 5 files changed, 116 insertions(+), 142 deletions(-) diff --git a/src/formats.c b/src/formats.c index b6b0e4d5..511d5587 100644 --- a/src/formats.c +++ b/src/formats.c @@ -1019,8 +1019,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_ENTH, ".enth Header"}, {meta_SDT, "High Voltage .sdt header"}, {meta_NGC_TYDSP, ".tydsp Header"}, - {meta_XBOX_WVS, "Metal Arms WVS Header (XBOX)"}, - {meta_NGC_WVS, "Metal Arms WVS Header (GameCube)"}, + {meta_WVS, "Swingin' Ape .WVS header"}, {meta_XBOX_MATX, "assumed Matrix file by .matx extension"}, {meta_DEC, "Falcom DEC RIFF header"}, {meta_VS, "Melbourne House .VS header"}, diff --git a/src/meta/meta.h b/src/meta/meta.h index 38066974..ce0f21c5 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -264,8 +264,8 @@ VGMSTREAM * init_vgmstream_ngc_tydsp(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_capdsp(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_xbox_wvs(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_wvs(STREAMFILE *streamFile); +VGMSTREAM* init_vgmstream_wvs_xbox(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_wvs_ngc(STREAMFILE* sf); VGMSTREAM * init_vgmstream_dc_str(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_dc_str_v2(STREAMFILE *streamFile); diff --git a/src/meta/wvs.c b/src/meta/wvs.c index 354a5595..1f727f35 100644 --- a/src/meta/wvs.c +++ b/src/meta/wvs.c @@ -1,134 +1,110 @@ -#include "meta.h" -#include "../coding/coding.h" - -/* WVS - found in Metal Arms - Glitch in the System (Xbox) */ -VGMSTREAM * init_vgmstream_xbox_wvs(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - size_t data_size; - - - /* check extension */ - if (!check_extensions(streamFile,"wvs")) - goto fail; - - if (read_16bitLE(0x0C,streamFile) != 0x69 && /* codec */ - read_16bitLE(0x08,streamFile) != 0x4400) - goto fail; - - start_offset = 0x20; - data_size = read_32bitLE(0x00,streamFile); - loop_flag = (read_16bitLE(0x0a,streamFile) == 0x472C); /* loop seems to be like this */ - channel_count = read_16bitLE(0x0e,streamFile); /* always stereo files */ - - if (data_size + start_offset != get_streamfile_size(streamFile)) - goto fail; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = vgmstream->num_samples; - - vgmstream->coding_type = coding_XBOX_IMA; - vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_XBOX_WVS; - - - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - - -/* - WVS (found in Metal Arms - Glitch in the System) -*/ -VGMSTREAM * init_vgmstream_ngc_wvs(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - int loop_flag; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("wvs",filename_extension(filename))) goto fail; - - if ((read_32bitBE(0x14,streamFile)*read_32bitBE(0x00,streamFile)+0x60) - != (get_streamfile_size(streamFile))) - { - goto fail; - } - - loop_flag = read_32bitBE(0x10,streamFile); - channel_count = read_32bitBE(0x00,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x60; - - if (channel_count == 1) { - vgmstream->sample_rate = 22050; - } else if (channel_count == 2) { - vgmstream->sample_rate = 44100; - } - - vgmstream->channels = channel_count; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset)/8/channel_count*14; //(read_32bitBE(0x0C,streamFile)-start_offset)/8/channel_count*14; - if (loop_flag) { - vgmstream->loop_start_sample = (read_32bitBE(0x10,streamFile)*2)/8/channel_count*14; - vgmstream->loop_end_sample = (read_32bitBE(0x14,streamFile)*2)/8/channel_count*14; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0x0C,streamFile); - vgmstream->meta_type = meta_NGC_WVS; - - - if (vgmstream->coding_type == coding_NGC_DSP) { - int i,c; - for (c=0;cch[c].adpcm_coef[i] = - read_16bitBE(0x18+c*0x20 +i*2,streamFile); - } - } - } - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* .WVS - found in Metal Arms - Glitch in the System (Xbox) */ +VGMSTREAM* init_vgmstream_wvs_xbox(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels, sample_rate; + size_t data_size; + + + /* checks */ + if (!check_extensions(sf,"wvs")) + goto fail; + + data_size = read_u32le(0x00,sf); + /* 0x04: float seconds (slightly bigger than max num_samples) */ + sample_rate = read_f32le(0x08,sf); + if (read_u16le(0x0c,sf) != 0x0069) /* codec */ + goto fail; + channels = read_s16le(0x0e,sf); + sample_rate = read_s32le(0x10,sf); + /* 0x10: sample rate (int) */ + /* 0x14: bitrate */ + /* 0x18: block size / bps */ + /* 0x1c: size? / block samples */ + + loop_flag = (channels > 1 && sample_rate >= 44100); /* bgm full loops */ + start_offset = 0x20; + + if (data_size + start_offset != get_streamfile_size(sf)) + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_WVS; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, channels); + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->coding_type = coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* .WVS - found in Metal Arms - Glitch in the System (GC) */ +VGMSTREAM* init_vgmstream_wvs_ngc(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels, sample_rate, interleave; + size_t data_size; + + + /* checks */ + if (!check_extensions(sf,"wvs")) + goto fail; + + channels = read_s32be(0x00,sf); + /* 0x04: float seconds (slightly bigger than max num_samples) */ + sample_rate = read_f32be(0x08,sf); + interleave = read_u32be(0x0C,sf); /* even in mono */ + /* 0x10: number of interleave blocks */ + data_size = read_s32be(0x14,sf) * channels; + + loop_flag = (channels > 1 && sample_rate >= 44100); /* bgm full loops */ + start_offset = 0x60; + + if (data_size + start_offset != get_streamfile_size(sf)) + goto fail; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_WVS; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = dsp_bytes_to_samples(data_size, channels); + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + if (interleave) + vgmstream->interleave_last_block_size = (data_size % (interleave * channels)) / channels; + + dsp_read_coefs_be(vgmstream, sf, 0x18, 0x20); + //dsp_read_hist_be(vgmstream, sf, 0x18 + 0x20*channels, 0x04); /* not seen */ + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index 54c23e02..530856d0 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -127,8 +127,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_aix, init_vgmstream_ngc_tydsp, init_vgmstream_capdsp, - init_vgmstream_xbox_wvs, - init_vgmstream_ngc_wvs, + init_vgmstream_wvs_xbox, + init_vgmstream_wvs_ngc, init_vgmstream_dc_str, init_vgmstream_dc_str_v2, init_vgmstream_xbox_matx, diff --git a/src/vgmstream.h b/src/vgmstream.h index 05259b50..a200f0a3 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -461,8 +461,7 @@ typedef enum { meta_NGC_DSP_KONAMI, /* Konami DSP header, found in various games */ meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */ meta_RAW_WAVM, - meta_XBOX_WVS, /* XBOX WVS */ - meta_NGC_WVS, /* Metal Arms - Glitch in the System */ + meta_WVS, meta_XBOX_MATX, /* XBOX MATX */ meta_XMU, meta_XVAS, From 8ceca09f68c51a553f45000fc1e82e1a64cfa0be Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 24 Jun 2021 22:39:04 +0200 Subject: [PATCH 5/7] Add ADX keycodes --- src/meta/adx_keys.h | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/meta/adx_keys.h b/src/meta/adx_keys.h index 829b7a77..dfcca54b 100644 --- a/src/meta/adx_keys.h +++ b/src/meta/adx_keys.h @@ -3,8 +3,8 @@ typedef struct { - uint16_t start,mult,add; /* XOR values derived from the actual key */ - char* key8; /* keystring used by type 8 encryption */ + uint16_t start, mult, add; /* XOR values derived from the actual key */ + const char* key8; /* keystring used by type 8 encryption */ uint64_t key9; /* keycode used by type 9 encryption */ } adxkey_info; @@ -20,7 +20,7 @@ static const adxkey_info adxkey8_list[] = { {0x49e1,0x4a57,0x553d, "karaage",0}, /* Blood+ (PS2) [Grasshopper Manufacture] */ - {0x5f5d,0x58bd,0x55ed, "LOVELOVE",0}, // obfuscated keystring is "KNUDKNUD", adds +1 to chars to get final key + {0x5f5d,0x58bd,0x55ed, "LOVELOVE",0}, // obfuscated keystring is "KNUDKNUD", adds +1 to chars to get final key /* Killer7 (PS2) [Grasshopper Manufacture] */ {0x50fb,0x5803,0x5701, "GHM",0}, @@ -70,11 +70,11 @@ static const adxkey_info adxkey8_list[] = { /* Nogizaka Haruka no Himitsu: Cosplay Hajimemashita (PS2) [Vridge] */ {0x45af,0x5f27,0x52b1, "SKFHSIA",0}, - /* Little Anchor (PS2) [D3 Publisher] */ - {0x5f65,0x5b3d,0x5f65, NULL,0}, // confirmed unique with guessadx + /* Little Anchor (PS2) [Vridge] */ + {0x5f65,0x5b3d,0x5f65, "KHNUJYTG",0}, /* Hanayoi Romanesque: Ai to Kanashimi (PS2) [Marvelous] */ - {0x5563,0x5047,0x43ed, NULL,0}, // 2nd from guessadx, other was {0x5562,0x5047,0x1433} + {0x5563,0x5047,0x43ed, "HANAOTM",0}, /* Mobile Suit Gundam: Gundam vs. Gundam NEXT PLUS (PSP) [Capcom] */ {0x4f7b,0x4fdb,0x5cbf, "CS-GGNX+",0}, @@ -83,10 +83,10 @@ static const adxkey_info adxkey8_list[] = { {0x4f7b,0x5071,0x4c61, "ELEMENGAL",0}, /* Rakushou! Pachi-Slot Sengen 6: Rio 2 Cruising Vanadis (PS2) [Net Corporation] */ - {0x53e9,0x586d,0x4eaf, NULL,0}, // confirmed unique with guessadx + {0x53e9,0x586d,0x4eaf, "waksde",0}, - /* Tears to Tiara Gaiden Avalon no Nazo (PS3) [Aquaplus] */ - {0x47e1,0x60e9,0x51c1, NULL,0}, // confirmed unique with guessadx + /* Tears to Tiara Gaiden: Avalon no Nazo (PS3) [Aquaplus] */ + {0x47e1,0x60e9,0x51c1, "Hello TtT world!",0}, // obfuscated keystring xors 0xF0 to chars to get final key /* Neon Genesis Evangelion: Koutetsu no Girlfriend 2nd (PS2) [Broccoli] */ {0x481d,0x4f25,0x5243, "eva2",0}, @@ -101,25 +101,25 @@ static const adxkey_info adxkey8_list[] = { {0x5f5d,0x552b,0x5507, "DATAM-KK2",0}, /* Sakura Taisen: Atsuki Chishio ni (PS2) [Sega] */ - {0x645d,0x6011,0x5c29, NULL,0}, // possible key: "[Seq][ADX] illegal cri or libsd status." + {0x645d,0x6011,0x5c29, NULL,0}, // keystring may be printf'd "%08X" + number (unlikely key: "[Seq][ADX] illegal cri or libsd status.") /* Sakura Taisen Monogatari: Mysterious Paris (PS2) [Sega] */ {0x62ad,0x4b13,0x5957, "inoue4126",0}, /* Sotsugyou 2nd Generation (PS2) [Jinx] */ - {0x6305,0x509f,0x4c01, NULL,0}, // First guess from guessadx, other was {0x6307,0x509f,0x2ac5} + {0x6305,0x509f,0x4c01, "MUSUMEG",0}, /* Kin'iro no Corda -La Corda d'Oro- (PSP) [Koei] */ - {0x55b7,0x67e5,0x5387, NULL,0}, // keystring not in ELF? + {0x55b7,0x67e5,0x5387, "neo3corda",0}, // keystring as code, char by char /* Nanatsuiro * Drops Pure!! (PS2) [Media Works] */ - {0x6731,0x645d,0x566b, NULL,0}, // confirmed unique with guessadx + {0x6731,0x645d,0x566b, "NANAT",0}, /* Shakugan no Shana (PS2) [Vridge] */ {0x5fc5,0x63d9,0x599f, "FUZETSU",0}, /* Uragiri wa Boku no Namae o Shitteiru (PS2) [Kadokawa Shoten] */ - {0x4c73,0x4d8d,0x5827, NULL,0}, // confirmed unique with guessadx + {0x4c73,0x4d8d,0x5827, "URABOKU-penguin",0}, /* StormLover!! (PSP), StormLover Kai!! (PSP) [Vridge] */ {0x5a11,0x67e5,0x6751, "HEXDPFMDKPQW",0}, /* unknown AHX key */ @@ -261,7 +261,7 @@ static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list static const int adxkey9_list_count = sizeof(adxkey9_list) / sizeof(adxkey9_list[0]); -/* preloaded list used to derive keystrings from ADX_Decoder (see VGAudio for how to calculate) */ +/* preloaded list used to derive keystrings from ADX_Decoder, found in executables (see VGAudio for how to calculate) */ static const uint16_t key8_primes[0x400] = { 0x401B,0x4021,0x4025,0x402B,0x4031,0x403F,0x4043,0x4045,0x405D,0x4061,0x4067,0x406D,0x4087,0x4091,0x40A3,0x40A9, 0x40B1,0x40B7,0x40BD,0x40DB,0x40DF,0x40EB,0x40F7,0x40F9,0x4109,0x410B,0x4111,0x4115,0x4121,0x4133,0x4135,0x413B, @@ -329,13 +329,15 @@ static const uint16_t key8_primes[0x400] = { 0x6779,0x6781,0x6785,0x6791,0x67AB,0x67BD,0x67C1,0x67CD,0x67DF,0x67E5,0x6803,0x6809,0x6811,0x6817,0x682D,0x6839, }; -static void derive_adx_key8(const char * key8, uint16_t * out_start, uint16_t * out_mult, uint16_t * out_add) { +static void derive_adx_key8(const char* key8, uint16_t* p_start, uint16_t* p_mult, uint16_t* p_add) { size_t key_size; uint16_t start = 0, mult = 0, add = 0; int i; - if (key8 == NULL || key8[0] == '\0') + if (key8 == NULL || key8[0] == '\0') /* strlen >= 1 */ goto end; + + /* calcs as found in exes, though there is some unrolling in the original code */ key_size = strlen(key8); start = key8_primes[0x100]; mult = key8_primes[0x200]; @@ -349,9 +351,9 @@ static void derive_adx_key8(const char * key8, uint16_t * out_start, uint16_t * } end: - *out_start = start; - *out_mult = mult; - *out_add = add; + *p_start = start; + *p_mult = mult; + *p_add = add; } From a3732d8df98f6d2243b8cc5bf96271cbf15de3d9 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 24 Jun 2021 22:39:52 +0200 Subject: [PATCH 6/7] Enable .bfstm region subsongs for testing (via .txtp) --- src/meta/bfstm.c | 163 +++++++++++++++++++++++++---------------------- 1 file changed, 87 insertions(+), 76 deletions(-) diff --git a/src/meta/bfstm.c b/src/meta/bfstm.c index c7fb775f..229cb2e8 100644 --- a/src/meta/bfstm.c +++ b/src/meta/bfstm.c @@ -3,22 +3,21 @@ /* Regions seem mostly for in-game purposes and are not very listenable on its own. - * Also, sample start is slightly off since vgmstream can't start in the middle of block ATM. + * Also, sample start is slightly off since vgmstream can't start in the middle of a frame ATM. * Otherwise this kinda works, but for now it's just a test. */ -#define BFSTM_ENABLE_REGION_SUBSONGS 0 -#define BFSTM_ENABLE_REGION_FORCE_LOOPS 0 /* this makes sense in SM3D World, but not in Zelda BotW) */ +#define BFSTM_ENABLE_REGION_SUBSONGS 1 #if BFSTM_ENABLE_REGION_SUBSONGS -static off_t bfstm_set_regions(STREAMFILE *streamFile, VGMSTREAM *vgmstream, int region_count, off_t regn_offset, int codec, int big_endian); +static off_t bfstm_set_regions(STREAMFILE* sf, VGMSTREAM* vgmstream, int region_count, off_t regn_offset, int codec, int big_endian); #endif -/* BFSTM - Nintendo Wii U format */ -VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +/* BFSTM - Nintendo Wii U/Switch format */ +VGMSTREAM* init_vgmstream_bfstm(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; off_t info_offset = 0, data_offset = 0; - int channel_count, loop_flag, codec; + int channels, loop_flag, codec; int big_endian; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; @@ -29,20 +28,20 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { /* checks */ - if ( !check_extensions(streamFile,"bfstm") ) + if (!check_extensions(sf,"bfstm")) goto fail; - - /* FSTM header */ - if (read_32bitBE(0x00, streamFile) != 0x4653544D) /* "FSTM" */ + if (!is_id32be(0x00,sf, "FSTM")) goto fail; - /* 0x06(2): header size (0x40), 0x08: version (0x00000400), 0x0c: file size */ + /* 0x06(2): header size (0x40) + * 0x08: version (0x00000400) + * 0x0c: file size */ /* check BOM */ - if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFEFF) { /* Wii U games */ + if (read_u16be(0x04, sf) == 0xFEFF) { /* Wii U games */ read_32bit = read_32bitBE; read_16bit = read_16bitBE; big_endian = 1; - } else if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFFFE) { /* Switch games */ + } else if (read_u16be(0x04, sf) == 0xFFFE) { /* Switch games */ read_32bit = read_32bitLE; read_16bit = read_16bitLE; big_endian = 0; @@ -53,18 +52,18 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { /* get sections (should always appear in the same order) */ { int i; - int section_count = read_16bit(0x10, streamFile); + int section_count = read_16bit(0x10, sf); for (i = 0; i < section_count; i++) { /* 0x00: id, 0x02(2): padding, 0x04(4): offset, 0x08(4): size */ - uint16_t section_id = read_16bit(0x14 + i*0xc+0x00, streamFile); + uint16_t section_id = read_16bit(0x14 + i*0xc+0x00, sf); switch(section_id) { - case 0x4000: info_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break; - case 0x4001: /* seek_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); */ break; - case 0x4002: data_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break; + case 0x4000: info_offset = read_32bit(0x14+i*0x0c+0x04, sf); break; + case 0x4001: /* seek_offset = read_32bit(0x14+i*0x0c+0x04, sf); */ break; + case 0x4002: data_offset = read_32bit(0x14+i*0x0c+0x04, sf); break; #if BFSTM_ENABLE_REGION_SUBSONGS - case 0x4003: regn_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break; + case 0x4003: regn_offset = read_32bit(0x14+i*0x0c+0x04, sf); break; #endif - case 0x4004: /* pdat_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); */ break; /* prefetch data */ + case 0x4004: /* pdat_offset = read_32bit(0x14+i*0x0c+0x04, sf); */ break; /* prefetch data */ default: break; } @@ -75,13 +74,13 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { } /* INFO section */ - if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */ + if (read_32bitBE(info_offset, sf) != 0x494E464F) /* "INFO" */ goto fail; - codec = read_8bit(info_offset + 0x20, streamFile); - loop_flag = read_8bit(info_offset + 0x21, streamFile); - channel_count = read_8bit(info_offset + 0x22, streamFile); + codec = read_8bit(info_offset + 0x20, sf); + loop_flag = read_8bit(info_offset + 0x21, sf); + channels = read_8bit(info_offset + 0x22, sf); #if BFSTM_ENABLE_REGION_SUBSONGS - region_count = read_8bit(info_offset + 0x23, streamFile); + region_count = read_8bit(info_offset + 0x23, sf); #endif @@ -89,18 +88,18 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bit(info_offset + 0x24, streamFile); - vgmstream->num_samples = read_32bit(info_offset + 0x2c, streamFile); - vgmstream->loop_start_sample = read_32bit(info_offset + 0x28, streamFile); + vgmstream->sample_rate = read_32bit(info_offset + 0x24, sf); + vgmstream->num_samples = read_32bit(info_offset + 0x2c, sf); + vgmstream->loop_start_sample = read_32bit(info_offset + 0x28, sf); vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->meta_type = meta_FSTM; - vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; - vgmstream->interleave_block_size = read_32bit(info_offset + 0x34, streamFile); - vgmstream->interleave_last_block_size = read_32bit(info_offset + 0x44, streamFile); + vgmstream->layout_type = (channels == 1) ? layout_none : layout_interleave; + vgmstream->interleave_block_size = read_32bit(info_offset + 0x34, sf); + vgmstream->interleave_last_block_size = read_32bit(info_offset + 0x44, sf); switch(codec) { case 0x00: @@ -116,13 +115,13 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { int i, c; off_t channel_indexes, channel_info_offset, coefs_offset; - channel_indexes = info_offset+0x08 + read_32bit(info_offset + 0x1C, streamFile); + channel_indexes = info_offset+0x08 + read_32bit(info_offset + 0x1C, sf); for (i = 0; i < vgmstream->channels; i++) { - channel_info_offset = channel_indexes + read_32bit(channel_indexes+0x04+(i*0x08)+0x04, streamFile); - coefs_offset = channel_info_offset + read_32bit(channel_info_offset+0x04, streamFile); + channel_info_offset = channel_indexes + read_32bit(channel_indexes+0x04+(i*0x08)+0x04, sf); + coefs_offset = channel_info_offset + read_32bit(channel_info_offset+0x04, sf); for (c = 0; c < 16; c++) { - vgmstream->ch[i].adpcm_coef[c] = read_16bit(coefs_offset + c*2, streamFile); + vgmstream->ch[i].adpcm_coef[c] = read_16bit(coefs_offset + c*2, sf); } } } @@ -134,10 +133,10 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { #if BFSTM_ENABLE_REGION_SUBSONGS - start_offset += bfstm_set_regions(streamFile, vgmstream, region_count, regn_offset, codec, big_endian); + start_offset += bfstm_set_regions(sf, vgmstream, region_count, regn_offset, codec, big_endian); #endif - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) goto fail; return vgmstream; @@ -151,17 +150,16 @@ fail: /* Newer .bfstm may have multiple regions, that are sample sections of some meaning, * like loop parts (Super Mario 3D World), or dynamic subsongs (Zelda: BotW) * We'll hack them in as subsongs (though seem mostly activated by game events) */ -static off_t bfstm_set_regions(STREAMFILE *streamFile, VGMSTREAM *vgmstream, int region_count, off_t regn_offset, int codec, int big_endian) { +static off_t bfstm_set_regions(STREAMFILE* sf, VGMSTREAM* vgmstream, int region_count, off_t regn_offset, int codec, int big_endian) { off_t start_offset; size_t stream_size; - int total_subsongs, target_subsong = streamFile->stream_index; - int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; - int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitLE : read_16bitLE; - + int total_subsongs, target_subsong = sf->stream_index; + int32_t (*read_s32)(off_t,STREAMFILE*) = big_endian ? read_s32be : read_s32le; + int16_t (*read_s16)(off_t,STREAMFILE*) = big_endian ? read_s16be : read_s16le; if (region_count <= 0 && regn_offset == 0 && codec != 0x02) goto fail; - if (read_32bitBE(regn_offset, streamFile) != 0x5245474E) /* "REGN" */ + if (!is_id32be(regn_offset, sf, "REGN")) goto fail; /* pretend each region is a subsong, but use first subsong as the whole file, @@ -171,61 +169,74 @@ static off_t bfstm_set_regions(STREAMFILE *streamFile, VGMSTREAM *vgmstream, int if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; if (target_subsong > 1) { - int sample_aligned = 0, sample_skip = 0; - int i; - off_t region_start, region_end; - size_t block_size; - size_t sample_start = read_32bit(regn_offset + 0x20 + (target_subsong-2)*0x100+0x00, streamFile); - size_t sample_end = read_32bit(regn_offset + 0x20 + (target_subsong-2)*0x100+0x04, streamFile) + 1; - off_t adpcm_offset = regn_offset + 0x20 + (target_subsong-2)*0x100+0x08; + int ch; + off_t region_start, region_end, block_size; + /* target region info */ + int32_t sample_start = read_s32(regn_offset + 0x20 + (target_subsong-2)*0x100 + 0x00, sf); + int32_t sample_end = read_s32(regn_offset + 0x20 + (target_subsong-2)*0x100 + 0x04, sf) + 1; + off_t adpcm_offset = regn_offset + 0x20 + (target_subsong-2)*0x100 + 0x08; /* rest is padding up to 0x100 */ - /* samples-to-bytes, approximate since samples could land in the middle of a 0x08 frame */ + + /* samples-to-bytes, approximate since regions' samples can land in the middle of a 0x08 frame */ + if (sample_start % 14) { + /* can't decode in the middle of a nibble ATM so align to frame */ + //VGM_LOG("BFSTM: sample align %i, %i\n", sample_start % 14, sample_end % 14); + sample_start -= sample_start % 14; + sample_end -= sample_start % 14; + } region_start = sample_start / 14 * vgmstream->channels * 0x08; region_end = sample_end / 14 * vgmstream->channels * 0x08; stream_size = region_end - region_start; //;VGM_LOG("BFSTM: region offset start=%lx, end=%lx\n", region_start, region_end); - /* align to block start or interleave causes funny sounds, but the bigger the interleave - * the less accurate this is (with 0x2000 align can be off by ~4600 samples per channel) */ - //todo could be fixed with interleave_first_block - block_size = (vgmstream->interleave_block_size*vgmstream->channels); + /* adjust region to closest block + use interleave first to correctly skip to first sample */ + block_size = (vgmstream->interleave_block_size * vgmstream->channels); if (region_start % block_size) { - region_start -= region_start % block_size; /* now aligned */ - //;VGM_LOG("BFSTM: new region start=%lx\n", region_start); + off_t region_skip = (region_start % block_size); + //;VGM_LOG("BFSTM: new region start=%lx - %lx\n", region_start, region_skip); - /* get position of our block (close but smaller than sample_start) */ - sample_aligned = dsp_bytes_to_samples(region_start, vgmstream->channels); - /* and how many samples to skip until actual sample_start */ - sample_skip = (sample_start - sample_aligned); + /* use interleave first to skip to correct offset */ + vgmstream->interleave_first_block_size = (block_size - region_skip) / vgmstream->channels; + vgmstream->interleave_first_skip = (region_skip) / vgmstream->channels; + region_start = region_start - region_skip + vgmstream->interleave_first_skip; /* now aligned */ } - //;VGM_LOG("BFSTM: region align=%i, skip=%i, start=%i, end=%i\n", sample_aligned, sample_skip, sample_start, sample_end); + //;VGM_LOG("BFSTM: region=%lx, adpcm=%lx, start=%i, end=%i\n", region_start, adpcm_offset, sample_start, sample_end); start_offset = region_start; - if (sample_end != vgmstream->num_samples) /* not exact but... */ - vgmstream->interleave_last_block_size = 0; + /* sample_end doesn't fall in last block, interleave last doesn't apply */ + { + int32_t block_samples = dsp_bytes_to_samples(block_size, vgmstream->channels); + int32_t samples_align = vgmstream->num_samples / block_samples * block_samples; + if (sample_end < samples_align) + vgmstream->interleave_last_block_size = 0; + } - vgmstream->num_samples = sample_skip + (sample_end - sample_start); - vgmstream->loop_start_sample = sample_skip; + vgmstream->num_samples = /*sample_skip +*/ (sample_end - sample_start); +#if 0 + /* this makes sense in SM3D World, but not in Zelda BotW */ + vgmstream->loop_start_sample = 0;; vgmstream->loop_end_sample = vgmstream->num_samples; -#if BFSTM_ENABLE_REGION_FORCE_LOOPS vgmstream_force_loop(vgmstream, 1, vgmstream->loop_start_sample, vgmstream->loop_end_sample); #endif /* maybe loops should be disabled with some regions? */ - /* this won't make sense after aligning, whatevs, doesn't sound too bad */ - for (i = 0; i < vgmstream->channels; i++) { - vgmstream->ch[i].adpcm_history1_16 = read_16bit(adpcm_offset+0x02+0x00, streamFile); - vgmstream->ch[i].adpcm_history2_16 = read_16bit(adpcm_offset+0x02+0x02, streamFile); + /* region_start points to correct frame (when compared to ADPCM predictor), but not sure if hist + * is for exact nibble rather than first (sounds ok though) */ + for (ch = 0; ch < vgmstream->channels; ch++) { + /* 0x00: ADPCM predictor */ + vgmstream->ch[ch].adpcm_history1_16 = read_s16(adpcm_offset + 0x02 + 0x06*ch, sf); + vgmstream->ch[ch].adpcm_history2_16 = read_s16(adpcm_offset + 0x04 + 0x06*ch, sf); } } else { start_offset = 0; - stream_size = get_streamfile_size(streamFile); + stream_size = get_streamfile_size(sf); } - vgmstream->num_streams = total_subsongs; + /* for now don't show subsongs since some regions are a bit strange */ + //vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; return start_offset; From 75ab4547e7d8f071a377fb1230c419b7e9e91f47 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 24 Jun 2021 22:39:58 +0200 Subject: [PATCH 7/7] Doc --- README.md | 100 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 1b7be0c1..4d650f7f 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ https://github.com/vgmstream/vgmstream/tree/master/cli/tools/txtp_maker.py Put in the same dir as test.exe/vgmstream_cli, then to drag-and-drop files with subsongs to `txtp_maker.py` (it has CLI options to control output too). -### Renamed files +### Common and unknown extensions A few extensions that vgmstream supports clash with common ones. Since players like foobar or Winamp don't react well to that, they may be renamed to make them playable through vgmstream. @@ -219,7 +219,7 @@ them playable through vgmstream. - `.aif` to `.laif` (standard Mac AIF, Asobo AIF, Ogg) - `.aiff/aifc` to `.laiffl/laifc` (standard Mac AIF) - `.asf` to `.lasf` (EA games, Argonaut ASF) -- `.bin` to `.lbin` (various) +- `.bin` to `.lbin` (various formats) - `.flac` to `.lflac` (standard FLAC) - `.mp2` to `.lmp2` (standard MP2) - `.mp3` to `.lmp3` (standard MP3) @@ -228,34 +228,41 @@ them playable through vgmstream. - `.ogg` to `.logg` (standard OGG) - `.opus` to `.lopus` (standard OPUS or Switch OPUS) - `.stm` to `.lstm` (Rockstar STM) -- `.wav` to `.lwav` (standard WAV) +- `.wav` to `.lwav` (standard WAV, various formats) - `.wma` to `.lwma` (standard WMA) - `.(any)` to `.vgmstream` (FFmpeg formats or TXTH) Command line tools don't have this restriction and will accept the original filename. -The main advantage to rename them is that vgmstream may use the file's -internal loop info, or apply subtle fixes, but is also limited in some ways -(like standard/player's tagging). `.vgmstream` is a catch-all extension that -may work as a last resort to make a file playable. +The main advantage of renaming here is that vgmstream may use the file's internal +loop info, or apply subtle fixes, but is also limited in some ways (like ignoring +standard tags). `.vgmstream` is a catch-all extension that may work as a last resort +to make a file playable. -Some plugins have options that allow any extension (common or unknown) to be -played, making renaming unnecessary. You may need to adjust plugin priority in -player's options first. +Some plugins have options that allow common extensions to be played, making any +renaming unnecessary. You may need to adjust plugin priority in player's options +first. Note that vgmstream also accepts certain extension-less files as-is too. +Similarly, vgmstream has a curated list of known extensions, that plugins may take +into account and ignore unknowns. Through *TXTH* you can make unknown files playable, +but you also need to either rename or set plugin options to allow "unknown extensions" +(or, preferably, report this new extension so it can be added to the known list). + +It's also possible to make a .txtp file that opens files with those common/unknown +extensions as a way to force them into vgmstream without renaming. + +#### Related issues Also be aware that other plugins (not vgmstream's) can tell the player they handle some extension, then not actually play it. This makes the file unplayable as vgmstream doesn't even get the chance to parse that file, so you may need to disable the offending plugin or rename the file (for example this may happen with -.asf and foobar2000). +`.asf` in foobar2000). -When extracting from a bigfile, sometimes internal files don't have an actual -name+extension. Those should be renamed to its proper/common extension, as the -extractor program may guess wrong (like .wav instead of .at3 or .wem). If -there is no known extension, usually the header id string may be used instead. - -Note that vgmstream also accepts certain extension-less files too. +When extracting from a bigfile, sometimes internal files don't have an proper +extension. Those should be renamed to its correct one when possible, as the +extractor program may guess wrong (like `.wav` instead of `.at3` or `.wem`). +If there is no known extension, usually the header id/magic string may be used instead. ### Demuxed videos vgmstream also supports audio from videos, but usually must be demuxed (extracted @@ -282,7 +289,6 @@ Some formats have companion files with external info, that should be left togeth - `.ogg.sfl` : loop info for `.ogg` - `.opus.sli`: loop info for `.opus` - `.pos`: loop info for .wav -- `.vgmstream.pos`: loop info for FFmpeg formats - `.acb`: names for `.awb` - `.xsb`: names for `.xwb` @@ -303,6 +309,7 @@ Similarly some formats split header+body data in separate files, examples: - `.wav.str`+`.wav` - `.wav`+`.dcs` - `.wbh`+`.wbd` + Both are needed to play and must be together. The usual rule is you open the bigger file (body), save a few formats where the smaller (header) file is opened instead for technical reasons (mainly some bank formats). @@ -370,7 +377,7 @@ dynamically during gameplay, or looping metadata is stored externally. Cases like those can be supported using an artificial files with info vgmstream needs. -Creation of these files is meant for advanced users, docs can be found in +Creation of these files is meant for advanced users, full docs can be found in vgmstream source. #### GENH @@ -389,6 +396,17 @@ file, or static values. This allows vgmstream to play unsupported formats. *TXTH* is recommended over *GENH* as it's far easier to create and has many more functions, plus doesn't modify original data. +Usage example (used when opening an unknown file named `bgm_01.pcm`): +**.pcm.txth** +``` +codec = PCM16LE +channels = @0x04 #in the file, at offset 4 +sample_rate = 48000 #hardcoded +start_offset = 0x10 +num_samples = data_size #auto +``` + + #### TXTP Text files with player configuration, named `(name).txtp`. @@ -397,14 +415,38 @@ and non-standard ways, like playing multiple small songs as a single one, or using some channels as a section of the song. For those cases we can create a *TXTP* file to customize how vgmstream handles songs. -Text inside `.txtp` can contain a list of filenames to play as one (ex. -`intro.vag(line)loop.vag`), a list of single-channel files to join as a single -multichannel file, subsong index (ex. `bgm.sxd#10`), per-file configurations like -number of loops, remove unneeded channels, force looping, and many other features. +Text inside `.txtp` can contain a list of filenames to play as one, a list of +single-channel files to join as a single multichannel file, subsong index, +per-file configurations like number of loops, remove unneeded channels, +force looping, and many other features. + +Usage examples (open directly, name can be set freely): +**bgm01-full.txtp** +``` +# plays 2 files as a single one +bgm01_intro.vag +bgm01_loop.vag +loop_mode = auto +``` + +**bgm-subsong10.txtp** +``` +# plays subsong number 10 +bgm.sxd#10 +``` + +**song01-looped.txtp** +``` +# force looping an .mp3 from 10 seconds up to file end +song02.mp3 #I 10.0 +``` + +**music01-demux2.txtp** +``` +# plays channels 3 and 4 only, removes rest +music01.bfstm #C3,4 +``` -For example, to force looping `bgm01.mp3`, make `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. #### TXTM A text file named `.txtm` for some formats with companion files. It lists @@ -414,11 +456,11 @@ It is needed for formats where name combos are hardcoded, so vgmstream doesn't know which companion file(s) to load if its name doesn't match the main file. Note that companion file order is usually important. -Usage example: +Usage example (used when opening files in the left part of the list): ``` # Harry Potter and the Chamber of Secrets (PS2) -entrance.mpf:entrance.mus,entrance_o.mus -willow.mpf:willow.mus,willow_o.mus +entrance.mpf: entrance.mus,entrance_o.mus +willow.mpf: willow.mus,willow_o.mus ``` ``` # Metal Gear Solid: Snake Eater 3D (3DS) names for .awb