diff --git a/src/formats.c b/src/formats.c index 3b14860f..db21765a 100644 --- a/src/formats.c +++ b/src/formats.c @@ -1157,7 +1157,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_WB, "Shooting Love. ~TRIZEAL~ WB header"}, {meta_S14, "Namco .S14 raw header"}, {meta_SSS, "Namco .SSS raw header"}, - {meta_PS2_GCM, "GCM 'MCG' Header"}, + {meta_PS2_GCM, "Namco GCM header"}, {meta_PS2_SMPL, "Homura SMPL header"}, {meta_PS2_MSA, "Success .MSA header"}, {meta_NGC_PDT, "Hudson .PDT header"}, diff --git a/src/meta/acb.c b/src/meta/acb.c index 84a4a115..9375ce68 100644 --- a/src/meta/acb.c +++ b/src/meta/acb.c @@ -15,6 +15,10 @@ VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf) { /* checks */ if (!is_id32be(0x00,sf, "@UTF")) goto fail; + /* mainly for bigger files (utf lib checks smaller) */ + if (read_u32be(0x04,sf) + 0x08 != get_streamfile_size(sf)) + goto fail; + if (!check_extensions(sf, "acb")) goto fail; diff --git a/src/meta/hca.c b/src/meta/hca.c index 61105d5a..86d912c9 100644 --- a/src/meta/hca.c +++ b/src/meta/hca.c @@ -99,18 +99,19 @@ VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) { vgmstream->layout_type = layout_none; vgmstream->codec_data = hca_data; - /* assumed mappings */ + /* Assumed mappings; seems correct vs Atom Viewer, that lists L/R/C/LFE/LS/RS and downmixes HCAs like that. + * USM HCA's seem to be L/R/SL/SR/C/LFE though (probably reordered at USM level, no detection done in Atom Viewer). */ { static const uint32_t hca_mappings[] = { 0, mapping_MONO, mapping_STEREO, mapping_2POINT1, - mapping_QUAD, + mapping_QUAD_side, mapping_5POINT0, - mapping_5POINT1, + mapping_5POINT1_surround, mapping_7POINT0, - mapping_7POINT1, + mapping_7POINT1_surround, }; vgmstream->channel_layout = hca_mappings[vgmstream->channels]; diff --git a/src/meta/opus.c b/src/meta/opus.c index 95e14c37..218e260b 100644 --- a/src/meta/opus.c +++ b/src/meta/opus.c @@ -347,10 +347,12 @@ VGMSTREAM* init_vgmstream_opus_sps_n1(STREAMFILE* sf) { /* checks */ if (read_u32be(0x00, sf) != 0x09000000) /* file type (see other N1 SPS) */ goto fail; + /* .sps: Labyrinth of Refrain: Coven of Dusk (Switch) * .nlsd: Disgaea Refine (Switch), Ys VIII (Switch) - * .at9: void tRrLM(); //Void Terrarium (Switch) */ - if (!check_extensions(sf, "sps,nlsd,at9")) + * .at9: void tRrLM(); //Void Terrarium (Switch) + * .opus: Asatsugutori (Switch) */ + if (!check_extensions(sf, "sps,nlsd,at9,opus,lopus")) goto fail; num_samples = read_32bitLE(0x0C, sf); diff --git a/src/meta/ps2_gcm.c b/src/meta/ps2_gcm.c index ff431834..96025f7c 100644 --- a/src/meta/ps2_gcm.c +++ b/src/meta/ps2_gcm.c @@ -1,125 +1,78 @@ -#include "meta.h" -#include "../util.h" -#include "../coding/coding.h" - -/* .GCM - from select Namco games released in around the PS2 era [Gunvari Collection + Time Crisis (PS2), NamCollection (PS2)] */ -VGMSTREAM* init_vgmstream_ps2_gcm(STREAMFILE* sf) { - VGMSTREAM* vgmstream = NULL; - off_t start_offset; - - uint32_t vagp_l_offset, vagp_r_offset, gcm_samples, data_size, gcm_sample_rate, gcm_channels; - - /* checks */ - /* .gcm: actual extension. */ - if (!check_extensions(sf, "gcm")) - goto fail; - - /* check header */ - if (read_u32be(0x00,sf) != 0x4D434700) /* "MCG" */ - goto fail; - - /* GCM format as Namco outlined it is a hacked VAGp stereo format that can hold 6-channel audio at will. - * so, first step is to deal with whatever quirks the format might have. */ - vagp_l_offset = read_u32le(0x04, sf); /* "left" VAGp header pointer. */ - if (vagp_l_offset != 0x20) - goto fail; - vagp_r_offset = read_u32le(0x08, sf); /* "right" VAGp header pointer. */ - if (vagp_r_offset != 0x50) - goto fail; - start_offset = read_u32le(0x0c, sf); /* raw "stereo VAG" data pointer. */ - if (start_offset != 0x80) - goto fail; - gcm_samples = read_u32le(0x10, sf); /* size of raw "stereo VAG" data, not exactly correct for multi-channel files as we'll see below. */ - - /* second step is to check actual GCM size and decide the "channels" value from there. - * not helping matters is there's nothing in the GCM header that indicates whether or not this GCM is actually multi-channel in any way. - * meaning we have to manually support those Klonoa multi-channel files (KLN3182M.GCM, KLN3191M.GCM, etc). */ - data_size = get_streamfile_size(sf) - start_offset; - if (data_size == (gcm_samples * 3)) { - /* 6-channel GCM file. */ - gcm_channels = 6; - } - else if (data_size == gcm_samples) { - /* stereo GCM file. */ - gcm_channels = 2; - } - else { - /* there is only one known GCM "multi-channel" setup and it's hacky. */ - goto fail; - } - /* what follows are two VAGp headers and raw data, third and final step is to perform sanity checks on them (except the data itself) and go from there. */ - - /* VAGp signature field, always present. */ - uint32_t vagp_l_sig, vagp_r_sig, vagp_sig; - vagp_l_sig = read_u32be(vagp_l_offset, sf); - vagp_r_sig = read_u32be(vagp_r_offset, sf); - if (vagp_l_sig != vagp_r_sig) { - /* check two identical values against each other. */ - goto fail; - } - else { - /* check for "VAGp" value. */ - vagp_sig = vagp_r_sig; - if (vagp_sig != 0x56414770) goto fail; /* "VAGp" */ - } - /* VAGp version field, always 4. */ - uint32_t vagp_l_ver, vagp_r_ver, vagp_ver; - vagp_l_ver = read_u32be(vagp_l_offset + 4, sf); - vagp_r_ver = read_u32be(vagp_r_offset + 4, sf); - if (vagp_l_ver != vagp_r_ver) { - /* check two identical values against each other. */ - goto fail; - } - else { - /* check for "version 4" value. */ - vagp_ver = vagp_r_ver; - if (vagp_ver != 4) goto fail; - } - /* VAGp size field, usually separate per channel and does not cover blank frames at all. */ - uint32_t vagp_l_size, vagp_r_size, vagp_samples; - vagp_l_size = read_u32be(vagp_l_offset + 12, sf); - vagp_r_size = read_u32be(vagp_r_offset + 12, sf); - if (vagp_l_size != vagp_r_size) { - /* check two identical values against each other. */ - goto fail; - } - else { - /* assign one of the two existing "vag size" values to an entirely new "vag size" variable to be used later. */ - vagp_samples = vagp_r_size; - } - /* VAGp sample rate field, usually separate per channel. */ - uint32_t vagp_l_sample_rate, vagp_r_sample_rate; - vagp_l_sample_rate = read_u32be(vagp_l_offset + 16, sf); - vagp_r_sample_rate = read_u32be(vagp_r_offset + 16, sf); - if (vagp_l_sample_rate != vagp_r_sample_rate) { - /* check two identical values against each other. */ - goto fail; - } - else { - /* assign one of the two existing "sample rate" values to an entirely new "sample rate" variable to be used later. */ - gcm_sample_rate = vagp_r_sample_rate; - } - - /* build the VGMSTREAM by allocating it to total number of channels and a single loop flag. - * though loop flag is set to 0. GCM files don't really "loop" by themselves. */ - vgmstream = allocate_vgmstream(gcm_channels,0); - if (!vgmstream) goto fail; - - /* fill in the vital statistics. */ - vgmstream->meta_type = meta_PS2_GCM; - vgmstream->sample_rate = gcm_sample_rate; - vgmstream->num_samples = ps_bytes_to_samples(vagp_samples, 1); - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_u32le(0x14, sf); - - /* open the file for reading. */ - if (!vgmstream_open_stream(vgmstream, sf, start_offset)) - goto fail; - return vgmstream; - - /* clean up anything we may have opened. */ -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../util.h" +#include "../coding/coding.h" + +/* .GCM - from PS2 Namco games [Gunvari Collection + Time Crisis (PS2), NamCollection (PS2)] */ +VGMSTREAM* init_vgmstream_ps2_gcm(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t start_offset, name_offset; + uint32_t vagp_l_offset, vagp_r_offset, track_size, data_size, channel_size; + int channels, sample_rate, interleave; + + + /* checks */ + if (!is_id32be(0x00,sf, "MCG\0")) + goto fail; + + /* .gcm: actual extension */ + if (!check_extensions(sf, "gcm")) + goto fail; + + + /* format is two v4 "VAGp" headers then interleaved data (even for 6ch files) */ + vagp_l_offset = read_u32le(0x04, sf); + if (!is_id32be(vagp_l_offset,sf, "VAGp")) + goto fail; + vagp_r_offset = read_u32le(0x08, sf); + if (!is_id32be(vagp_r_offset,sf, "VAGp")) + goto fail; + + start_offset = read_u32le(0x0c, sf); + track_size = read_u32le(0x10, sf); /* stereo size */ + interleave = read_s32le(0x14, sf); + /* 0x18/1c: null */ + + /* nothing in the header indicates multi-channel files (*M.GCM), check expected sizes of stereo pairs */ + data_size = get_streamfile_size(sf) - start_offset; + if (data_size == (track_size * 3)) { + channels = 6; + } + else if (data_size == track_size) { + channels = 2; + } + else { + goto fail; /* unknown setup (could also check start frames are null) */ + } + + /* get some values from the VAGp */ + channel_size = read_u32be(vagp_l_offset + 0x0c, sf); /* without padding */ + sample_rate = read_s32be(vagp_l_offset + 0x10, sf); + name_offset = vagp_l_offset + 0x20; /* both VAGp use the same name (sometimes with an L/R letter) */ + + if (channel_size != read_u32be(vagp_r_offset + 0x0c, sf)) /* unlikely... */ + goto fail; + if (sample_rate != read_u32be(vagp_r_offset + 0x10, sf)) /* unlikely.. */ + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, 0); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_PS2_GCM; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1); + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + read_string(vgmstream->stream_name,0x10+1, name_offset,sf); + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +}