From 706b71d7fd8228c100f6fb848c2cf945b90ada1e Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 15 Aug 2019 15:12:13 +0200 Subject: [PATCH 1/9] Add Pivotal .sch [The Great Escape, Conflict: Desert Storm] --- src/formats.c | 1 + src/meta/meta.h | 1 + src/meta/psf.c | 475 +++++++++++++++++++++++++++++++++++++++++++----- src/vgmstream.c | 1 + 4 files changed, 432 insertions(+), 46 deletions(-) diff --git a/src/formats.c b/src/formats.c index 81278130..2a017e03 100644 --- a/src/formats.c +++ b/src/formats.c @@ -388,6 +388,7 @@ static const char* extension_list[] = { "sbin", "sc", "scd", + "sch", "sd9", "sdf", "sdt", diff --git a/src/meta/meta.h b/src/meta/meta.h index 73af3cbf..86b34de8 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -856,5 +856,6 @@ VGMSTREAM * init_vgmstream_xavs(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_psf_single(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_sch(STREAMFILE * streamFile); #endif /*_META_H*/ diff --git a/src/meta/psf.c b/src/meta/psf.c index a7f79ef4..940d87ac 100644 --- a/src/meta/psf.c +++ b/src/meta/psf.c @@ -25,32 +25,36 @@ VGMSTREAM * init_vgmstream_psf_single(STREAMFILE *streamFile) { flags = read_8bit(0x03,streamFile); switch(flags) { case 0xC0: /* [The Great Escape (PS2), Conflict: Desert Storm (PS2)] */ + case 0x40: /* [The Great Escape (PS2)] */ case 0xA1: /* [Conflict: Desert Storm 2 (PS2)] */ case 0x21: /* [Conflict: Desert Storm 2 (PS2), Conflict: Global Storm (PS2)] */ //case 0x22: /* [Conflict: Vietman (PS2)] */ //todo weird size value, stereo, only one found - channel_count = 2; - if (flags == 0x21) - channel_count = 1; - interleave = 0x10; codec = coding_PSX; + interleave = 0x10; + + channel_count = 2; + if (flags == 0x21 || flags == 0x40) + channel_count = 1; start_offset = 0x08; break; case 0x80: /* [The Great Escape (PC/Xbox), Conflict: Desert Storm (Xbox/GC), Conflict: Desert Storm 2 (Xbox)] */ case 0x81: /* [Conflict: Desert Storm 2 (Xbox), Conflict: Vietnam (Xbox)] */ case 0x01: /* [Conflict: Global Storm (Xbox)] */ + codec = coding_PSX_pivotal; + interleave = 0x10; + channel_count = 2; if (flags == 0x01) channel_count = 1; - interleave = 0x10; - codec = coding_PSX_pivotal; start_offset = 0x08; break; case 0xD1: /* [Conflict: Desert Storm 2 (GC)] */ - channel_count = 2; - interleave = 0x08; codec = coding_NGC_DSP; + interleave = 0x08; + + channel_count = 2; start_offset = 0x08 + 0x60 * channel_count; break; @@ -65,16 +69,14 @@ VGMSTREAM * init_vgmstream_psf_single(STREAMFILE *streamFile) { /* pitch/cents? */ rate_value = (psf_config >> 20) & 0xFFF; switch(rate_value) { - //case 0xEB5: - //case 0xEB4: - case 0xEB3: sample_rate = 44100; break; - case 0x555: sample_rate = 16000; break; - case 0x355: sample_rate = 11050; break; - case 0x1d5: sample_rate = 6000; break; /* ? */ - case 0x1cc: sample_rate = 5000; break; + case 3763: sample_rate = 44100; break; + case 1365: sample_rate = 16000; break; + case 940: sample_rate = 11050; break; + case 460: sample_rate = 5000; break; default: VGM_LOG("PSF: unknown rate value %x\n", rate_value); - goto fail; + sample_rate = rate_value * 11.72; /* not exact but works well enough */ + break; } data_size = (psf_config & 0xFFFFF) * (interleave * channel_count); /* in blocks */ @@ -119,6 +121,8 @@ VGMSTREAM * init_vgmstream_psf_single(STREAMFILE *streamFile) { goto fail; } + vgmstream->stream_size = data_size; + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; return vgmstream; @@ -144,7 +148,7 @@ VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE *streamFile) { goto fail; if (read_32bitBE(0x00,streamFile) != 0x50534660 && /* "PSF\60" [The Great Escape (PC/Xbox/PS2), Conflict: Desert Storm (Xbox/GC)] */ - read_32bitBE(0x00,streamFile) != 0x50534631) /* "PSF\31" [Conflict: Desert Storm 2 (Xbox/GC/PS2)] */ + read_32bitBE(0x00,streamFile) != 0x50534631) /* "PSF\31" [Conflict: Desert Storm 2 (Xbox/GC/PS2), Conflict: Global Terror (Xbox)] */ goto fail; segment_count = read_32bitLE(0x04, streamFile); @@ -161,10 +165,10 @@ VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE *streamFile) { for (i = 0; i < segment_count; i++) { off_t psf_offset; size_t psf_size; - uint32_t psf_id; /* mini table */ psf_offset = read_32bitLE(offset + 0x00, streamFile); + psf_size = get_streamfile_size(streamFile) - psf_offset; /* not ok but meh */ /* 0x04-0c: 0x02*4 transition segments (possibly to 4 song variations) */ /* use last section transition as loop */ @@ -175,15 +179,7 @@ VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE *streamFile) { } /* multiple segment can point to the same PSF offset (for repeated song sections) */ - //todo reuse repeated VGMSTREAMs to improve memory a bit - - psf_id = read_32bitBE(psf_offset + 0x00, streamFile); - psf_size = read_32bitLE(psf_offset + 0x04, streamFile); - if (psf_id == 0x505346D1) //todo improve - psf_size = (psf_size & 0xFFFFF) * 0x10; - else - psf_size = (psf_size & 0xFFFFF) * 0x20; - //;VGM_LOG("PSF: offset=%lx, size=%x\n", psf_offset, psf_size); + //todo reuse repeated VGMSTREAMs to improve memory and bitrate calcs a bit temp_streamFile = setup_subfile_streamfile(streamFile, psf_offset, psf_size, "psf"); if (!temp_streamFile) goto fail; @@ -200,8 +196,6 @@ VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE *streamFile) { vgmstream = allocate_segmented_vgmstream(data,loop_flag, loop_start, loop_end); if (!vgmstream) goto fail; - vgmstream->stream_size = get_streamfile_size(streamFile); - return vgmstream; fail: if (!vgmstream) free_layout_segmented(data); @@ -210,33 +204,422 @@ fail: return NULL; } -#if 0 -VGMSTREAM * init_vgmstream_sch(STREAMFILE *streamFile) { +/* ***************************************************** */ + +static VGMSTREAM * init_vgmstream_psf_pfsm(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - int loop_flag, channel_count; + int loop_flag, channel_count, sample_rate = 0, rate_value = 0, interleave, big_endian; + size_t data_size; + coding_t codec; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + + /* standard: + * 0x00: -1/number (lang?) + * 0x04: config/size? + * 0x08: channel size? only ok for PSX-pivotal + * 0x0c: sample rate or rate_value + * 0x0e: 0x4=PSX-pivotal or 0xFF=PSX + * 0x0f: name size (0xCC/FF=null) + * 0x10: data + * + * GC is similar with 0x20-align between some fields + */ + + /* checks */ + //if (!check_extensions(streamFile, "psf")) + // goto fail; + if (read_32bitBE(0x00,streamFile) != 0x5046534D && /* "PFSM" */ + read_32bitLE(0x00,streamFile) != 0x5046534D) /* "PFSM" (BE) */ + goto fail; + + big_endian = (read_32bitLE(0x00,streamFile) == 0x5046534D); + if (big_endian) { + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } + else { + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + + loop_flag = 0; + + + if (big_endian && read_32bit(0x50, streamFile) != 0) { /* GC */ + codec = coding_NGC_DSP; + interleave = 0x08; + channel_count = 1; + rate_value = (uint16_t)read_16bit(0x48, streamFile); + + start_offset = 0x60 + 0x60 * channel_count; + } + else if (big_endian) { /* GC */ + codec = coding_PCM16BE; + interleave = 0x02; + channel_count = 1; + rate_value = (uint16_t)read_16bit(0x48, streamFile); + + start_offset = 0x60; + } + else if ((uint8_t)read_8bit(0x16, streamFile) == 0xFF) { /* PS2 */ + codec = coding_PSX; + interleave = 0x10; + rate_value = (uint16_t)read_16bit(0x14, streamFile); + channel_count = 1; + + start_offset = 0x18; + } + else { /* PC/Xbox, some PS2/GC */ + codec = coding_PSX_pivotal; + interleave = 0x10; + sample_rate = (uint16_t)read_16bit(0x14, streamFile); + channel_count = 1; + + start_offset = 0x18; + } + + data_size = get_streamfile_size(streamFile) - start_offset; + + /* pitch/cents? */ + if (sample_rate == 0) { + /* pitch/cents? */ + switch(rate_value) { + case 3763: sample_rate = 44100; break; + case 1365: sample_rate = 16000; break; + case 940: sample_rate = 11050; break; + case 460: sample_rate = 5000; break; + default: + VGM_LOG("PSF: unknown rate value %x\n", rate_value); + sample_rate = rate_value * 11.72; /* not exact but works well enough */ + break; + } + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_PSF; + vgmstream->sample_rate = sample_rate; + + switch(codec) { + case coding_PCM16BE: + vgmstream->coding_type = coding_PCM16BE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16); + break; + + case coding_PSX: + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + break; + + case coding_PSX_pivotal: + vgmstream->coding_type = coding_PSX_pivotal; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_cfg_bytes_to_samples(data_size, 0x10, channel_count); + break; + + case coding_NGC_DSP: + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + /* has standard DSP headers at 0x08 */ + dsp_read_coefs_be(vgmstream,streamFile,0x60+0x1c,0x60); + dsp_read_hist_be (vgmstream,streamFile,0x60+0x40,0x60); + + vgmstream->num_samples = read_32bitBE(0x60, streamFile);//dsp_bytes_to_samples(data_size, channel_count); + break; + + default: + goto fail; + } + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + + +typedef enum { UNKNOWN, IMUS, PFST, PFSM } sch_type; + + + +/* SCH - Pivotal games multi-audio container [The Great Escape, Conflict series] */ +VGMSTREAM * init_vgmstream_sch(STREAMFILE *streamFile) { + VGMSTREAM *vgmstream = NULL; + STREAMFILE *external_streamFile = NULL; + STREAMFILE *temp_streamFile = NULL; + off_t skip = 0, chunk_offset, target_offset = 0, header_offset, subfile_offset = 0; + size_t file_size, chunk_padding, target_size = 0, subfile_size = 0; + int big_endian; + int total_subsongs = 0, target_subsong = streamFile->stream_index; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + sch_type target_type = UNKNOWN; + char stream_name[STREAM_NAME_SIZE] ={0}; /* checks */ if (!check_extensions(streamFile, "sch")) goto fail; - /* chunked format (id+size, GC pads to 0x20 and uses BE/inverted ids): - * - SCH\0: start - * - IMUS: points to a external .psf + segment table (same as in .psf, TGE only?) - * - BANK: volume/etc info? points to something? - * - PFSM: single .psf-like file (larger header) - * - PFST: points to single PSF offset (.psf in TGE, or STREAMS.SWD); may be chained to next PFST? - * - * no other info so total subsongs would be count of usable chunks - * in later games, segmented .psf seems to be removed and PFST is used instead - */ + if (read_32bitBE(0x00,streamFile) == 0x48445253) /* "HDRSND" (found on later games) */ + skip = 0x0E; + if (read_32bitBE(skip + 0x00,streamFile) != 0x53434800 && /* "SCH\0" */ + read_32bitLE(skip + 0x00,streamFile) != 0x53434800) /* "SCH\0" (BE consoles) */ + goto fail; + + + /* chunked format (id+size, GC pads to 0x20 and uses BE/inverted ids): + * no other info so total subsongs would be count of usable chunks + * (offsets are probably in level .dat files) */ + big_endian = (read_32bitLE(skip + 0x00,streamFile) == 0x53434800); + if (big_endian) { + read_32bit = read_32bitBE; + chunk_padding = 0x18; + } + else { + read_32bit = read_32bitLE; + chunk_padding = 0; + } + + file_size = get_streamfile_size(streamFile); + if (read_32bit(skip + 0x04,streamFile) + skip + 0x08 + chunk_padding < file_size) /* sometimes padded */ + goto fail; + + if (target_subsong == 0) target_subsong = 1; + + chunk_offset = skip + 0x08 + chunk_padding; + + /* get all files*/ + while (chunk_offset < file_size) { + uint32_t chunk_id = read_32bitBE(chunk_offset + 0x00,streamFile); + uint32_t chunk_size = read_32bit(chunk_offset + 0x04,streamFile); + sch_type current_type = UNKNOWN; + + switch(chunk_id) { + case 0x494D5553: /* "IMUS" (TGE PC/Xbox only) */ + current_type = IMUS; + break; + + case 0x54534650: + case 0x50465354: /* "PFST" */ + current_type = PFST; + break; + + case 0x4D534650: + case 0x5046534D: /* "PFSM" */ + current_type = PFSM; + break; + + case 0x4B4E4142: + case 0x42414E4B: /* "BANK" */ + /* unknown format (variable size), maybe config for entry numbers */ + break; + case 0x424C4F4B: /* "BLOK" [Conflict: Desert Storm (Xbox)] */ + /* some ids or something? */ + break; + + default: + VGM_LOG("SCH: unknown chunk at %lx\n", chunk_offset); + goto fail; + } + + if (current_type != UNKNOWN) + total_subsongs++; + + if (total_subsongs == target_subsong && target_type == UNKNOWN) { + target_type = current_type; + target_offset = chunk_offset; + target_size = 0x08 + chunk_padding + chunk_size; + } + + chunk_offset += 0x08 + chunk_padding + chunk_size; + } + + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; + if (target_size == 0) goto fail; + + header_offset = target_offset + 0x08 + chunk_padding; + + //;VGM_LOG("SCH: offset=%lx, size=%x\n",target_offset, target_size); + + switch(target_type) { + case IMUS: { /* external segmented track */ + STREAMFILE *psf_streamFile; + uint8_t name_size; + char name[255]; + + /* 0x00: config/size? + * 0x04: name size + * 0x05: segments + * 0x06: ? + * 0x08: relative path to .psf + * 0xNN: segment table (same as .psf) + */ + + name_size = read_8bit(header_offset + 0x04, streamFile); + read_string(name,name_size, header_offset + 0x08, streamFile); + + /* later games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */ + if ((uint8_t)read_8bit(header_offset + 0x07, streamFile) == 0xCC) { + external_streamFile = open_streamfile_by_filename(streamFile, "Stream.swd"); + if (!external_streamFile) goto fail; + + subfile_offset = read_32bit(header_offset + 0x08 + name_size, streamFile); + subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */ + + temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf"); + if (!temp_streamFile) goto fail; + + psf_streamFile = temp_streamFile; + } + else { + external_streamFile = open_streamfile_by_filename(streamFile, name); + if (!external_streamFile) goto fail; + + psf_streamFile = external_streamFile; + } + + vgmstream = init_vgmstream_psf_segmented(psf_streamFile); + if (!vgmstream) { + vgmstream = init_vgmstream_psf_single(psf_streamFile); + if (!vgmstream) goto fail; + } + + snprintf(stream_name,sizeof(stream_name), "%s-%s" , "IMUS", name); + break; + } + + case PFST: { /* external track */ + STREAMFILE *psf_streamFile; + uint8_t name_size; + char name[255]; + + if (chunk_padding == 0 && target_size > 0x08 + 0x0c) { /* TGE PC/Xbox version */ + /* 0x00: -1/0 + * 0x04: config/size? + * 0x08: channel size + * 0x0c: sample rate? (differs vs PSF) + * 0x0e: 4? + * 0x0f: name size + * 0x10: name + */ + + /* later games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */ + if ((read_32bitBE(header_offset + 0x14, streamFile) & 0x0000FFFF) == 0xCCCC) { + name_size = read_8bit(header_offset + 0x13, streamFile); + read_string(name,name_size, header_offset + 0x18, streamFile); + + external_streamFile = open_streamfile_by_filename(streamFile, "Stream.swd"); + if (!external_streamFile) goto fail; + + subfile_offset = read_32bit(header_offset + 0x0c, streamFile); + subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */ + + temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf"); + if (!temp_streamFile) goto fail; + + psf_streamFile = temp_streamFile; + } + else { + name_size = read_8bit(header_offset + 0x0f, streamFile); + read_string(name,name_size, header_offset + 0x10, streamFile); + + external_streamFile = open_streamfile_by_filename(streamFile, name); + if (!external_streamFile) goto fail; + + psf_streamFile = external_streamFile; + } + } + else if (chunk_padding) { + strcpy(name, "STREAM.SWD"); /* fixed */ + + /* 0x00: -1 + * 0x04: config/size? + * 0x08: .swd offset + */ + external_streamFile = open_streamfile_by_filename(streamFile, name); + if (!external_streamFile) goto fail; + + subfile_offset = read_32bit(header_offset + 0x24, streamFile); + subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */ + + temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf"); + if (!temp_streamFile) goto fail; + + psf_streamFile = temp_streamFile; + } + else { /* others */ + strcpy(name, "STREAM.SWD"); /* fixed */ + + /* 0x00: -1 + * 0x04: config/size? + * 0x08: .swd offset + */ + external_streamFile = open_streamfile_by_filename(streamFile, name); + if (!external_streamFile) goto fail; + + subfile_offset = read_32bit(header_offset + 0x08, streamFile); + subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */ + + temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf"); + if (!temp_streamFile) goto fail; + + psf_streamFile = temp_streamFile; + } + + vgmstream = init_vgmstream_psf_segmented(psf_streamFile); + if (!vgmstream) { + vgmstream = init_vgmstream_psf_single(psf_streamFile); + if (!vgmstream) goto fail; + } + + snprintf(stream_name,sizeof(stream_name), "%s-%s" , "PFST", name); + break; + } + + case PFSM: + /* internal sound */ + + temp_streamFile = setup_subfile_streamfile(streamFile, target_offset,target_size, NULL); + if (!temp_streamFile) goto fail; + + vgmstream = init_vgmstream_psf_pfsm(temp_streamFile); + if (!vgmstream) goto fail; + + snprintf(stream_name,sizeof(stream_name), "%s" , "PFSM"); + break; + + default: /* target not found */ + goto fail; + } + + vgmstream->num_streams = total_subsongs; + strcpy(vgmstream->stream_name, stream_name); - return vgmstream; -fail: - if (!vgmstream) free_layout_layered(data); close_streamfile(temp_streamFile); + close_streamfile(external_streamFile); + return vgmstream; + +fail: + close_streamfile(temp_streamFile); + close_streamfile(external_streamFile); close_vgmstream(vgmstream); return NULL; } -#endif diff --git a/src/vgmstream.c b/src/vgmstream.c index 94ac6cba..e521a4ff 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -471,6 +471,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_psf_single, init_vgmstream_psf_segmented, init_vgmstream_dsp_itl, + init_vgmstream_sch, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ From dbfa909a9fc3edd361a0fd2a70beaa66a1499513 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 15 Aug 2019 15:14:32 +0200 Subject: [PATCH 2/9] Add TXTP @downmix macro and improve plugin stereo downmixing --- doc/TXTP.md | 1 + src/meta/txtp.c | 12 +++++ src/mixing.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ src/mixing.h | 1 + src/plugins.c | 14 +++--- 5 files changed, 136 insertions(+), 6 deletions(-) diff --git a/doc/TXTP.md b/doc/TXTP.md index 047bbf8a..1e744e45 100644 --- a/doc/TXTP.md +++ b/doc/TXTP.md @@ -364,6 +364,7 @@ Manually setting values gets old, so TXTP supports a bunch of simple macros. The - `remix N (channels)`: same, but mixes selected channels to N channels properly adjusting volume (for layered bgm) - `crosstrack N`: crossfades between Nch tracks after every loop (loop count is adjusted as needed) - `crosslayer-v/b/e N`: crossfades Nch layers to the main track after every loop (loop count is adjusted as needed) +- `downmix`: downmixes up to 8 channels (7.1, 5.1, etc) to stereo, using standard downmixing formulas. `channels` can be multiple comma-separated channels or N~M ranges and may be ommited were applicable to mean "all channels" (channel order doesn't matter but it's internally fixed). diff --git a/src/meta/txtp.c b/src/meta/txtp.c index fa857616..58423926 100644 --- a/src/meta/txtp.c +++ b/src/meta/txtp.c @@ -27,6 +27,7 @@ typedef enum { MACRO_LAYER, MACRO_CROSSTRACK, MACRO_CROSSLAYER, + MACRO_DOWNMIX, } txtp_mix_t; @@ -498,6 +499,7 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) { case MACRO_LAYER: mixing_macro_layer(vgmstream, mix.max, mix.mask, mix.mode); break; case MACRO_CROSSTRACK: mixing_macro_crosstrack(vgmstream, mix.max); break; case MACRO_CROSSLAYER: mixing_macro_crosslayer(vgmstream, mix.max, mix.mode); break; + case MACRO_DOWNMIX: mixing_macro_downmix(vgmstream, mix.max); break; default: break; @@ -1071,6 +1073,16 @@ static void parse_config(txtp_entry *cfg, char *config) { add_mixing(cfg, &mix, type); } + else if (strcmp(command,"@downmix") == 0) { + txtp_mix_data mix = {0}; + + mix.max = 2; /* stereo only for now */ + //nm = get_int(config, &mix.max); + //config += nm; + //if (nm == 0) continue; + + add_mixing(cfg, &mix, MACRO_DOWNMIX); + } else if (config[nc] == ' ') { //;VGM_LOG("TXTP: comment\n"); break; /* comment, ignore rest */ diff --git a/src/mixing.c b/src/mixing.c index 81ceffbc..d20f0b82 100644 --- a/src/mixing.c +++ b/src/mixing.c @@ -925,6 +925,120 @@ void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode) { mixing_push_killmix(vgmstream, max); } + +typedef enum { + pos_FL = 0, + pos_FR = 1, + pos_FC = 2, + pos_LFE = 3, + pos_BL = 4, + pos_BR = 5, + pos_FLC = 6, + pos_FRC = 7, + pos_BC = 8, + pos_SL = 9, + pos_SR = 10, +} mixing_position_t; + +void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_mapping*/) { + mixing_data *data = vgmstream->mixing_data; + int ch, output_channels, mp_in, mp_out, ch_in, ch_out; + mapping_t input_mapping, output_mapping; + const double vol_max = 1.0; + const double vol_sqrt = 1 / sqrt(2); + const double vol_half = 1 / 2; + double matrix[16][16] = {{0}}; + + + if (!data) + return; + if (max <= 1 || data->output_channels <= max || max >= 8) + return; + + /* assume WAV defaults if not set */ + input_mapping = vgmstream->channel_layout; + if (input_mapping == 0) { + switch(data->output_channels) { + case 1: input_mapping = mapping_MONO; break; + case 2: input_mapping = mapping_STEREO; break; + case 3: input_mapping = mapping_2POINT1; break; + case 4: input_mapping = mapping_QUAD; break; + case 5: input_mapping = mapping_5POINT0; break; + case 6: input_mapping = mapping_5POINT1; break; + case 7: input_mapping = mapping_7POINT0; break; + case 8: input_mapping = mapping_7POINT1; break; + default: return; + } + } + + /* build mapping matrix[input channel][output channel] = volume, + * using standard WAV/AC3 downmix formulas + * - https://www.audiokinetic.com/library/edge/?source=Help&id=downmix_tables + * - https://www.audiokinetic.com/library/edge/?source=Help&id=standard_configurations + */ + switch(max) { + case 1: + output_mapping = mapping_MONO; + matrix[pos_FL][pos_FC] = vol_sqrt; + matrix[pos_FR][pos_FC] = vol_sqrt; + matrix[pos_FC][pos_FC] = vol_max; + matrix[pos_SL][pos_FC] = vol_half; + matrix[pos_SR][pos_FC] = vol_half; + matrix[pos_BL][pos_FC] = vol_half; + matrix[pos_BR][pos_FC] = vol_half; + break; + case 2: + output_mapping = mapping_STEREO; + matrix[pos_FL][pos_FL] = vol_max; + matrix[pos_FR][pos_FR] = vol_max; + matrix[pos_FC][pos_FL] = vol_sqrt; + matrix[pos_FC][pos_FR] = vol_sqrt; + matrix[pos_SL][pos_FL] = vol_sqrt; + matrix[pos_SR][pos_FR] = vol_sqrt; + matrix[pos_BL][pos_FL] = vol_sqrt; + matrix[pos_BR][pos_FR] = vol_sqrt; + break; + default: + /* not sure if +3ch would use FC/LFE, SL/BR and whatnot without passing extra config, so ignore for now */ + return; + } + + /* save and make N fake channels at the beginning for easier calcs */ + output_channels = data->output_channels; + for (ch = 0; ch < max; ch++) { + mixing_push_upmix(vgmstream, 0); + } + + /* downmix */ + ch_in = 0; + for (mp_in = 0; mp_in < 16; mp_in++) { + /* read input mapping (ex. 5.1) and find channel */ + if (!(input_mapping & (1< max) + break; + } + + ch_in++; + if (ch_in >= output_channels) + break; + } + + /* remove unneeded channels */ + mixing_push_killmix(vgmstream, max); +} + /* ******************************************************************* */ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) { diff --git a/src/mixing.h b/src/mixing.h index 98e0b2e1..9bb49b97 100644 --- a/src/mixing.h +++ b/src/mixing.h @@ -36,6 +36,7 @@ void mixing_macro_track(VGMSTREAM* vgmstream, uint32_t mask); void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode); void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max); void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode); +void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_mapping*/); #endif /* _MIXING_H_ */ diff --git a/src/plugins.c b/src/plugins.c index 4baf602f..d36cb5b1 100644 --- a/src/plugins.c +++ b/src/plugins.c @@ -234,12 +234,14 @@ void vgmstream_mixing_autodownmix(VGMSTREAM *vgmstream, int max_channels) { if (max_channels <= 0) return; - /* guess mixing the best we can */ - //todo: could use standard downmixing for known max_channels <> vgmstream->channels combos: - // https://www.audiokinetic.com/library/edge/?source=Help&id=downmix_tables#tbl_mono - // https://www.audiokinetic.com/library/edge/?source=Help&id=standard_configurations - - mixing_macro_layer(vgmstream, max_channels, 0, 'e'); + /* guess mixing the best we can, using standard downmixing if possible + * (without mapping we can't be sure if format is using a standard layout) */ + if (vgmstream->channel_layout && max_channels <= 2) { + mixing_macro_downmix(vgmstream, max_channels); + } + else { + mixing_macro_layer(vgmstream, max_channels, 0, 'e'); + } return; } From e8989f5300b34ee45f9cbe57b80126a10aa25d85 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 15 Aug 2019 15:15:00 +0200 Subject: [PATCH 3/9] Add default HCA mappings --- src/meta/hca.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/meta/hca.c b/src/meta/hca.c index 095f9bc0..42c56ec5 100644 --- a/src/meta/hca.c +++ b/src/meta/hca.c @@ -79,6 +79,23 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) { vgmstream->layout_type = layout_none; vgmstream->codec_data = hca_data; + /* assumed mappings */ + { + static const uint32_t hca_mappings[] = { + 0, + mapping_MONO, + mapping_STEREO, + mapping_2POINT1, + mapping_QUAD, + mapping_5POINT0, + mapping_5POINT1, + mapping_7POINT0, + mapping_7POINT1, + }; + + vgmstream->channel_layout = hca_mappings[vgmstream->channels]; + } + return vgmstream; fail: From 5a80a29c371bb6b801269dd14dea5d3fccefe517 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 15 Aug 2019 15:46:03 +0200 Subject: [PATCH 4/9] Allow mini-TXTP with "commands" to simplify config --- doc/TXTP.md | 6 ++++-- src/meta/txtp.c | 33 +++++++++++++-------------------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/doc/TXTP.md b/doc/TXTP.md index 1e744e45..d57137ac 100644 --- a/doc/TXTP.md +++ b/doc/TXTP.md @@ -520,9 +520,11 @@ The parser is fairly simplistic and lax, and may be erratic with edge cases or b ## MINI-TXTP -To simplify TXTP creation, if the .txtp is empty (0 bytes) its filename is used directly as a command. Note that extension is also included (since vgmstream needs a full filename). +To simplify TXTP creation, if the .txtp doesn't set a name inside its filename is used directly including config. Note that extension must be included (since vgmstream needs a full filename). You can set `commands` inside the .txtp too: - *bgm.sxd2#12.txtp*: plays subsong 12 -- *Ryoshima Coast 1 & 2.aix#c1,2.txtp*: channel mask +- *bgm.sxd2#12.txtp*, , inside has `commands = #@volume 0.5`: plays subsong 12 at half volume +- *bgm.sxd2.txtp*, , inside has `commands = #12 #@volume 0.5`: plays subsong 12 at half volume +- *Ryoshima Coast 1 & 2.aix#C1,2.txtp*: channel downmix - *boss2_3ningumi_ver6.adx#l2#F.txtp*: loop twice then play song end file normally - etc diff --git a/src/meta/txtp.c b/src/meta/txtp.c index 58423926..3c13ca00 100644 --- a/src/meta/txtp.c +++ b/src/meta/txtp.c @@ -1317,29 +1317,12 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) { txtp->is_segmented = 1; - /* empty file: use filename with config (ex. "song.ext#3.txtp") */ - if (get_streamfile_size(streamFile) == 0) { - char filename[PATH_LIMIT] = {0}; - char* ext; - get_streamfile_filename(streamFile, filename,PATH_LIMIT); - - /* remove ".txtp" */ - ext = strrchr(filename,'.'); - if (!ext) goto fail; /* ??? */ - ext[0] = '\0'; - - if (!add_entry(txtp, filename, 0)) - goto fail; - - return txtp; - } - - /* skip BOM if needed */ - if ((uint16_t)read_16bitLE(0x00, streamFile) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamFile) == 0xFEFF) + if (file_size > 0 && + ((uint16_t)read_16bitLE(0x00, streamFile) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamFile) == 0xFEFF)) txt_offset = 0x02; - /* normal file: read and parse lines */ + /* read and parse lines */ while (txt_offset < file_size) { char line[TXTP_LINE_MAX] = {0}; char key[TXTP_LINE_MAX] = {0}, val[TXTP_LINE_MAX] = {0}; /* at least as big as a line to avoid overflows (I hope) */ @@ -1371,6 +1354,16 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) { goto fail; } + /* mini-txth: if no entries are set try with filename, ex. from "song.ext#3.txtp" use "song.ext#3" + * (it's possible to have default "commands" inside the .txtp plus filename+config) */ + if (txtp->entry_count == 0) { + char filename[PATH_LIMIT] = {0}; + + get_streamfile_basename(streamFile, filename, sizeof(filename)); + + add_entry(txtp, filename, 0); + } + return txtp; fail: From de294a5a5ec70c0299a562c335e5710a5f55be53 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 15 Aug 2019 15:48:52 +0200 Subject: [PATCH 5/9] Add .p16 extension [Astal (SAT]] --- src/formats.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/formats.c b/src/formats.c index 2a017e03..c3bb4b24 100644 --- a/src/formats.c +++ b/src/formats.c @@ -321,6 +321,7 @@ static const char* extension_list[] = { "ovb", "p04", //txth/reserved [Psychic Force 2012 (DC)] + "p16", //txth/reserved [Astal (SAT)] "p1d", //txth/reserved [Farming Simulator 18 (3DS)] "p2a", //txth/reserved [Thunderhawk Operation Phoenix (PS2)] "p2bt", From badf3477e3a6c826a4e6409c0d73664aa2ef4c97 Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 15 Aug 2019 16:25:52 +0200 Subject: [PATCH 6/9] Add Blitz Games .ima and improve decoder [Lilo & Stitch (PC)] --- src/coding/ima_decoder.c | 6 ++-- src/formats.c | 2 ++ src/libvgmstream.vcproj | 4 +++ src/libvgmstream.vcxproj | 1 + src/libvgmstream.vcxproj.filters | 3 ++ src/meta/ima.c | 52 ++++++++++++++++++++++++++++++++ src/meta/meta.h | 2 ++ src/vgmstream.c | 1 + src/vgmstream.h | 1 + 9 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/meta/ima.c diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index cd033875..ba82ac8c 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -255,8 +255,8 @@ static void blitz_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset delta = (step >> 1) + delta * step; /* custom */ sample_decoded += delta; - /* somehow the exe tries to clamp hist, but actually doesn't (bug?), - * not sure if pcm buffer would be clamped outside though */ + /* in Zapper somehow the exe tries to clamp hist but actually doesn't (bug? not in Lilo & Stitch), + * seems the pcm buffer must be clamped outside though to fix some scratchiness */ *hist1 = sample_decoded;//clamp16(sample_decoded); *step_index += IMA_IndexTable[sample_nibble]; if (*step_index < 0) *step_index=0; @@ -445,7 +445,7 @@ void decode_blitz_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channels int nibble_shift = (i&1?4:0); //low nibble first blitz_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); - outbuf[sample_count] = (short)(hist1); + outbuf[sample_count] = (short)clamp16(hist1); } stream->adpcm_history1_32 = hist1; diff --git a/src/formats.c b/src/formats.c index c3bb4b24..c082582b 100644 --- a/src/formats.c +++ b/src/formats.c @@ -198,6 +198,7 @@ static const char* extension_list[] = { "ikm", "ild", "ilv", //txth/reserved [Star Wars Episode III (PS2)] + "ima", "imc", "int", "isd", @@ -1192,6 +1193,7 @@ static const meta_info meta_info_list[] = { {meta_XAVS, "Reflections XAVS header"}, {meta_PSF, "Pivotal PSF header"}, {meta_DSP_ITL_i, "Infernal .ITL DSP header"}, + {meta_IMA, "Blitz Games .IMA header"}, }; diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index e04fbe58..92626884 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -708,6 +708,10 @@ RelativePath=".\meta\nub_idsp.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index de48b9dc..898be7b9 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -164,6 +164,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 0eecc019..d04b19c0 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1447,6 +1447,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files diff --git a/src/meta/ima.c b/src/meta/ima.c new file mode 100644 index 00000000..7ef9349d --- /dev/null +++ b/src/meta/ima.c @@ -0,0 +1,52 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* .IMA - Blitz Games early games [Lilo & Stitch: Trouble in Paradise (PC)] */ +VGMSTREAM * init_vgmstream_ima(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + int loop_flag, channel_count, num_samples, sample_rate; + + + /* checks */ + if (!check_extensions(streamFile, "ima")) + goto fail; + + if (read_32bitBE(0x00,streamFile) != 0x02000000) /* version? */ + goto fail; + if (read_32bitBE(0x04,streamFile) != 0) + goto fail; + + num_samples = read_32bitLE(0x08, streamFile); + channel_count = read_32bitLE(0x0c,streamFile); + sample_rate = read_32bitLE(0x10, streamFile); + + loop_flag = 0; + start_offset = 0x14; + + if (channel_count > 1) /* unknown interleave */ + goto fail; + if (num_samples != ima_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count)) + goto fail; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_IMA; + vgmstream->sample_rate = sample_rate; + + vgmstream->coding_type = coding_BLITZ_IMA; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = num_samples; + + if (!vgmstream_open_stream(vgmstream,streamFile,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 86b34de8..9385637d 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -858,4 +858,6 @@ VGMSTREAM * init_vgmstream_psf_single(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_sch(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_ima(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/src/vgmstream.c b/src/vgmstream.c index e521a4ff..a4ef49b5 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -472,6 +472,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_psf_segmented, init_vgmstream_dsp_itl, init_vgmstream_sch, + init_vgmstream_ima, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ diff --git a/src/vgmstream.h b/src/vgmstream.h index 48b889d4..9d184b8d 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -714,6 +714,7 @@ typedef enum { meta_XAVS, meta_PSF, meta_DSP_ITL_i, + meta_IMA, } meta_t; From 3e4f64fda89c75f7b68ee1b101fe7745656ed9df Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 15 Aug 2019 22:07:12 +0200 Subject: [PATCH 7/9] Clean psx_gms --- src/formats.c | 5 +-- src/meta/meta.h | 2 +- src/meta/psx_gms.c | 89 +++++++++++++++++----------------------------- src/vgmstream.c | 2 +- src/vgmstream.h | 2 +- 5 files changed, 39 insertions(+), 61 deletions(-) diff --git a/src/formats.c b/src/formats.c index c082582b..3319d8ae 100644 --- a/src/formats.c +++ b/src/formats.c @@ -394,6 +394,7 @@ static const char* extension_list[] = { "sd9", "sdf", "sdt", + "seb", "seg", "sf0", "sfl", @@ -820,8 +821,8 @@ static const meta_info meta_info_list[] = { {meta_PS2_VAGi, "Sony VAGi header"}, {meta_PS2_VAGp, "Sony VAGp header"}, {meta_PS2_pGAV, "Sony pGAV header"}, - {meta_PSX_GMS, "assumed Grandia GMS file by .gms extension"}, - {meta_STR_WAV, "Blitz Games STR+WAV header"}, + {meta_SEB, "Game Arts .SEB header"}, + {meta_STR_WAV, "Blitz Games .STR+WAV header"}, {meta_PS2_ILD, "ILD header"}, {meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"}, {meta_XBOX_WAVM, "Xbox WAVM raw header"}, diff --git a/src/meta/meta.h b/src/meta/meta.h index 9385637d..b35be541 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -88,7 +88,7 @@ VGMSTREAM * init_vgmstream_raw(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_psx_gms(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_seb(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_ild(STREAMFILE *streamFile); diff --git a/src/meta/psx_gms.c b/src/meta/psx_gms.c index c4834148..657a7352 100644 --- a/src/meta/psx_gms.c +++ b/src/meta/psx_gms.c @@ -1,76 +1,53 @@ #include "meta.h" -#include "../util.h" -/* GMS - PSX GMS format has no recognition ID. - This format was used essentially in Grandia Games but - can be easily used by other header format as the format of the header is very simple - - known extensions : GMS - - 2008-05-19 - Fastelbja : First version ... -*/ - -VGMSTREAM * init_vgmstream_psx_gms(STREAMFILE *streamFile) { +/* .seb - Game Arts games [Grandia (PS1), Grandia II/III/X (PS2)] */ +VGMSTREAM * init_vgmstream_seb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - int loop_flag=0; - int channel_count; off_t start_offset; - int i; + int loop_flag, channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("gms",filename_extension(filename))) goto fail; - /* check loop */ - loop_flag = (read_32bitLE(0x20,streamFile)==0); - - /* Always stereo files */ - channel_count=read_32bitLE(0x00,streamFile); - - /* build the VGMSTREAM */ + /* checks */ + /* .seb: found in Grandia II (PS2) .idx */ + /* .gms: fake? (.stz+idx bigfile without names, except in Grandia II) */ + if (!check_extensions(streamFile, "seb,gms,")) + goto fail; + + channel_count = read_32bitLE(0x00,streamFile); + if (channel_count > 2) goto fail; /* mono or stereo */ + /* 0x08/0c: unknown count, possibly related to looping */ + + start_offset = 0x800; + + if (read_32bitLE(0x10,streamFile) > get_streamfile_size(streamFile) || /* loop start offset */ + read_32bitLE(0x18,streamFile) > get_streamfile_size(streamFile)) /* loop end offset */ + goto fail; + /* in Grandia III sometimes there is a value at 0x24/34 */ + + loop_flag = (read_32bitLE(0x20,streamFile) == 0); + + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; + vgmstream->meta_type = meta_SEB; vgmstream->sample_rate = read_32bitLE(0x04,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x1C,streamFile); - - /* Get loop point values */ - if(vgmstream->loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x1C,streamFile); - } + vgmstream->num_samples = read_32bitLE(0x1c,streamFile); + vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile); + vgmstream->loop_end_sample = read_32bitLE(0x1c,streamFile); + vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x800; - vgmstream->meta_type = meta_PSX_GMS; - - start_offset = 0x800; - - /* open the file for reading by each channel */ - { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - (off_t)(start_offset+vgmstream->interleave_block_size*i); - } - } + vgmstream->interleave_block_size = 0x800; + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/src/vgmstream.c b/src/vgmstream.c index a4ef49b5..b90fe0b6 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -51,7 +51,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_mic, init_vgmstream_ngc_dsp_std_int, init_vgmstream_vag, - init_vgmstream_psx_gms, + init_vgmstream_seb, init_vgmstream_ps2_ild, init_vgmstream_ps2_pnb, init_vgmstream_xbox_wavm, diff --git a/src/vgmstream.h b/src/vgmstream.h index 9d184b8d..33e13cc8 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -349,7 +349,7 @@ typedef enum { meta_PS2_VAGi, /* VAGi Interleaved File */ meta_PS2_VAGp, /* VAGp Mono File */ meta_PS2_pGAV, /* VAGp with Little Endian Header */ - meta_PSX_GMS, /* GMS File (used in PS1 & PS2) [no header_id] */ + meta_SEB, meta_STR_WAV, /* Blitz Games STR+WAV files */ meta_PS2_ILD, /* ILD File */ meta_PS2_PNB, /* PsychoNauts Bgm File */ From 3f45b62117b5940df602c01d57d807456361b70a Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 15 Aug 2019 22:08:48 +0200 Subject: [PATCH 8/9] Rename psx_gms.c to seb.c --- src/libvgmstream.vcproj | 2 +- src/libvgmstream.vcxproj | 2 +- src/libvgmstream.vcxproj.filters | 2 +- src/meta/{psx_gms.c => seb.c} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename src/meta/{psx_gms.c => seb.c} (100%) diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 92626884..ba8d0c65 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1399,7 +1399,7 @@ > - + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index d04b19c0..6423991d 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -859,7 +859,7 @@ meta\Source Files - + meta\Source Files diff --git a/src/meta/psx_gms.c b/src/meta/seb.c similarity index 100% rename from src/meta/psx_gms.c rename to src/meta/seb.c From a3296cd27417af5f0c099914fd1cbfb7d8a1f85d Mon Sep 17 00:00:00 2001 From: bnnm Date: Thu, 15 Aug 2019 22:15:37 +0200 Subject: [PATCH 9/9] Minor tweaks --- src/coding/ogg_vorbis_decoder.c | 3 +++ src/streamfile.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coding/ogg_vorbis_decoder.c b/src/coding/ogg_vorbis_decoder.c index 5ca7f47d..198d14aa 100644 --- a/src/coding/ogg_vorbis_decoder.c +++ b/src/coding/ogg_vorbis_decoder.c @@ -99,6 +99,7 @@ void reset_ogg_vorbis(VGMSTREAM *vgmstream) { ogg_vorbis_codec_data *data = vgmstream->codec_data; if (!data) return; + /* this seek cleans internal buffers */ ov_pcm_seek(&data->ogg_vorbis_file, 0); } @@ -106,6 +107,8 @@ void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) { ogg_vorbis_codec_data *data = vgmstream->codec_data; if (!data) return; + /* this seek crosslaps to avoid possible clicks, so seeking to 0 will + * decode a bit differently than ov_pcm_seek */ ov_pcm_seek_lap(&data->ogg_vorbis_file, num_sample); } diff --git a/src/streamfile.c b/src/streamfile.c index 6ce19f75..bf1228cf 100644 --- a/src/streamfile.c +++ b/src/streamfile.c @@ -890,7 +890,7 @@ size_t read_string(char * buf, size_t maxsize, off_t offset, STREAMFILE *streamF if (buf) buf[pos] = '\0'; return maxsize; } - if (c < 0x20 || c > 0xA5) + if (c < 0x20 || (uint8_t)c > 0xA5) goto fail; }