diff --git a/doc/TXTP.md b/doc/TXTP.md
index 047bbf8a..d57137ac 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).
@@ -519,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/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/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/formats.c b/src/formats.c
index 81278130..3319d8ae 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",
@@ -321,6 +322,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",
@@ -388,9 +390,11 @@ static const char* extension_list[] = {
"sbin",
"sc",
"scd",
+ "sch",
"sd9",
"sdf",
"sdt",
+ "seb",
"seg",
"sf0",
"sfl",
@@ -817,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"},
@@ -1190,6 +1194,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..ba8d0c65 100644
--- a/src/libvgmstream.vcproj
+++ b/src/libvgmstream.vcproj
@@ -708,6 +708,10 @@
RelativePath=".\meta\nub_idsp.c"
>
+
+
@@ -1395,7 +1399,7 @@
>
+
@@ -440,7 +441,7 @@
-
+
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index 0eecc019..6423991d 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -859,7 +859,7 @@
meta\Source Files
-
+
meta\Source Files
@@ -1447,6 +1447,9 @@
meta\Source Files
+
+ meta\Source Files
+
meta\Source Files
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:
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 73af3cbf..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);
@@ -856,5 +856,8 @@ 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);
+
+VGMSTREAM * init_vgmstream_ima(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/meta/psx_gms.c b/src/meta/psx_gms.c
deleted file mode 100644
index c4834148..00000000
--- a/src/meta/psx_gms.c
+++ /dev/null
@@ -1,76 +0,0 @@
-#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) {
- VGMSTREAM * vgmstream = NULL;
- char filename[PATH_LIMIT];
-
- int loop_flag=0;
- int channel_count;
- off_t start_offset;
- int i;
-
- /* 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 */
- vgmstream = allocate_vgmstream(channel_count,loop_flag);
- if (!vgmstream) goto fail;
-
- /* fill in the vital statistics */
- vgmstream->channels = channel_count;
- 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->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);
- }
- }
-
- return vgmstream;
-
- /* clean up anything we may have opened */
-fail:
- if (vgmstream) close_vgmstream(vgmstream);
- return NULL;
-}
diff --git a/src/meta/seb.c b/src/meta/seb.c
new file mode 100644
index 00000000..657a7352
--- /dev/null
+++ b/src/meta/seb.c
@@ -0,0 +1,53 @@
+#include "meta.h"
+
+
+/* .seb - Game Arts games [Grandia (PS1), Grandia II/III/X (PS2)] */
+VGMSTREAM * init_vgmstream_seb(STREAMFILE *streamFile) {
+ VGMSTREAM * vgmstream = NULL;
+ off_t start_offset;
+ int loop_flag, channel_count;
+
+
+ /* 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;
+
+ vgmstream->meta_type = meta_SEB;
+ vgmstream->sample_rate = read_32bitLE(0x04,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;
+
+ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
+ goto fail;
+ return vgmstream;
+
+fail:
+ close_vgmstream(vgmstream);
+ return NULL;
+}
diff --git a/src/meta/txtp.c b/src/meta/txtp.c
index fa857616..3c13ca00 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 */
@@ -1305,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) */
@@ -1359,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:
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;
}
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;
}
diff --git a/src/vgmstream.c b/src/vgmstream.c
index 94ac6cba..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,
@@ -471,6 +471,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_psf_single,
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..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 */
@@ -714,6 +714,7 @@ typedef enum {
meta_XAVS,
meta_PSF,
meta_DSP_ITL_i,
+ meta_IMA,
} meta_t;