From 54dd018c969798711a6d1966e19d65962dc1726a Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 9 Oct 2021 12:51:33 +0200 Subject: [PATCH] Add LPCM .w extension, cleanup --- src/formats.c | 6 ++- src/meta/ast_mmv.c | 55 ++++++++++++++++++++ src/meta/ikm.c | 123 +++++++++++++++++++++----------------------- src/meta/meta.h | 10 ++-- src/meta/ps2_ast.c | 69 +++++++++++-------------- src/meta/ps2_lpcm.c | 115 ++++++++++++++++++----------------------- src/vgmstream.c | 9 ++-- src/vgmstream.h | 5 +- 8 files changed, 211 insertions(+), 181 deletions(-) create mode 100644 src/meta/ast_mmv.c diff --git a/src/formats.c b/src/formats.c index ad468ca8..f6c330a8 100644 --- a/src/formats.c +++ b/src/formats.c @@ -567,6 +567,7 @@ static const char* extension_list[] = { "vsv", "vxn", + "w", "waa", "wac", "wad", @@ -1118,7 +1119,8 @@ static const meta_info meta_info_list[] = { {meta_XBOX_HLWAV, "Half-Life 2 .WAV header"}, {meta_MYSPD, "U-Sing .MYSPD header"}, {meta_HIS, "Her Interactive HIS header"}, - {meta_PS2_AST, "KOEI AST header"}, + {meta_AST_MV, "MicroVision AST header"}, + {meta_AST_MMV, "Marvelous AST header"}, {meta_CAPDSP, "Capcom DSP header"}, {meta_DMSG, "Microsoft RIFF DMSG header"}, {meta_PONA_3DO, "Policenauts BGM header"}, @@ -1146,7 +1148,7 @@ static const meta_info meta_info_list[] = { {meta_DSP_XIII, "XIII dsp header"}, {meta_DSP_CABELAS, "Cabelas games .DSP header"}, {meta_PS2_ADM, "Dragon Quest V .ADM raw header"}, - {meta_PS2_LPCM, "LPCM header"}, + {meta_LPCM_SHADE, "Shade LPCM header"}, {meta_PS2_VMS, "VMS Header"}, {meta_XAU, "XPEC XAU header"}, {meta_GH3_BAR, "Guitar Hero III Mobile .bar"}, diff --git a/src/meta/ast_mmv.c b/src/meta/ast_mmv.c new file mode 100644 index 00000000..feed9e69 --- /dev/null +++ b/src/meta/ast_mmv.c @@ -0,0 +1,55 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* AST - from Marvelous(?) games [Katekyou Hitman Reborn! Dream Hyper Battle! (PS2), Binchou-tan: Shiawasegoyomi (PS2)] */ +VGMSTREAM* init_vgmstream_ast_mmv(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t start_offset, data_size; + int loop_flag, channels, sample_rate, interleave; + + /* checks */ + if (!is_id32be(0x00,sf, "AST\0")) + goto fail; + + /* .ast: from executables (usually found in bigfiles) */ + if (!check_extensions(sf,"ast")) + goto fail; + + data_size = read_u32le(0x04, sf); + if (data_size != get_streamfile_size(sf)) + goto fail; + + sample_rate = read_s32le(0x08,sf); + channels = read_32bitLE(0x0C,sf); + interleave = read_u32le(0x10,sf); + /* 0x14: number of blocks */ + /* 0x18: ? (not fully related to size/time) */ + /* 0x1c: f32 time */ + loop_flag = 0; + start_offset = 0x100; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_AST_MMV; + + vgmstream->num_samples = ps_bytes_to_samples(data_size - start_offset, channels); + vgmstream->sample_rate = sample_rate; + vgmstream->interleave_block_size = interleave; + + vgmstream->layout_type = layout_interleave; + vgmstream->coding_type = coding_PSX; + + read_string(vgmstream->stream_name,0x20, 0x20,sf); + + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) + goto fail; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/ikm.c b/src/meta/ikm.c index f73db803..f32246b5 100644 --- a/src/meta/ikm.c +++ b/src/meta/ikm.c @@ -2,74 +2,50 @@ #include "../coding/coding.h" -/* IKM - MiCROViSiON PS2 container [Zwei (PS2)] */ -VGMSTREAM* init_vgmstream_ikm_ps2(STREAMFILE* sf) { - VGMSTREAM* vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ikm_ps2(STREAMFILE* sf) { + VGMSTREAM* v = NULL; off_t start_offset; - int loop_flag, channel_count; + int loop_flag, channels; - - /* checks */ - if (!is_id32be(0x00,sf, "IKM\0")) - goto fail; - if (!check_extensions(sf,"ikm")) - goto fail; - - /* 0x20: type 03? */ if (!is_id32be(0x40,sf, "AST\0")) goto fail; loop_flag = (read_s32le(0x14, sf) > 0); - channel_count = read_s32le(0x50, sf); + channels = read_s32le(0x50, sf); start_offset = 0x800; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; + v = allocate_vgmstream(channels, loop_flag); + if (!v) goto fail; - vgmstream->meta_type = meta_IKM; - vgmstream->sample_rate = read_s32le(0x44, sf); - vgmstream->num_samples = ps_bytes_to_samples(read_s32le(0x4c, sf), channel_count); - vgmstream->loop_start_sample = read_s32le(0x14, sf); - vgmstream->loop_end_sample = read_s32le(0x18, sf); - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; /* @0x40 / channels */ + v->meta_type = meta_IKM; + v->sample_rate = read_s32le(0x44, sf); + v->num_samples = ps_bytes_to_samples(read_s32le(0x4c, sf), channels); + v->loop_start_sample = read_s32le(0x14, sf); + v->loop_end_sample = read_s32le(0x18, sf); + v->coding_type = coding_PSX; + v->layout_type = layout_interleave; + v->interleave_block_size = 0x10; /* @0x40 / channels */ - if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + if (!vgmstream_open_stream(v, sf, start_offset)) goto fail; - return vgmstream; + return v; fail: - close_vgmstream(vgmstream); + close_vgmstream(v); return NULL; } -/* IKM - MiCROViSiON PC container [Chaos Legion (PC), Legend of Galactic Heroes (PC)] */ -VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) { - VGMSTREAM* vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) { + VGMSTREAM* v = NULL; off_t start_offset; - - /* checks */ - if (!is_id32be(0x00,sf, "IKM\0")) - goto fail; - if (!check_extensions(sf,"ikm")) - goto fail; - /* 0x20: type 01? */ - /* find "OggS" start */ - if (is_id32be(0x30,sf, "OggS")) { + if (is_id32be(0x30,sf, "OggS")) start_offset = 0x30; /* Chaos Legion (PC) */ - } - else if (is_id32be(0x800,sf, "OggS")) { + else start_offset = 0x800; /* Legend of Galactic Heroes (PC) */ - } - else { - goto fail; - } - { ogg_vorbis_meta_info_t ovmi = {0}; @@ -81,31 +57,23 @@ VGMSTREAM* init_vgmstream_ikm_pc(STREAMFILE* sf) { ovmi.loop_flag = ovmi.loop_end > 0; ovmi.stream_size = read_s32le(0x24, sf); - vgmstream = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi); + v = init_vgmstream_ogg_vorbis_config(sf, start_offset, &ovmi); + if (!v) goto fail; } - return vgmstream; + return v; fail: - close_vgmstream(vgmstream); + close_vgmstream(v); return NULL; } -/* IKM - MiCROViSiON PSP container [The Legend of Heroes: A Tear of Vermillion (PSP)] */ -VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) { - VGMSTREAM* vgmstream = NULL; +static VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) { + VGMSTREAM* v = NULL; STREAMFILE* temp_sf = NULL; off_t start_offset; size_t data_size; - - /* checks */ - if (!is_id32be(0x00,sf, "IKM\0")) - goto fail; - if (!check_extensions(sf,"ikm")) - goto fail; - - /* 0x20: type 00? */ if (!is_id32be(0x800,sf, "RIFF")) goto fail; @@ -116,16 +84,43 @@ VGMSTREAM* init_vgmstream_ikm_psp(STREAMFILE* sf) { temp_sf = setup_subfile_streamfile(sf, start_offset, data_size, "at3"); if (!temp_sf) goto fail; - vgmstream = init_vgmstream_riff(temp_sf); - if (!vgmstream) goto fail; + v = init_vgmstream_riff(temp_sf); + if (!v) goto fail; - vgmstream->meta_type = meta_IKM; + v->meta_type = meta_IKM; close_streamfile(temp_sf); - return vgmstream; + return v; fail: close_streamfile(temp_sf); - close_vgmstream(vgmstream); + close_vgmstream(v); + return NULL; +} + +/* IKM - MiCROViSiON container */ +VGMSTREAM* init_vgmstream_ikm(STREAMFILE* sf) { + uint32_t type; + + /* checks */ + if (!is_id32be(0x00,sf, "IKM\0")) + goto fail; + + if (!check_extensions(sf,"ikm")) + goto fail; + + type = read_u32le(0x20, sf); + switch(type) { + case 0x00: /* The Legend of Heroes: A Tear of Vermillion (PSP) */ + return init_vgmstream_ikm_psp(sf); + case 0x01: /* Chaos Legion (PC), Legend of Galactic Heroes (PC) */ + return init_vgmstream_ikm_pc(sf); + case 0x03: /* Zwei (PS2) */ + return init_vgmstream_ikm_ps2(sf); + default: + goto fail; + } + +fail: return NULL; } diff --git a/src/meta/meta.h b/src/meta/meta.h index c2e097fa..628079ff 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -222,9 +222,7 @@ VGMSTREAM * init_vgmstream_leg(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_filp(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ikm_ps2(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ikm_pc(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_ikm_psp(STREAMFILE * streamFile); +VGMSTREAM* init_vgmstream_ikm(STREAMFILE* sf); VGMSTREAM * init_vgmstream_sfs(STREAMFILE * streamFile); @@ -444,7 +442,9 @@ VGMSTREAM * init_vgmstream_myspd(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_his(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_ast(STREAMFILE* streamFile); +VGMSTREAM* init_vgmstream_ast_mv(STREAMFILE* sf); + +VGMSTREAM* init_vgmstream_ast_mmv(STREAMFILE* sf); VGMSTREAM * init_vgmstream_dmsg(STREAMFILE* streamFile); @@ -480,7 +480,7 @@ VGMSTREAM * init_vgmstream_ps2_wad(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps2_lpcm(STREAMFILE* streamFile); +VGMSTREAM* init_vgmstream_lpcm_shade(STREAMFILE* sf); VGMSTREAM * init_vgmstream_dsp_bdsp(STREAMFILE* streamFile); diff --git a/src/meta/ps2_ast.c b/src/meta/ps2_ast.c index 3c9ef56b..e038f199 100644 --- a/src/meta/ps2_ast.c +++ b/src/meta/ps2_ast.c @@ -1,57 +1,48 @@ #include "meta.h" #include "../coding/coding.h" -/* AST - from Koei and Marvelous games (same internal dev?) */ -VGMSTREAM * init_vgmstream_ps2_ast(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count, variant_type; +/* AST - from MicroVision lib games [P.T.O. IV (PS2), Naval Ops: Warship Gunner (PS2)] */ +VGMSTREAM* init_vgmstream_ast_mv(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t start_offset, data_size, check; + int loop_flag, channels, interleave, sample_rate; - /* check extension */ - if (!check_extensions(streamFile,"ast")) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x41535400) /* "AST\0" */ + /* checks */ + if (!is_id32be(0x00,sf, "AST\0")) goto fail; - /* determine variant (after 0x10 is garbage/code data in type 1 until 0x800, but consistent in all songs) */ - if (read_32bitBE(0x10,streamFile) == 0x00000000 || read_32bitBE(0x10,streamFile) == 0x20002000) { - variant_type = 1; /* Koei: P.T.O. IV (0x00000000), Naval Ops: Warship Gunner (0x20002000) */ - channel_count = 2; - } - else { - variant_type = 2; /* Marvelous: Katekyoo Hitman Reborn! Dream Hyper Battle!, Binchou-tan: Shiawasegoyomi */ - channel_count = read_32bitLE(0x0C,streamFile); - } + if (!check_extensions(sf,"ast")) + goto fail; + + channels = 2; + sample_rate = read_s32le(0x04, sf); + interleave = read_u32le(0x08,sf); + data_size = read_u32le(0x0c,sf); /* may have garbage */ + check = read_u32be(0x10, sf); + /* rest: null/garbage */ loop_flag = 0; + start_offset = 0x800; + + /* there is a variation in .ikm (Zwei), with loops and different start offset */ + if (check != 0x20002000 && /* NO:WG (garbage up to start) */ + check != 0x00000000) /* PTO4 */ + goto fail; + /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - if (variant_type == 1) { - start_offset = 0x800; - vgmstream->sample_rate = read_32bitLE(0x04,streamFile); - vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x0C,streamFile)-start_offset,channel_count); - vgmstream->interleave_block_size = read_32bitLE(0x08,streamFile); - } - else if (variant_type == 2) { - start_offset = 0x100; - vgmstream->sample_rate = read_32bitLE(0x08,streamFile); - vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile)-start_offset,channel_count); - vgmstream->interleave_block_size = read_32bitLE(0x10,streamFile); - } - else { - goto fail; - } + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(data_size - start_offset, channels); + vgmstream->interleave_block_size = interleave; - vgmstream->layout_type = layout_interleave; vgmstream->coding_type = coding_PSX; - vgmstream->meta_type = meta_PS2_AST; + vgmstream->layout_type = layout_interleave; + vgmstream->meta_type = meta_AST_MV; - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; diff --git a/src/meta/ps2_lpcm.c b/src/meta/ps2_lpcm.c index 0037cded..2f2b0913 100644 --- a/src/meta/ps2_lpcm.c +++ b/src/meta/ps2_lpcm.c @@ -1,64 +1,51 @@ -#include "meta.h" -#include "../util.h" - -/* LPCM (from Ah! My Goddess (PS2)) */ -VGMSTREAM * init_vgmstream_ps2_lpcm(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - - int loop_flag; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("lpcm",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0,streamFile) != 0x4C50434D) /* LPCM */ - goto fail; - - loop_flag = read_32bitLE(0x8,streamFile); - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x10; - vgmstream->channels = channel_count; - vgmstream->sample_rate = 48000; - vgmstream->coding_type = coding_PCM16LE; - vgmstream->num_samples = read_32bitLE(0x4,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(0x8,streamFile); - vgmstream->loop_end_sample = read_32bitLE(0xc,streamFile); - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 2; - vgmstream->meta_type = meta_PS2_LPCM; - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - return vgmstream; - -fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" + +/* LPCM - from Shade's 'Shade game library' (ShdLib) [Ah! My Goddess (PS2), Warship Gunner (PS2)] */ +VGMSTREAM* init_vgmstream_lpcm_shade(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + uint32_t loop_flag, channels; + + /* checks */ + if (!is_id32be(0x00, sf, "LPCM")) + goto fail; + + /* .w: real extension + * .lpcm: fake (header id) */ + if (!check_extensions(sf, "w,lpcm")) + goto fail; + + /* extra checks since header is kind of simple */ + if (read_s32le(0x04,sf) * 0x02 * 2 > get_streamfile_size(sf)) /* data size is less than total samples */ + goto fail; + if (read_u32le(0x10,sf) != 0) /* just in case */ + goto fail; + + start_offset = 0x800; /* assumed, closer to num_samples */ + + loop_flag = read_s32le(0x8,sf) != 0; + channels = 2; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_LPCM_SHADE; + vgmstream->sample_rate = 48000; + + vgmstream->num_samples = read_s32le(0x4,sf); + vgmstream->loop_start_sample = read_s32le(0x8,sf); + vgmstream->loop_end_sample = read_s32le(0xc,sf); + + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; + + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; +fail: + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index c42bced8..8c1e71a0 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -92,9 +92,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_musx, init_vgmstream_leg, init_vgmstream_filp, - init_vgmstream_ikm_ps2, - init_vgmstream_ikm_pc, - init_vgmstream_ikm_psp, + init_vgmstream_ikm, init_vgmstream_sfs, init_vgmstream_bg00, init_vgmstream_sat_dvi, @@ -214,7 +212,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_xbox_hlwav, init_vgmstream_myspd, init_vgmstream_his, - init_vgmstream_ps2_ast, + init_vgmstream_ast_mmv, + init_vgmstream_ast_mv, init_vgmstream_dmsg, init_vgmstream_ngc_dsp_aaap, init_vgmstream_ngc_dsp_konami, @@ -237,7 +236,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_dsp_xiii, init_vgmstream_dsp_cabelas, init_vgmstream_ps2_adm, - init_vgmstream_ps2_lpcm, + init_vgmstream_lpcm_shade, init_vgmstream_dsp_bdsp, init_vgmstream_ps2_vms, init_vgmstream_xau, diff --git a/src/vgmstream.h b/src/vgmstream.h index 19684b64..ac6ac141 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -507,7 +507,8 @@ typedef enum { meta_PONA_3DO, /* Policenauts (3DO) */ meta_PONA_PSX, /* Policenauts (PSX) */ meta_XBOX_HLWAV, /* Half Life 2 (XBOX) */ - meta_PS2_AST, /* Some KOEI game (PS2) */ + meta_AST_MV, + meta_AST_MMV, meta_DMSG, /* Nightcaster II - Equinox (XBOX) */ meta_NGC_DSP_AAAP, /* Turok: Evolution (NGC), Vexx (NGC) */ meta_PS2_STER, /* Juuni Kokuki: Kakukaku Taru Ou Michi Beni Midori no Uka */ @@ -530,7 +531,7 @@ typedef enum { meta_DSP_XIII, /* XIII, possibly more (Ubisoft header???) */ meta_DSP_CABELAS, /* Cabelas games */ meta_PS2_ADM, /* Dragon Quest V (PS2) */ - meta_PS2_LPCM, /* Ah! My Goddess */ + meta_LPCM_SHADE, meta_DSP_BDSP, /* Ah! My Goddess */ meta_PS2_VMS, /* Autobahn Raser - Police Madness */ meta_XAU, /* XPEC Entertainment (Beat Down (PS2 Xbox), Spectral Force Chronicle (PS2)) */