Merge pull request #459 from bnnm/sch-ima-etc

sch ima etc
This commit is contained in:
bnnm 2019-08-15 23:09:15 +02:00 committed by GitHub
commit e3e8a8269d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 738 additions and 162 deletions

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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"},
};

View File

@ -708,6 +708,10 @@
RelativePath=".\meta\nub_idsp.c"
>
</File>
<File
RelativePath=".\meta\ima.c"
>
</File>
<File
RelativePath=".\meta\imc.c"
>
@ -1395,7 +1399,7 @@
>
</File>
<File
RelativePath=".\meta\psx_gms.c"
RelativePath=".\meta\seb.c"
>
</File>
<File

View File

@ -164,6 +164,7 @@
<ClCompile Include="meta\ezw.c" />
<ClCompile Include="meta\ffmpeg.c" />
<ClCompile Include="meta\g1l.c" />
<ClCompile Include="meta\ima.c" />
<ClCompile Include="meta\imc.c" />
<ClCompile Include="meta\ios_psnd.c" />
<ClCompile Include="meta\ktss.c" />
@ -440,7 +441,7 @@
<ClCompile Include="meta\xa.c" />
<ClCompile Include="meta\fag.c" />
<ClCompile Include="meta\ffdl.c" />
<ClCompile Include="meta\psx_gms.c" />
<ClCompile Include="meta\seb.c" />
<ClCompile Include="meta\ea_swvr.c" />
<ClCompile Include="meta\raw.c" />
<ClCompile Include="meta\redspark.c" />

View File

@ -859,7 +859,7 @@
<ClCompile Include="meta\ffdl.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\psx_gms.c">
<ClCompile Include="meta\seb.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ea_swvr.c">
@ -1447,6 +1447,9 @@
<ClCompile Include="meta\ios_psnd.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ima.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\imc.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

View File

@ -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:

52
src/meta/ima.c Normal file
View File

@ -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;
}

View File

@ -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*/

View File

@ -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

View File

@ -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;i<channel_count;i++) {
vgmstream->ch[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;
}

53
src/meta/seb.c Normal file
View File

@ -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;
}

View File

@ -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:

View File

@ -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<<mp_in)))
continue;
ch_out = 0;
for (mp_out = 0; mp_out < 16; mp_out++) {
/* read output mapping (ex. 2.0) and find channel */
if (!(output_mapping & (1<<mp_out)))
continue;
VGM_LOG("i=%i,j=%i, ch_out=%i, ch_in=%i + %i, %f\n", mp_in,mp_out,ch_out, max, ch_in, matrix[mp_out][mp_in]);
mixing_push_add(vgmstream, ch_out, max + ch_in, matrix[mp_in][mp_out]);
ch_out++;
if (ch_out > 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) {

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;