Updated VGMStream to r1702-0-ga76ac04d

CQTexperiment
Christopher Snowhill 2022-01-12 16:53:42 -08:00
parent e3df82cf70
commit f2656bc7b3
23 changed files with 741 additions and 455 deletions

View File

@ -136,6 +136,9 @@
832BF82B21E0514B006F50F1 /* xopus.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81921E0514A006F50F1 /* xopus.c */; };
832BF82C21E0514B006F50F1 /* hca_keys_awb.h in Headers */ = {isa = PBXBuildFile; fileRef = 832BF81A21E0514A006F50F1 /* hca_keys_awb.h */; };
832BF82D21E0514B006F50F1 /* nus3audio.c in Sources */ = {isa = PBXBuildFile; fileRef = 832BF81B21E0514B006F50F1 /* nus3audio.c */; };
832FC36C278FA4CB0056A860 /* ubi_ckd_cwav_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 832FC367278FA4CB0056A860 /* ubi_ckd_cwav_streamfile.h */; };
832FC36D278FA4CB0056A860 /* ubi_ckd_cwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 832FC36B278FA4CB0056A860 /* ubi_ckd_cwav.c */; };
832FC36F278FAE3E0056A860 /* encrypted_mc161_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 832FC36E278FAE3E0056A860 /* encrypted_mc161_streamfile.h */; };
83345A521F8AEB2800B2EAA4 /* xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83345A4E1F8AEB2800B2EAA4 /* xvag.c */; };
83349719275DD2AC00302E21 /* wbk.c in Sources */ = {isa = PBXBuildFile; fileRef = 83349715275DD2AC00302E21 /* wbk.c */; };
833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; };
@ -322,7 +325,6 @@
836F6F9A18BDC2190095E648 /* meta.h in Headers */ = {isa = PBXBuildFile; fileRef = 836F6E5E18BDC2180095E648 /* meta.h */; };
836F6F9B18BDC2190095E648 /* mn_str.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E5F18BDC2180095E648 /* mn_str.c */; };
836F6F9C18BDC2190095E648 /* mp4.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6018BDC2180095E648 /* mp4.c */; };
836F6F9D18BDC2190095E648 /* msvp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6118BDC2180095E648 /* msvp.c */; };
836F6F9E18BDC2190095E648 /* mus_acm.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6218BDC2180095E648 /* mus_acm.c */; };
836F6F9F18BDC2190095E648 /* musc.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6318BDC2180095E648 /* musc.c */; };
836F6FA018BDC2190095E648 /* musx.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6E6418BDC2180095E648 /* musx.c */; };
@ -953,6 +955,9 @@
832BF81921E0514A006F50F1 /* xopus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xopus.c; sourceTree = "<group>"; };
832BF81A21E0514A006F50F1 /* hca_keys_awb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hca_keys_awb.h; sourceTree = "<group>"; };
832BF81B21E0514B006F50F1 /* nus3audio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nus3audio.c; sourceTree = "<group>"; };
832FC367278FA4CB0056A860 /* ubi_ckd_cwav_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ubi_ckd_cwav_streamfile.h; sourceTree = "<group>"; };
832FC36B278FA4CB0056A860 /* ubi_ckd_cwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_ckd_cwav.c; sourceTree = "<group>"; };
832FC36E278FAE3E0056A860 /* encrypted_mc161_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = encrypted_mc161_streamfile.h; sourceTree = "<group>"; };
83345A4E1F8AEB2800B2EAA4 /* xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvag.c; sourceTree = "<group>"; };
83349715275DD2AC00302E21 /* wbk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wbk.c; sourceTree = "<group>"; };
833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = "<group>"; };
@ -1139,7 +1144,6 @@
836F6E5E18BDC2180095E648 /* meta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = meta.h; sourceTree = "<group>"; };
836F6E5F18BDC2180095E648 /* mn_str.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mn_str.c; sourceTree = "<group>"; };
836F6E6018BDC2180095E648 /* mp4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mp4.c; sourceTree = "<group>"; };
836F6E6118BDC2180095E648 /* msvp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = msvp.c; sourceTree = "<group>"; };
836F6E6218BDC2180095E648 /* mus_acm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mus_acm.c; sourceTree = "<group>"; };
836F6E6318BDC2180095E648 /* musc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = musc.c; sourceTree = "<group>"; };
836F6E6418BDC2180095E648 /* musx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = musx.c; sourceTree = "<group>"; };
@ -1904,6 +1908,7 @@
8306B0C42098458D000302D4 /* ea_wve_ad10.c */,
8306B0BF2098458C000302D4 /* ea_wve_au00.c */,
83AF2CC826226BA500538240 /* encrypted_bgm_streamfile.h */,
832FC36E278FAE3E0056A860 /* encrypted_mc161_streamfile.h */,
83031ECE243C50DE00C3F3E0 /* encrypted.c */,
836F6E4918BDC2180095E648 /* exakt_sc.c */,
836F6E4A18BDC2180095E648 /* excitebots.c */,
@ -1990,7 +1995,6 @@
83C7280A22BC893C00678B4A /* msf.c */,
83709E011ECBC1A4005C03D3 /* mss.c */,
834FE0E7215C79EC000A5D3D /* msv.c */,
836F6E6118BDC2180095E648 /* msvp.c */,
83C7280E22BC893D00678B4A /* mta2_streamfile.h */,
83C727FF22BC893900678B4A /* mta2.c */,
83C7280322BC893A00678B4A /* mtaf.c */,
@ -2182,6 +2186,8 @@
8306B0D22098458F000302D4 /* txtp.c */,
8351F32B2212B57000A606E4 /* ubi_bao_streamfile.h */,
8306B0D420984590000302D4 /* ubi_bao.c */,
832FC367278FA4CB0056A860 /* ubi_ckd_cwav_streamfile.h */,
832FC36B278FA4CB0056A860 /* ubi_ckd_cwav.c */,
836F6EFC18BDC2190095E648 /* ubi_ckd.c */,
837CEAE023487F2A00E62A4A /* ubi_hx.c */,
8306B0D720984590000302D4 /* ubi_jade.c */,
@ -2341,6 +2347,7 @@
83AA7F802519C042004C5298 /* sab_streamfile.h in Headers */,
83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */,
83E7FD6325EF2B0C00683FD2 /* tac_decoder_lib_data.h in Headers */,
832FC36C278FA4CB0056A860 /* ubi_ckd_cwav_streamfile.h in Headers */,
83E7FD6125EF2B0C00683FD2 /* tac_decoder_lib.h in Headers */,
834FE0EC215C79ED000A5D3D /* kma9_streamfile.h in Headers */,
83E7FD6225EF2B0C00683FD2 /* tac_decoder_lib_ops.h in Headers */,
@ -2376,6 +2383,7 @@
83C7280F22BC893D00678B4A /* xwb_xsb.h in Headers */,
8315868B26F586F900803A3A /* m2_psb.h in Headers */,
83AA7F732519BFEA004C5298 /* mpeg_bitreader.h in Headers */,
832FC36F278FAE3E0056A860 /* encrypted_mc161_streamfile.h in Headers */,
83C7281622BC893D00678B4A /* xwma_konami_streamfile.h in Headers */,
839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */,
839E21E51F2EDAF100EE54D7 /* vorbis_custom_decoder.h in Headers */,
@ -2679,6 +2687,7 @@
83299FD01E7660C7003A3242 /* bik.c in Sources */,
8346D97C25BF838C00D1A8B0 /* mjb_mjh.c in Sources */,
839C3D27270D49FF00E13653 /* lpcm_fb.c in Sources */,
832FC36D278FA4CB0056A860 /* ubi_ckd_cwav.c in Sources */,
8373341823F60C7B00DE14DC /* tgcadpcm_decoder.c in Sources */,
83349719275DD2AC00302E21 /* wbk.c in Sources */,
83FC417326D3304D009A2022 /* xsh_xsd_xss.c in Sources */,
@ -3126,7 +3135,6 @@
8349A9151FE6258200E26435 /* ps2_xa2_rrp.c in Sources */,
836F703518BDC2190095E648 /* svs.c in Sources */,
836F6FA318BDC2190095E648 /* naomi_spsd.c in Sources */,
836F6F9D18BDC2190095E648 /* msvp.c in Sources */,
8306B0B920984552000302D4 /* blocked_matx.c in Sources */,
83A21F7B201D895B000F04B9 /* blocked_xvag.c in Sources */,
836F6FBA18BDC2190095E648 /* ngc_ymf.c in Sources */,

View File

@ -339,7 +339,7 @@ static const char* extension_list[] = {
"msf",
"mss",
"msv",
"msvp",
"msvp", //fake extension/header id for .msv
"mta2",
"mtaf",
"mul",
@ -1081,7 +1081,6 @@ static const meta_info meta_info_list[] = {
{meta_ISH_ISD, "ISH+ISD DSP Header"},
{meta_GSP_GSB, "Tecmo GSP+GSB Header"},
{meta_YDSP, "Yuke's DSP (YDSP) Header"},
{meta_MSVP, "MSVP Header"},
{meta_NGC_SSM, "SSM DSP Header"},
{meta_PS2_JOE, "Asobo Studio .JOE header"},
{meta_VGS, "Guitar Hero VGS Header"},

View File

@ -2,6 +2,7 @@
#include "../coding/coding.h"
#include "ogg_vorbis_streamfile.h"
#include "encrypted_bgm_streamfile.h"
#include "encrypted_mc161_streamfile.h"
//todo fuse ogg encryptions and use generic names
@ -158,7 +159,7 @@ static VGMSTREAM* init_vgmstream_encrypted_rpgmvo_riff(STREAMFILE* sf) {
e.new_sf = setup_subfile_streamfile(e.temp_sf, 0x10, riff_size, "wav");
if (!e.new_sf) goto fail;
dump_streamfile(e.new_sf, 0);
e.vgmstream = init_vgmstream_riff(e.new_sf);
close_streamfile(e.temp_sf);
close_streamfile(e.new_sf);
@ -171,6 +172,26 @@ fail:
}
/* Minecraft (PC) before v1.6.1 (Java version) */
static VGMSTREAM* init_vgmstream_encrypted_mc161(STREAMFILE* sf) {
encrypted_t e = {0};
if (!check_extensions(sf,"mus"))
goto fail;
/* all files use a different key so just fail on meta init */
e.temp_sf = setup_mc161_streamfile(sf);
if (!e.temp_sf) goto fail;
e.vgmstream = init_vgmstream_ogg_vorbis(e.temp_sf);
close_streamfile(e.temp_sf);
return e.vgmstream;
fail:
return NULL;
}
/* parser for various encrypted games */
VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
VGMSTREAM* v = NULL;
@ -187,5 +208,8 @@ VGMSTREAM* init_vgmstream_encrypted(STREAMFILE* sf) {
v = init_vgmstream_encrypted_rpgmvo_riff(sf);
if (v) return v;
v = init_vgmstream_encrypted_mc161(sf);
if (v) return v;
return NULL;
}

View File

@ -0,0 +1,101 @@
#ifndef _MC161_STREAMFILE_H_
#define _MC161_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
int32_t base_key;
int32_t curr_key;
uint32_t curr_offset;
} mc161_io_data;
static void decrypt_chunk(uint8_t* buf, int buf_size, mc161_io_data* data) {
int i;
int32_t hash = data->curr_key;
for (i = 0; i < buf_size; i++) {
buf[i] = (uint8_t)(buf[i] ^ ((hash >> 8) & 0xFF));
hash = (int32_t)(hash * 498729871) + (85731 * (int8_t)buf[i]); /* signed */
}
data->curr_key = hash;
data->curr_offset += buf_size;
}
static void update_key(STREAMFILE* sf, off_t offset, mc161_io_data* data) {
uint8_t buf[0x800];
size_t bytes;
size_t to_skip;
if (offset < data->curr_offset || offset == 0x00) {
data->curr_key = data->base_key;
data->curr_offset = 0x00;
to_skip = offset;
}
else {
to_skip = offset - data->curr_offset;
}
/* update key by reading and decrypt all data between current offset + last known key to requested offset */
while (to_skip > 0) {
size_t read_size = sizeof(buf);
if (read_size > to_skip)
read_size = to_skip;
bytes = read_streamfile(buf, data->curr_offset, read_size, sf);
if (!bytes) /* ??? */
break;
decrypt_chunk(buf, bytes, data); /* updates curr_offset and key */
to_skip -= bytes;
}
}
/* XOR depends on decrypted data, meanings having to decrypt linearly to reach some offset. */
static size_t mc161_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length, mc161_io_data* data) {
size_t bytes;
/* read and decrypt unneded data */
update_key(sf, offset, data);
/* read and decrypt current data */
bytes = read_streamfile(dest, offset, length, sf);
decrypt_chunk(dest, bytes, data);
return bytes;
}
/* String.hashCode() should be equivalent to this on Windows (though implementation-defined), note Java has no unsigned */
static int32_t mc161_get_java_hashcode(STREAMFILE* sf) {
char filename[1024];
int i = 0;
int32_t hash = 0;
get_streamfile_filename(sf, filename, sizeof(filename));
while (filename[i] != '\0') {
hash = 31 * hash + (uint8_t)filename[i];
i++;
}
return hash;
}
/* decrypts Minecraft old streams (some info from: https://github.com/ata4/muscode) */
static STREAMFILE* setup_mc161_streamfile(STREAMFILE* sf) {
STREAMFILE* new_sf = NULL;
mc161_io_data io_data = {0};
io_data.base_key = mc161_get_java_hashcode(sf);
io_data.curr_key = io_data.base_key;
io_data.curr_offset = 0x00;
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(mc161_io_data), mc161_io_read, NULL);
new_sf = open_fakename_streamfile_f(new_sf, NULL, "ogg");
return new_sf;
}
#endif /* _MC161_STREAMFILE_H_ */

View File

@ -874,8 +874,11 @@ static const hcakey_info hcakey_list[] = {
// Super Robot Wars 30 (PC)
{6734488621090458}, // 0017ECFB5201069A
// CHUNITHM NEW (AC)
{32931609366120192}, // 0074FF1FCE264700
// CHUNITHM NEW (AC)
{32931609366120192}, // 0074FF1FCE264700
// Shaman King: Funbari Chronicle (Android)
{1620612098671}, // 0000017954022A6F
};

View File

@ -345,8 +345,6 @@ VGMSTREAM * init_vgmstream_ydsp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_msvp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ngc_ssm(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE * streamFile);
@ -971,4 +969,6 @@ VGMSTREAM* init_vgmstream_lpcm_fb(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_wbk(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_wbk_nslb(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_ubi_ckd_cwav(STREAMFILE* sf);
#endif /*_META_H*/

View File

@ -1,43 +1,47 @@
#include "meta.h"
#include "../coding/coding.h"
/* MSV - from Sony MultiStream format [Fight Club (PS2)] */
VGMSTREAM * init_vgmstream_msv(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t channel_size;
int loop_flag, channel_count;
/* checks */
if ( !check_extensions(streamFile,"msv") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4D535670) /* "MSVp" */
goto fail;
start_offset = 0x30;
channel_count = 1;
channel_size = read_32bitBE(0x0c,streamFile);
loop_flag = 0; /* no looping and last 16 bytes (end frame) are removed, from Sony's docs */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_MSV;
vgmstream->sample_rate = read_32bitBE(0x10,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(channel_size,1);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_none;
read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile);
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
/* MSV - from Sony MultiStream format [Fight Club (PS2), PoPcap Hits Vol. 1 (PS2)] */
VGMSTREAM* init_vgmstream_msv(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
size_t channel_size;
int loop_flag, channels;
if (!is_id32be(0x00,sf, "MSVp"))
goto fail;
/* checks */
/* .msv: actual extension
* .msvp: header ID */
if (!check_extensions(sf,"msv,msvp"))
goto fail;
channels = 1;
channel_size = read_u32be(0x0c,sf);
loop_flag = 0; /* no looping and last 16 bytes (end frame) are removed, from Sony's docs */
start_offset = 0x30;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_MSV;
vgmstream->sample_rate = read_u32be(0x10,sf);
vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_none;
read_string(vgmstream->stream_name,0x10+1, 0x20,sf);
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,70 +0,0 @@
#include "meta.h"
#include "../util.h"
/* MSVP (from PoPcap Hits Vol. 1) */
VGMSTREAM * init_vgmstream_msvp(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("msvp",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x4D535670) /* "MSVp" */
goto fail;
loop_flag = 0;
channel_count = 1;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x30;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x10,streamFile);
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset)*28/16/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = loop_flag;
vgmstream->loop_end_sample = (read_32bitBE(0x0C,streamFile))*28/16/channel_count;
}
/* Just to be sure that there comes a 2 channel file */
if (channel_count == 1) {
vgmstream->layout_type = layout_none;
} else if (channel_count == 2) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10; /* Unknown for now */
}
vgmstream->meta_type = meta_MSVP;
/* 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;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -18,7 +18,7 @@ VGMSTREAM* init_vgmstream_dtk(STREAMFILE* sf) {
/* check valid frames as files have no header, and .adp/wav are common */
{
int i;
int i, blanks = 0;
for (i = 0; i < 10; i++) { /* try a bunch of frames */
/* header 0x00/01 are repeated in 0x02/03 (for error correction?),
* could also test header values (upper nibble should be 0..3, and lower nibble 0..C) */
@ -26,10 +26,13 @@ VGMSTREAM* init_vgmstream_dtk(STREAMFILE* sf) {
read_u8(0x01 + i*0x20,sf) != read_u8(0x03 + i*0x20,sf))
goto fail;
/* frame headers for silent frames are 0x0C, never null */
if (read_u8(0x00 + i*0x20,sf) == 0x00)
goto fail;
/* silent frame headers are almost always 0x0C, save a few uncommon tracks [Wave Race Blue Storm (GC), 1080 Silver Storm (GC)] */
if (read_u16be(0x00 + i*0x20,sf) == 0x00)
blanks++;
}
if (blanks > 3)
goto fail;
}
/* DTK (Disc Track) are DVD hardware-decoded streams, always stereo and no loop.

View File

@ -1364,7 +1364,7 @@ fail:
}
/* WIIADPCM - Exient wrapper [Need for Speed: Hot Pursuit (Wii)] */
/* WIIADPCM - Exient wrapper [Need for Speed: Hot Pursuit (Wii), Angry Birds: Star Wars (WiiU)] */
VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) {
dsp_meta dspm = {0};
@ -1375,18 +1375,28 @@ VGMSTREAM* init_vgmstream_dsp_wiiadpcm(STREAMFILE* sf) {
goto fail;
dspm.interleave = read_u32be(0x08,sf); /* interleave offset */
if (dspm.interleave) {
dspm.interleave -= 0x10;
}
/* 0x0c: 0 when RAM (2 DSP headers), interleave size when stream (2 WIIADPCM headers) */
/* 0x0c: NFS = 0 when RAM (2 DSP headers), interleave size when stream (2 WIIADPCM headers)
* AB = 0 (2 WIIADPCM headers) */
dspm.channels = (dspm.interleave ? 2 : 1);
dspm.max_channels = 2;
dspm.header_offset = 0x10;
if (read_u32be(0x10,sf) != 0)
dspm.header_offset = 0x10; /* NFSHP */
else
dspm.header_offset = 0x20; /* ABSW */
if (dspm.interleave)
dspm.interleave -= dspm.header_offset;
dspm.interleave_first_skip = 0x60 + dspm.header_offset;
dspm.interleave_first = dspm.interleave - dspm.interleave_first_skip;
dspm.header_spacing = dspm.interleave;
dspm.start_offset = dspm.header_offset + 0x60;
//
VGM_LOG("%lx, %x\n", dspm.header_offset, dspm.interleave);
VGM_LOG("%x, %x\n", dspm.interleave_first_skip, dspm.interleave_first);
dspm.meta_type = meta_DSP_WIIADPCM;
return init_vgmstream_dsp_common(sf, &dspm);
fail:

View File

@ -103,21 +103,21 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
fmt->size = read_u32(offset+0x04,sf);
/* WAVEFORMAT */
fmt->codec = read_u16(offset+0x08,sf);
fmt->channels = read_u16(offset+0x0a,sf);
fmt->sample_rate = read_u32(offset+0x0c,sf);
//fmt->avg_bps = read_u32(offset+0x10,sf);
fmt->block_size = read_u16(offset+0x14,sf);
fmt->bps = read_u16(offset+0x16,sf);
fmt->codec = read_u16(offset+0x08+0x00,sf);
fmt->channels = read_u16(offset+0x08+0x02,sf);
fmt->sample_rate = read_u32(offset+0x08+0x04,sf);
//fmt->avg_bps = read_u32(offset+0x08+0x08,sf);
fmt->block_size = read_u16(offset+0x08+0x0c,sf);
fmt->bps = read_u16(offset+0x08+0x0e,sf);
/* WAVEFORMATEX */
if (fmt->size >= 0x10) {
fmt->extra_size = read_u16(offset+0x18,sf);
fmt->extra_size = read_u16(offset+0x08+0x10,sf);
/* 0x1a+ depends on codec (ex. coef table for MSADPCM, samples_per_frame in MS-IMA, etc) */
}
/* WAVEFORMATEXTENSIBLE */
if (fmt->codec == 0xFFFE && fmt->extra_size >= 0x16) {
//fmt->extra_samples = read_u16(offset+0x1a,sf); /* valid_bits_per_sample or samples_per_block */
fmt->channel_layout = read_u32(offset+0x1c,sf);
//fmt->extra_samples = read_u16(offset+0x08+0x12,sf); /* valid_bits_per_sample or samples_per_block */
fmt->channel_layout = read_u32(offset+0x08+0x14,sf);
/* 0x10 guid at 0x20 */
/* happens in various .at3/at9, may be a bug in their encoder b/c MS's defs set mono as FC */
@ -135,7 +135,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
goto fail;
switch (fmt->codec) {
case 0x00: /* Yamaha AICA ADPCM [Headhunter (DC), Bomber hehhe (DC), Rayman 2 (DC)] (unofficial) */
case 0x0000: /* Yamaha AICA ADPCM [Headhunter (DC), Bomber hehhe (DC), Rayman 2 (DC)] (unofficial) */
if (fmt->bps != 4) goto fail;
if (fmt->block_size != 0x02*fmt->channels &&
fmt->block_size != 0x01*fmt->channels) goto fail;
@ -143,7 +143,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
fmt->interleave = 0x01;
break;
case 0x01: /* PCM */
case 0x0001: /* PCM */
switch (fmt->bps) {
case 24: /* Omori (PC) */
fmt->coding_type = coding_PCM24LE;
@ -160,7 +160,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
fmt->interleave = fmt->block_size / fmt->channels;
break;
case 0x02: /* MSADPCM */
case 0x0002: /* MSADPCM */
if (fmt->bps == 4) {
fmt->coding_type = coding_MSADPCM;
if (!msadpcm_check_coefs(sf, fmt->offset + 0x08 + 0x14))
@ -174,19 +174,29 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk
}
break;
case 0x11: /* MS-IMA ADPCM [Layton Brothers: Mystery Room (iOS/Android)] */
case 0x0011: /* MS-IMA ADPCM [Layton Brothers: Mystery Room (iOS/Android)] */
if (fmt->bps != 4) goto fail;
fmt->coding_type = coding_MS_IMA;
break;
case 0x20: /* Yamaha AICA ADPCM [Takuyo/Dynamix/etc DC games] */
case 0x0020: /* Yamaha AICA ADPCM [Takuyo/Dynamix/etc DC games] (official-ish) */
if (fmt->bps != 4) goto fail;
fmt->coding_type = coding_AICA;
/* official RIFF spec has 0x20 as 'Yamaha ADPCM', but data is probably not pure AICA
* (maybe with headered frames and would need extra detection?) */
break;
case 0x69: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox)] */
#ifdef VGM_USE_MPEG
case 0x0055: /* MP3 [Bear in the Big Blue House: Bear's Imagine That! (PC)] (official) */
fmt->coding_type = coding_MPEG_custom;
/* some oddities, unsure if part of standard:
* - block size is 1 (in mono)
* - bps is 16
* - extra size 0x0c, has channels? and (possibly) approx frame size */
break;
#endif
case 0x0069: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox)] */
if (fmt->bps != 4) goto fail;
fmt->coding_type = coding_XBOX_IMA;
break;
@ -355,13 +365,14 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
* .rws: Climax ATRAC3 [Silent Hill Origins (PSP), Oblivion (PSP)]
* .aud: EA Replay ATRAC3
* .at9: standard ATRAC9
* .ckd: renamed ATRAC9 [Rayman Origins (Vita)]
* .saf: Whacked! (Xbox)
* .mwv: Level-5 games [Dragon Quest VIII (PS2), Rogue Galaxy (PS2)]
* .ima: Baja: Edge of Control (PS3/X360)
* .nsa: Studio Ring games that uses NScripter [Hajimete no Otetsudai (PC)]
* .pcm: Silent Hill Arcade (PC)
*/
if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,saf,ima,nsa,pcm") ) {
if ( check_extensions(sf, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,ckd,saf,ima,nsa,pcm") ) {
;
}
else if ( check_extensions(sf, "mwv") ) {
@ -590,6 +601,11 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
JunkFound = 1;
break;
case 0x64737068: /* "dsph" */
case 0x63776176: /* "cwav" */
goto fail; /* parse elsewhere */
default:
/* ignorance is bliss */
break;
@ -625,6 +641,10 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
read_u32be(start_offset+0x3c, sf) == 0xFFFFFFFF)
goto fail;
///* MSADPCM .ckd are parsed elsewhere, though they are valid so no big deal if parsed here (just that loops should be ignored) */
if (!fmt.is_at9 && check_extensions(sf, "ckd"))
goto fail;
/* ignore Gitaroo Man Live! (PSP) multi-RIFF (to allow chunked TXTH) */
if (fmt.is_at3 && get_streamfile_size(sf) > 0x2800 && read_32bitBE(0x2800, sf) == 0x52494646) { /* "RIFF" */
goto fail;
@ -661,7 +681,11 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = fmt.block_size;
break;
#ifdef VGM_USE_MPEG
case coding_MPEG_custom:
vgmstream->layout_type = layout_none;
break;
#endif
case coding_MSADPCM:
vgmstream->layout_type = layout_none;
vgmstream->frame_size = fmt.block_size;
@ -804,6 +828,21 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
}
#endif
#ifdef VGM_USE_MPEG
case coding_MPEG_custom: {
mpeg_custom_config cfg = {0};
vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, fmt.channels, MPEG_STANDARD, &cfg);
if (!vgmstream->codec_data) goto fail;
/* should provide "fact" but it's optional (some game files don't include it) */
if (!fact_sample_count)
fact_sample_count = mpeg_get_samples(sf, start_offset, data_size);
vgmstream->num_samples = fact_sample_count;
}
break;
#endif
default:
goto fail;
}

View File

@ -1,17 +1,20 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "../util/endianness.h"
static off_t get_rws_string_size(off_t offset, STREAMFILE *streamFile);
static off_t get_rws_string_size(off_t offset, STREAMFILE* sf);
typedef struct {
int big_endian;
uint32_t codec;
int channel_count;
int channels;
int sample_rate;
int interleave;
int frame_size;
off_t file_name_offset;
@ -43,50 +46,52 @@ typedef struct {
/* RWS - RenderWare Stream (from games using RenderWare Audio middleware) */
VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
VGMSTREAM* init_vgmstream_rws(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, offset;
size_t stream_size;
int loop_flag;
int i;
int total_subsongs, target_subsong = streamFile->stream_index;
int total_subsongs, target_subsong = sf->stream_index;
rws_header rws = {0};
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
read_u32_t read_u32;
read_u16_t read_u16;
if (read_u32le(0x00,sf) != 0x0000080d) /* audio file id */
goto fail;
rws.file_size = read_u32le(0x04, sf); /* audio file size */
if (rws.file_size + 0x0c != get_streamfile_size(sf))
goto fail;
/* checks */
if (!check_extensions(streamFile,"rws"))
if (!check_extensions(sf,"rws"))
goto fail;
/* Audio .RWS is made of file + header + data chunks (non-audio .RWS with other chunks exist).
* Chunk format (LE): id, size, RW version, data of size (version is repeated but same for all chunks).
* Chunk format (LE): id, size, RW version, then data (version is repeated but same for all chunks).
* Version is 16b main + 16b build (possibly shifted), no known differences between versions,
* and can vary between files of a game. ex: 0c02, 1003, 1400 = 3.5, 1803 = 3.6, 1C02 = 3.7. */
/* parse audio chunks */
if (read_32bitLE(0x00,streamFile) != 0x0000080d) /* audio file id */
if (read_u32le(0x0c,sf) != 0x0000080e) /* header id */
goto fail;
rws.file_size = read_32bitLE(0x04, streamFile); /* audio file size */
if (rws.file_size + 0x0c != get_streamfile_size(streamFile))
goto fail;
if (read_32bitLE(0x0c,streamFile) != 0x0000080e) /* header id */
goto fail;
rws.header_size = read_32bitLE(0x10, streamFile); /* header size */
rws.header_size = read_u32le(0x10, sf); /* header size */
rws.data_offset = 0x0c + 0x0c + rws.header_size; /* usually 0x800 but not always */
if (read_32bitLE(rws.data_offset + 0x00, streamFile) != 0x0000080f) /* data chunk id */
if (read_u32le(rws.data_offset + 0x00, sf) != 0x0000080f) /* data chunk id */
goto fail;
rws.data_size = read_32bitLE(rws.data_offset + 0x04, streamFile); /* data chunk size */
if (rws.data_size+0x0c + rws.data_offset != get_streamfile_size(streamFile))
rws.data_size = read_u32le(rws.data_offset + 0x04, sf); /* data chunk size */
if (rws.data_size+0x0c + rws.data_offset != get_streamfile_size(sf))
goto fail;
/* inside header chunk (many unknown fields are probably IDs/config/garbage,
* as two files of the same size vary a lot) */
offset = 0x0c + 0x0c;
rws.big_endian = guess_endianness32bit(offset + 0x00, streamFile); /* GC/Wii/X360 */
read_32bit = rws.big_endian ? read_32bitBE : read_32bitLE;
rws.big_endian = guess_endian32(offset + 0x00, sf); /* GC/Wii/X360 */
read_u32 = rws.big_endian ? read_u32be : read_u32le;
read_u16 = rws.big_endian ? read_u16be : read_u16le;
/* base header */
{
@ -94,9 +99,9 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
/* 0x04/08/10: sizes of various sections? */
/* 0x14/18: config? */
/* 0x1c: null? */
rws.total_segments = read_32bit(offset + 0x20, streamFile);
rws.total_segments = read_u32(offset + 0x20, sf);
/* 0x24: config? */
rws.total_layers = read_32bit(offset + 0x28, streamFile);
rws.total_layers = read_u32(offset + 0x28, sf);
/* 0x2c: config? */
/* 0x30: 0x800? */
/* 0x34: block_layers_size? */
@ -109,7 +114,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
/* audio file name */
{
rws.file_name_offset = offset;
offset += get_rws_string_size(offset, streamFile);
offset += get_rws_string_size(offset, sf);
}
/* RWS data can be divided in two ways:
@ -144,15 +149,15 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
if (i+1 == rws.target_segment) {
/* 0x00/04/0c: config? */
/* others: ? */
rws.segment_layers_size = read_32bit(offset + 0x18, streamFile); /* sum of all including padding */
rws.segment_offset = read_32bit(offset + 0x1c, streamFile);
rws.segment_layers_size = read_u32(offset + 0x18, sf); /* sum of all including padding */
rws.segment_offset = read_u32(offset + 0x1c, sf);
}
offset += 0x20;
}
/* usable layer sizes per segment */
for (i = 0; i < (rws.total_segments * rws.total_layers); i++) {
size_t usable_size = read_32bit(offset, streamFile); /* without padding */
size_t usable_size = read_u32(offset, sf); /* without padding */
/* size order: segment1 layer1 size, ..., segment1 layerN size, segment2 layer1 size, etc */
if (i+1 == target_subsong) { /* order matches our subsong order */
rws.usable_size = usable_size;
@ -170,7 +175,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
if (i+1 == rws.target_segment) {
rws.segment_name_offset = offset;
}
offset += get_rws_string_size(offset, streamFile);
offset += get_rws_string_size(offset, sf);
}
/* layer info */
@ -179,13 +184,15 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
/* 0x00/04: config? */
/* 0x08: null? */
/* 0x0c: related to samples per frame? (XBOX-IMA=07, PSX=1C, DSP=0E, PCM=01) */
//block_size_pad = read_32bit(offset + 0x10, streamFile); /* with padding, can be different per layer */
/* 0x14/18: ? */
//block_size_pad = read_u32(offset + 0x10, sf); /* with padding, can be different per layer */
/* 0x14: config? */
rws.interleave = read_u16(offset + 0x18, sf); /* wrong values in Burnout 2 Xbox, otherwise correct */
rws.frame_size = read_u16(offset + 0x1a, sf); /* same */
/* 0x1c: codec related? */
rws.block_size = read_32bit(offset + 0x20, streamFile); /* without padding */
rws.layer_start = read_32bit(offset + 0x24, streamFile); /* skip data */
rws.block_size = read_u32(offset + 0x20, sf); /* without padding */
rws.layer_start = read_u32(offset + 0x24, sf); /* skip data */
}
rws.block_layers_size += read_32bit(offset + 0x10, streamFile); /* needed to skip during decode */
rws.block_layers_size += read_u32(offset + 0x10, sf); /* needed to skip during decode */
offset += 0x28;
}
@ -193,15 +200,17 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
for (i = 0; i < rws.total_layers; i++) {
uint32_t layer_codec = 0;
if (i+1 == rws.target_layer) {
rws.sample_rate = read_32bit(offset + 0x00, streamFile);
rws.sample_rate = read_u32(offset + 0x00, sf);
/* 0x04: config? */
//rws.layer_size = read_32bit(offset + 0x08, streamFile); /* same or close to usable size */
//rws.layer_size = read_u32(offset + 0x08, sf); /* same or close to usable size */
/* 0x0c: bits per sample */
rws.channel_count = read_8bit(offset + 0x0d, streamFile);
/* others: ? */
rws.codec = (uint32_t)read_32bit(offset + 0x1c, streamFile); /* 128b uuid (32b-16b-16b-8b*8) but first 32b is enough */
rws.channels = read_u8(offset + 0x0d, sf);
/* 0x0e: null or some value ? */
/* 0x10/0x14: null? */
/* 0x18: null or some size? */
rws.codec = read_u32(offset + 0x1c, sf); /* 128b uuid (32b-16b-16b-8b*8) but first 32b is enough */
}
layer_codec = (uint32_t)read_32bit(offset + 0x1c, streamFile);
layer_codec = read_u32(offset + 0x1c, sf);
offset += 0x2c;
/* DSP has an extra field per layer */
@ -226,7 +235,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
if (i+1 == rws.target_layer) {
rws.layer_name_offset = offset;
}
offset += get_rws_string_size(offset, streamFile);
offset += get_rws_string_size(offset, sf);
}
/* rest is padding/garbage until chunk end (may contain strings and uninitialized memory) */
@ -249,11 +258,11 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
{
char base_name[STREAM_NAME_SIZE], file_name[STREAM_NAME_SIZE], segment_name[STREAM_NAME_SIZE], layer_name[STREAM_NAME_SIZE];
get_streamfile_basename(streamFile, base_name, sizeof(base_name));
get_streamfile_basename(sf, base_name, sizeof(base_name));
/* null terminated */
read_string(file_name,STREAM_NAME_SIZE, rws.file_name_offset, streamFile);
read_string(segment_name,STREAM_NAME_SIZE, rws.segment_name_offset, streamFile);
read_string(layer_name,STREAM_NAME_SIZE, rws.layer_name_offset, streamFile);
read_string(file_name,STREAM_NAME_SIZE, rws.file_name_offset, sf);
read_string(segment_name,STREAM_NAME_SIZE, rws.segment_name_offset, sf);
read_string(layer_name,STREAM_NAME_SIZE, rws.layer_name_offset, sf);
/* some internal names aren't very interesting and are stuff like "SubStream" */
if (strcmp(base_name, file_name) == 0) {
@ -275,7 +284,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(rws.channel_count,loop_flag);
vgmstream = allocate_vgmstream(rws.channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_RWS;
@ -289,41 +298,47 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
vgmstream->full_block_size = rws.block_layers_size;
switch(rws.codec) {
case 0xD01BD217: /* {D01BD217,3587,4EED,B9,D9,B8,E8,6E,A9,B9,95} PCM PC/X360 */
/* ex. D.i.R.T.: Origin of the Species (PC), The Legend of Spyro (X360) */
vgmstream->coding_type = coding_PCM16_int;
vgmstream->codec_endian = (rws.big_endian);
vgmstream->interleave_block_size = 0x02; /* only to setup channels */
case 0xD01BD217: /* {D01BD217,3587,4EED,B9,D9,B8,E8,6E,A9,B9,95} PCM PC/X360/PS2 */
/* D.i.R.T.: Origin of the Species (PC), The Legend of Spyro (X360), kill.switch (PS2) */
if (rws.interleave == 0x02) { /* PC, X360 */
vgmstream->coding_type = coding_PCM16_int;
vgmstream->codec_endian = (rws.big_endian);
vgmstream->interleave_block_size = rws.interleave; /* only to setup channels */
}
else { /* PS2 */
vgmstream->coding_type = rws.big_endian ? coding_PCM16BE : coding_PCM16LE;
vgmstream->interleave_block_size = rws.interleave; /* only to setup channels */
}
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, rws.channel_count, 16);
vgmstream->num_samples = pcm16_bytes_to_samples(stream_size, rws.channels);
break;
case 0xD9EA9798: /* {D9EA9798,BBBC,447B,96,B2,65,47,59,10,2E,16} PS-ADPCM PS2 */
/* ex. Silent Hill Origins (PS2), Ghost Rider (PS2), Max Payne 2 (PS2), Nana (PS2) */
/* Silent Hill Origins (PS2), Ghost Rider (PS2), Max Payne 2 (PS2), Nana (PS2) */
vgmstream->coding_type = coding_PSX;
vgmstream->interleave_block_size = rws.block_size / 2;
vgmstream->num_samples = ps_bytes_to_samples(stream_size, rws.channel_count);
vgmstream->num_samples = ps_bytes_to_samples(stream_size, rws.channels);
break;
case 0xF86215B0: /* {F86215B0,31D5,4C29,BD,37,CD,BF,9B,D1,0C,53} DSP GC/Wii */
/* ex. Burnout 2 (GC), Alice in Wonderland (Wii) */
/* Burnout 2 (GC), Alice in Wonderland (Wii) */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->interleave_block_size = rws.block_size / 2;
/* get coefs (all channels share them; also seem fixed for all RWS) */
dsp_read_coefs_be(vgmstream, streamFile, rws.coefs_offset, 0);
dsp_read_coefs_be(vgmstream, sf, rws.coefs_offset, 0);
vgmstream->num_samples = dsp_bytes_to_samples(stream_size, rws.channel_count);
vgmstream->num_samples = dsp_bytes_to_samples(stream_size, rws.channels);
break;
case 0xEF386593: /* {EF386593,B611,432D,95,7F,A7,1A,DE,44,22,7A} XBOX-IMA PC */
case 0x632FA22B: /* {632FA22B,11DD,458F,AA,27,A5,C3,46,E9,79,0E} XBOX-IMA Xbox */
/* ex. Broken Sword 3 (PC), Jacked (PC/Xbox), Burnout 2 (Xbox) */
/* Broken Sword 3 (PC), Jacked (PC/Xbox), Burnout 2 (Xbox) */
vgmstream->coding_type = coding_XBOX_IMA; /* same data though different uuid */
vgmstream->interleave_block_size = 0;
vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, rws.channel_count);
vgmstream->num_samples = xbox_ima_bytes_to_samples(stream_size, rws.channels);
break;
default:
@ -332,7 +347,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
}
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
@ -343,10 +358,10 @@ fail:
/* rws-strings are null-terminated then padded to 0x10 (weirdly enough the padding contains garbage) */
static off_t get_rws_string_size(off_t offset, STREAMFILE *streamFile) {
static off_t get_rws_string_size(off_t offset, STREAMFILE* sf) {
int i;
for (i = 0; i < 255; i++) { /* arbitrary max */
if (read_8bit(offset+i, streamFile) == 0) { /* null terminator */
if (read_u8(offset+i, sf) == 0) { /* null terminator */
return i + (0x10 - (i % 0x10)); /* size is padded */
}
}

View File

@ -2,54 +2,56 @@
#include "../util.h"
#include "../coding/coding.h"
/* RXWS - from Sony SCEI PS2 games (Okage: Shadow King, Genji, Bokura no Kazoku) */
/* RXWS - from Sony SCEI games [Okage: Shadow King (PS2), Genji (PS2), Bokura no Kazoku (PS2))] */
VGMSTREAM* init_vgmstream_rxws(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* sh = NULL;
STREAMFILE* sf_head = NULL;
STREAMFILE* sf_body = NULL;
off_t start_offset, chunk_offset, name_offset = 0;
size_t stream_size, chunk_size;
int loop_flag = 0, channels, is_separate = 0, type, sample_rate;
int loop_flag = 0, channels, is_xwh = 0, type, sample_rate;
int32_t num_samples, loop_start;
int total_subsongs, target_subsong = sf->stream_index;
/* for plugins that start with .xwb */
if (check_extensions(sf,"xwb")) {
/* extra check to reject Microsoft's XWB faster */
if (is_id32be(0x00,sf,"WBND") || is_id32be(0x00,sf,"DNBW")) /* LE/BE */
goto fail;
sf_head = open_streamfile_by_ext(sf, "xwh");
if (!sf_head) goto fail;
}
else {
sf_head = sf;
}
if (!is_id32be(0x00,sf_head,"RXWS"))
goto fail;
/* checks */
/* .xws: header and data
* .xwh+xwb: header + data (.bin+dat are also found in Wild Arms 4/5) */
if (!check_extensions(sf,"xws,xwb"))
goto fail;
is_separate = check_extensions(sf,"xwb");
/* xwh+xwb: use xwh as header; otherwise use the current file */
if (is_separate) {
/* extra check to reject Microsoft's XWB faster */
if (is_id32be(0x00,sf,"WBND") || /* (LE) */
is_id32be(0x00,sf,"DNBW")) /* (BE) */
goto fail;
sh = open_streamfile_by_ext(sf, "xwh");
if (!sh) goto fail;
} else {
sh = sf;
}
if (!is_id32be(0x00,sh,"RXWS"))
goto fail;
/* file size (just the .xwh/xws) */
if (read_u32le(0x04,sh) + 0x10 != get_streamfile_size(sh))
if (read_u32le(0x04,sf_head) + 0x10 != get_streamfile_size(sf_head))
goto fail;
/* 0x08: version (0x100/0x200)
* 0x0C: null */
/* typical chunks: FORM, FTXT, MARK, BODY (for .xws) */
if (!is_id32be(0x10,sh,"FORM")) /* main header (always first) */
if (!is_id32be(0x10,sf_head,"FORM")) /* main header (always first) */
goto fail;
chunk_size = read_u32le(0x10+0x04,sh); /* size - 0x10 */
chunk_size = read_u32le(0x10+0x04,sf_head); /* size - 0x10 */
/* 0x08 version (0x100), 0x0c: null */
chunk_offset = 0x20;
/* check multi-streams */
total_subsongs = read_s32le(chunk_offset+0x00,sh);
total_subsongs = read_s32le(chunk_offset+0x00,sf_head);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
@ -57,60 +59,72 @@ VGMSTREAM* init_vgmstream_rxws(STREAMFILE* sf) {
/* read stream header */
{
off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong-1); /* position in FORM */
off_t stream_offset, next_stream_offset, data_offset = 0;
off_t stream_offset, next_stream_offset, body_offset;
type = read_u8(header_offset+0x00, sh);
type = read_u8(header_offset+0x00, sf_head);
/* 0x01: unknown (always 0x1c) */
/* 0x02: flags? (usually 8002/0002, & 0x01 if looped) */
/* 0x04: vol/pan stuff? (0x00007F7F) */
/* 0x08: null? */
channels = read_u8(header_offset+0x09, sh);
channels = read_u8(header_offset+0x09, sf_head);
sample_rate = read_u16le(header_offset+0x0a,sf_head);
/* 0x0c: null? */
sample_rate = read_u16le(header_offset+0x0a,sh);
stream_offset = read_u32le(header_offset+0x10,sh);
num_samples = read_s32le(header_offset+0x14,sh);
loop_start = read_s32le(header_offset+0x18,sh);
stream_offset = read_u32le(header_offset+0x10,sf_head);
num_samples = read_s32le(header_offset+0x14,sf_head);
loop_start = read_s32le(header_offset+0x18,sf_head);
loop_flag = (loop_start >= 0);
/* find data start and size */
if (is_separate) {
data_offset = 0x00;
}
else {
off_t current_chunk = 0x10;
/* note the extra 0x10 in chunk_size/offsets */
while (current_chunk < get_streamfile_size(sf)) {
if (is_id32be(current_chunk,sf, "BODY")) {
data_offset = 0x10 + current_chunk;
/* find body start and size (needed for stream_size) */
{
uint32_t current_chunk = 0x10;
body_offset = 0x00;
while (current_chunk < get_streamfile_size(sf_head)) {
if (is_id32be(current_chunk,sf_head, "BODY")) {
body_offset = 0x10 + current_chunk;
is_xwh = 1;
break;
}
current_chunk += 0x10 + read_u32le(current_chunk+4,sf);
/* note the extra 0x10 in chunk_size/offsets */
current_chunk += 0x10 + read_u32le(current_chunk + 0x04,sf_head);
}
/* .xwh and .xws are only different in that the latter has BODY chunk (no flags/sizes) */
is_xwh = !body_offset;
/* for plugins that start with .xwh (and don't check extensions) */
if (is_xwh && sf == sf_head) {
sf_body = open_streamfile_by_ext(sf, "xwb");
if (!sf_body) goto fail;
}
else {
sf_body = sf;
}
if (!data_offset) goto fail;
}
if (target_subsong == total_subsongs) {
next_stream_offset = get_streamfile_size(is_separate ? sf : sh) - data_offset;
uint32_t max_size = get_streamfile_size(sf_body);
next_stream_offset = max_size - body_offset;
} else {
off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong);
next_stream_offset = read_u32le(next_header_offset+0x10,sh);
next_stream_offset = read_u32le(next_header_offset+0x10, sf_head);
}
stream_size = next_stream_offset - stream_offset;
start_offset = data_offset + stream_offset;
start_offset = body_offset + stream_offset;
}
/* get stream name (always follows FORM) */
if (is_id32be(0x10+0x10 + chunk_size,sh, "FTXT")) {
if (is_id32be(0x10+0x10 + chunk_size,sf_head, "FTXT")) {
chunk_offset = 0x10+0x10 + chunk_size + 0x10;
if (read_s32le(chunk_offset+0x00,sh) == total_subsongs) {
name_offset = chunk_offset + read_u32le(chunk_offset+0x04 + (target_subsong-1)*0x04,sh);
if (read_s32le(chunk_offset+0x00,sf_head) == total_subsongs) {
name_offset = chunk_offset + read_u32le(chunk_offset+0x04 + (target_subsong-1)*0x04,sf_head);
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_RXWS;
@ -118,7 +132,7 @@ VGMSTREAM* init_vgmstream_rxws(STREAMFILE* sf) {
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sh);
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset, sf_head);
switch (type) {
case 0x00: /* PS-ADPCM */
@ -149,7 +163,7 @@ VGMSTREAM* init_vgmstream_rxws(STREAMFILE* sf) {
encoder_delay = 1024 + 69*2; /* observed default */
vgmstream->num_samples = num_samples - encoder_delay;
vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay);
vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf_body, start_offset,stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
@ -164,14 +178,16 @@ VGMSTREAM* init_vgmstream_rxws(STREAMFILE* sf) {
}
/* open the file for reading */
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
if (!vgmstream_open_stream(vgmstream, sf_body, start_offset))
goto fail;
if (is_separate && sh) close_streamfile(sh);
if (sf != sf_head) close_streamfile(sf_head);
if (sf != sf_body) close_streamfile(sf_body);
return vgmstream;
fail:
if (is_separate && sh) close_streamfile(sh);
if (sf != sf_head) close_streamfile(sf_head);
if (sf != sf_body) close_streamfile(sf_body);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -6,93 +6,123 @@
VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* sf_head = NULL;
STREAMFILE* sf_body = NULL;
off_t start_offset, data_offset, chunk_offset, name_offset = 0;
size_t stream_size;
uint32_t base1_offset, base2_offset, base3_offset;
int is_sgx, is_sgb = 0;
int loop_flag, channels, codec;
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
int is_sgx, is_sgd = 0;
int loop_flag, channels, codec, sample_rate;
int32_t num_samples, loop_start_sample, loop_end_sample;
int total_subsongs, target_subsong = sf->stream_index;
/* check extension, case insensitive */
/* .sgx: header+data (Genji), .sgd: header+data, .sgh/sgd: header/data */
if (!check_extensions(sf,"sgx,sgd,sgb"))
goto fail;
is_sgx = check_extensions(sf,"sgx");
is_sgb = check_extensions(sf,"sgb");
/* SGB+SGH: use SGH as header; otherwise use the current file as header */
if (is_sgb) {
/* for plugins that start with .sgb */
if (check_extensions(sf,"sgb")) {
sf_head = open_streamfile_by_ext(sf, "sgh");
if (!sf_head) goto fail;
} else {
}
else {
sf_head = sf;
}
/* SGXD base (size 0x10) */
if (read_32bitBE(0x00,sf_head) != 0x53475844) /* "SGXD" */
if (!is_id32be(0x00,sf_head, "SGXD"))
goto fail;
/* 0x04 SGX: full header_size; SGD/SGH: unknown header_size (counting from 0x0/0x8/0x10, varies) */
/* 0x08 SGX: first chunk offset? (0x10); SGD/SGH: full header_size */
/* 0x0c SGX/SGH: full data size with padding; SGD: full data size + 0x80000000 with padding */
if (is_sgb) {
data_offset = 0x00;
} else if ( is_sgx ) {
data_offset = read_32bitLE(0x04,sf_head);
/* checks */
/* .sgx: header+data (Genji)
* .sgd: header+data (common)
* .sgh+sgd: header+data */
if (!check_extensions(sf,"sgx,sgd,sgb"))
goto fail;
/* SGXD base (size 0x10), always LE even on PS3 */
/* 0x04: SGX = full header size
SGD/SGH = bank name offset (part of NAME table, usually same as filename) */
/* 0x08: SGX = first chunk offset? (0x10)
SGD/SGH = full header size */
/* 0x0c: SGX/SGH = full data size with padding /
SGD = full data size ^ (1<<31) with padding */
base1_offset = read_u32le(0x04, sf_head);
base2_offset = read_u32le(0x08, sf_head);
base3_offset = read_u32le(0x0c, sf_head);
is_sgx = base2_offset == 0x10; /* fixed size */
is_sgd = base3_offset & (1 << 31); /* flag */
/* Ogg SGXD don't have flag (probably due to codec hijack, or should be split), allow since it's not so obvious */
if (!(is_sgx || is_sgd) && get_streamfile_size(sf_head) != base2_offset) /* sgh but wrong header size must be sgd */
is_sgd = 1;
/* for plugins that start with .sgh (and don't check extensions) */
if (!(is_sgx || is_sgd) && sf == sf_head) {
sf_body = open_streamfile_by_ext(sf, "sgb");
if (!sf_body) goto fail;
}
else {
sf_body = sf;
}
if (is_sgx) {
data_offset = base1_offset;
} else if (is_sgd) {
data_offset = base2_offset;
} else {
data_offset = read_32bitLE(0x08,sf_head);
data_offset = 0x00;
}
/* typical chunks: WAVE, RGND, NAME (strings for WAVE or RGND), SEQD (related to SFX), WSUR, WMKR, BUSS */
/* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */
if (is_sgx) { /* position after chunk+size */
if (read_32bitBE(0x10,sf_head) != 0x57415645) goto fail; /* "WAVE" */
if (!is_id32be(0x10,sf_head, "WAVE"))
goto fail;
chunk_offset = 0x18;
} else {
if (!find_chunk_le(sf_head, 0x57415645,0x10,0, &chunk_offset,NULL)) goto fail; /* "WAVE" */
if (!find_chunk_le(sf_head, get_id32be("WAVE"),0x10,0, &chunk_offset, NULL))
goto fail;
}
/* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */
/* check multi-streams (usually only SE containers; Puppeteer) */
total_subsongs = read_32bitLE(chunk_offset+0x04,sf_head);
total_subsongs = read_s32le(chunk_offset+0x04,sf_head);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* read stream header */
{
off_t stream_offset;
uint32_t stream_offset;
chunk_offset += 0x08 + 0x38 * (target_subsong-1); /* position in target header*/
/* 0x00 ? (00/01/02) */
if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */
name_offset = read_32bitLE(chunk_offset+0x04,sf_head);
codec = read_8bit(chunk_offset+0x08,sf_head);
channels = read_8bit(chunk_offset+0x09,sf_head);
name_offset = read_u32le(chunk_offset+0x04,sf_head);
codec = read_u8(chunk_offset+0x08,sf_head);
channels = read_u8(chunk_offset+0x09,sf_head);
/* 0x0a null */
sample_rate = read_32bitLE(chunk_offset+0x0c,sf_head);
sample_rate = read_s32le(chunk_offset+0x0c,sf_head);
/* 0x10 info_type: meaning of the next value
* (00=null, 30/40=data size without padding (ADPCM, ATRAC3plus), 80/A0=block size (AC3) */
/* 0x14 info_value (see above) */
/* 0x18 unknown (ex. 0x0008/0010/3307/CC02/etc)x2 */
/* 0x1c null */
/* 0x10: info_type, meaning of the next value
* (00=null, 30/40=data size without padding (ADPCM, ATRAC3plus), 80/A0=block size (AC3) */
/* 0x14: info_value (see above) */
/* 0x18: unknown (ex. 0x0008/0010/3307/CC02/etc)x2 */
/* 0x1c: null */
num_samples = read_32bitLE(chunk_offset+0x20,sf_head);
loop_start_sample = read_32bitLE(chunk_offset+0x24,sf_head);
loop_end_sample = read_32bitLE(chunk_offset+0x28,sf_head);
stream_size = read_32bitLE(chunk_offset+0x2c,sf_head); /* stream size (without padding) / interleave (for type3) */
num_samples = read_s32le(chunk_offset+0x20,sf_head);
loop_start_sample = read_s32le(chunk_offset+0x24,sf_head);
loop_end_sample = read_s32le(chunk_offset+0x28,sf_head);
stream_size = read_u32le(chunk_offset+0x2c,sf_head); /* stream size (without padding) / interleave (for type3) */
if (is_sgx) {
stream_offset = 0x0;
} else{
stream_offset = read_32bitLE(chunk_offset+0x30,sf_head);
stream_offset = read_u32le(chunk_offset+0x30,sf_head);
}
/* 0x34 SGX: unknown; SGD/SGH: stream size (with padding) / interleave */
/* 0x34: SGX = unknown
* SGD/SGH = stream size (with padding) / interleave */
loop_flag = loop_start_sample!=0xffffffff && loop_end_sample!=0xffffffff;
loop_flag = loop_start_sample != -1 && loop_end_sample != -1;
start_offset = data_offset + stream_offset;
}
@ -121,7 +151,7 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) {
#ifdef VGM_USE_VORBIS
case 0x02: /* Ogg Vorbis [Ni no Kuni: Wrath of the White Witch Remastered (PC)] (codec hijack?) */
vgmstream->codec_data = init_ogg_vorbis(sf, start_offset, stream_size, NULL);
vgmstream->codec_data = init_ogg_vorbis(sf_body, start_offset, stream_size, NULL);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_OGG_VORBIS;
vgmstream->layout_type = layout_none;
@ -130,7 +160,7 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) {
case 0x03: /* PS-ADPCM [Genji (PS3), Ape Escape Move (PS3)]*/
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
if (is_sgx || is_sgb) {
if (!is_sgd) {
vgmstream->interleave_block_size = 0x10;
} else { /* this only seems to happen with SFX */
vgmstream->interleave_block_size = stream_size;
@ -143,7 +173,7 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) {
#ifdef VGM_USE_FFMPEG
case 0x04: { /* ATRAC3plus [Kurohyo 1/2 (PSP), BraveStory (PSP)] */
vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, start_offset, NULL);
vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf_body, start_offset, NULL);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
@ -163,7 +193,7 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) {
#ifdef VGM_USE_FFMPEG
case 0x06: { /* AC3 [Tokyo Jungle (PS3), Afrika (PS3)] */
vgmstream->codec_data = init_ffmpeg_offset(sf, start_offset, stream_size);
vgmstream->codec_data = init_ffmpeg_offset(sf_body, start_offset, stream_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
@ -173,7 +203,6 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) {
ffmpeg_set_skip_samples(vgmstream->codec_data, 256);
/* SGXD loop/sample values are relative (without skip samples), no need to adjust */
break;
}
#endif
@ -183,14 +212,16 @@ VGMSTREAM* init_vgmstream_sgxd(STREAMFILE* sf) {
goto fail;
}
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
if (!vgmstream_open_stream(vgmstream, sf_body, start_offset))
goto fail;
if (is_sgb && sf_head) close_streamfile(sf_head);
if (sf != sf_head) close_streamfile(sf_head);
if (sf != sf_body) close_streamfile(sf_body);
return vgmstream;
fail:
if (is_sgb && sf_head) close_streamfile(sf_head);
if (sf != sf_head) close_streamfile(sf_head);
if (sf != sf_body) close_streamfile(sf_body);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,116 +1,110 @@
#include "meta.h"
#include <ctype.h>
/* .sli+ogg/opus - KiriKiri engine / WaveLoopManager loop points loader [Fate/Stay Night (PC), World End Economica (PC)] */
VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamData = NULL;
int32_t loop_start = -1, loop_length = -1;
int32_t loop_from = -1, loop_to = -1;
/* checks */
if (!check_extensions(streamFile, "sli"))
goto fail;
{
/* try with file.ogg/opus.sli=header and file.ogg/opus=data */
char basename[PATH_LIMIT];
get_streamfile_basename(streamFile,basename,PATH_LIMIT);
streamData = open_streamfile_by_filename(streamFile, basename);
if (!streamData) goto fail;
}
/* let the real initer do the parsing */
if (check_extensions(streamData, "ogg")) { /* Fate/Stay Night (PC) */
#ifdef VGM_USE_VORBIS
vgmstream = init_vgmstream_ogg_vorbis(streamData);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_OGG_SLI;
#else
goto fail;
#endif
}
else if (check_extensions(streamData, "opus")) { /* Sabbat of the Witch (PC) */
#ifdef VGM_USE_FFMPEG
vgmstream = init_vgmstream_ffmpeg(streamData);
if (!vgmstream) goto fail;
/* FFmpeg's Opus encoder delay is borked but no need to fix:
* somehow sli+opus use 0 in the OpusHead (to simplify looping?) */
vgmstream->meta_type = meta_OPUS_SLI;
#else
goto fail;
#endif
}
else {
goto fail;
}
/* find loop text */
{
char line[PATH_LIMIT];
size_t bytes_read;
off_t sli_offset;
int line_ok;
sli_offset = 0;
while ((loop_start == -1 || loop_length == -1) && sli_offset < get_streamfile_size(streamFile)) {
char *endptr, *foundptr;
bytes_read = read_line(line, sizeof(line), sli_offset, streamFile, &line_ok);
if (!line_ok) goto fail;
if (memcmp("LoopStart=",line,10)==0 && line[10] != '\0') {
loop_start = strtol(line+10,&endptr,10);
if (*endptr != '\0') {
loop_start = -1; /* if it didn't parse cleanly */
}
}
else if (memcmp("LoopLength=",line,11)==0 && line[11] != '\0') {
loop_length = strtol(line+11,&endptr,10);
if (*endptr != '\0') {
loop_length = -1; /* if it didn't parse cleanly */
}
}
/* a completely different format (2.0?), also with .sli extension and can be handled similarly */
if ((foundptr = strstr(line,"To=")) != NULL && isdigit(foundptr[3])) {
loop_to = strtol(foundptr+3,&endptr,10);
if (*endptr != ';') {
loop_to = -1;
}
}
if ((foundptr = strstr(line,"From=")) != NULL && isdigit(foundptr[5])) {
loop_from = strtol(foundptr+5,&endptr,10);
if (*endptr != ';') {
loop_from = -1;
}
}
sli_offset += bytes_read;
}
}
if (loop_start != -1 && loop_length != -1) { /* v1 */
vgmstream_force_loop(vgmstream,1,loop_start, loop_start+loop_length);
}
else if (loop_from != -1 && loop_to != -1) { /* v2 */
vgmstream_force_loop(vgmstream,1,loop_to, loop_from);
}
else {
goto fail; /* if there's no loop points the .sli wasn't valid */
}
close_streamfile(streamData);
return vgmstream;
fail:
close_streamfile(streamData);
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include <ctype.h>
/* .sli+ogg/opus - KiriKiri engine / WaveLoopManager loop points loader [Fate/Stay Night (PC), World End Economica (PC)] */
VGMSTREAM* init_vgmstream_sli_ogg(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* sf_data = NULL;
int32_t loop_start = -1, loop_length = -1;
int32_t loop_from = -1, loop_to = -1;
/* checks */
if (!check_extensions(sf, "sli"))
goto fail;
{
/* try with file.ogg/opus.sli=header and file.ogg/opus=data */
char basename[PATH_LIMIT];
get_streamfile_basename(sf,basename,PATH_LIMIT);
sf_data = open_streamfile_by_filename(sf, basename);
if (!sf_data) goto fail;
}
if (!is_id32be(0x00, sf_data, "OggS"))
goto fail;
/* let the real initer do the parsing */
if (is_id32be(0x1c, sf_data, "Opus")) { /* Sabbat of the Witch (PC) */
vgmstream = init_vgmstream_ogg_opus(sf_data);
if (!vgmstream) goto fail;
/* somehow sli+opus use 0 encoder delay in the OpusHead (to simplify looping?) */
vgmstream->meta_type = meta_OPUS_SLI;
}
else { /* Fate/Stay Night (PC) */
vgmstream = init_vgmstream_ogg_vorbis(sf_data);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_OGG_SLI;
}
/* find loop text */
{
char line[PATH_LIMIT];
size_t bytes_read;
off_t sli_offset;
int line_ok;
sli_offset = 0;
while ((loop_start == -1 || loop_length == -1) && sli_offset < get_streamfile_size(sf)) {
char *endptr, *foundptr;
bytes_read = read_line(line, sizeof(line), sli_offset, sf, &line_ok);
if (!line_ok) goto fail;
sli_offset += bytes_read;
/* files may be padded with 0s */
/* comments in v2.0 [Sabbath of the Witch (PC), KARAKARA (PC)] */
if (line[0] == '#')
continue;
if (memcmp("LoopStart=", line,10) == 0 && line[10] != '\0') {
loop_start = strtol(line + 10, &endptr, 10);
if (*endptr != '\0') {
loop_start = -1; /* if it didn't parse cleanly */
}
}
else if (memcmp("LoopLength=", line, 11) == 0 && line[11] != '\0') {
loop_length = strtol(line + 11, &endptr, 10);
if (*endptr != '\0') {
loop_length = -1; /* if it didn't parse cleanly */
}
}
/* a completely different format ("#2.00"?), can be handled similarly */
if ((foundptr = strstr(line,"To=")) != NULL && isdigit(foundptr[3])) {
loop_to = strtol(foundptr + 3, &endptr, 10);
if (*endptr != ';') {
loop_to = -1;
}
}
if ((foundptr = strstr(line,"From=")) != NULL && isdigit(foundptr[5])) {
loop_from = strtol(foundptr + 5, &endptr, 10);
if (*endptr != ';') {
loop_from = -1;
}
}
}
}
if (loop_start != -1 && loop_length != -1) { /* v1 */
vgmstream_force_loop(vgmstream, 1, loop_start, loop_start + loop_length);
}
else if (loop_from != -1 && loop_to != -1) { /* v2 */
vgmstream_force_loop(vgmstream, 1, loop_to, loop_from);
}
else {
goto fail; /* if there's no loop points the .sli wasn't valid */
}
close_streamfile(sf_data);
return vgmstream;
fail:
close_streamfile(sf_data);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,5 +1,6 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../coding/coding.h"
typedef enum { MSADPCM, DSP, MP3, XMA2 } ckd_codec;
@ -9,31 +10,36 @@ VGMSTREAM* init_vgmstream_ubi_ckd(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, first_offset = 0x0c, chunk_offset;
size_t chunk_size, data_size;
int loop_flag, channel_count, interleave = 0, format;
int loop_flag, channels, interleave = 0, format;
ckd_codec codec;
int big_endian;
uint32_t (*read_u32)(off_t,STREAMFILE*);
uint16_t (*read_u16)(off_t,STREAMFILE*);
/* checks */
if (!is_id32be(0x00,sf, "RIFF"))
goto fail;
if (!is_id32be(0x08,sf, "WAVE"))
goto fail;
/* .wav.ckd: main (other files are called .xxx.ckd too) */
if (!check_extensions(sf,"ckd"))
goto fail;
/* another slighly funny RIFF */
if (read_u32be(0x00,sf) != 0x52494646) /* "RIFF" */
/* another slighly funny RIFF, mostly standard except machine endian and minor oddities */
if (!is_id32be(0x0c,sf, "fmt "))
goto fail;
if (read_u32be(0x08,sf) != 0x57415645) /* "WAVE" */
goto fail;
/* RIFF size is normal */
big_endian = guess_endianness32bit(0x04, sf);
read_u32 = big_endian ? read_u32be : read_u32le;
read_u16 = big_endian ? read_u16be : read_u16le;
if (read_u32(0x04, sf) + 0x04 + 0x04 != get_streamfile_size(sf))
goto fail;
loop_flag = 0;
format = read_u16(0x14,sf);
channel_count = read_u16(0x16,sf);
channels = read_u16(0x16,sf);
switch(format) {
case 0x0002:
@ -50,7 +56,7 @@ VGMSTREAM* init_vgmstream_ubi_ckd(STREAMFILE* sf) {
} else if (find_chunk_be(sf, 0x6461744C,first_offset,0, &chunk_offset,&chunk_size)) { /* "datL" */
/* mono "datL" or full interleave with a "datR" after the "datL" (no check, pretend it exists) */
start_offset = chunk_offset;
data_size = chunk_size * channel_count;
data_size = chunk_size * channels;
interleave = (0x4+0x4) + chunk_size; /* don't forget to skip the "datR"+size chunk */
} else {
goto fail;
@ -107,11 +113,11 @@ VGMSTREAM* init_vgmstream_ubi_ckd(STREAMFILE* sf) {
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_u32(0x18,sf);
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channels);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->meta_type = meta_UBI_CKD;
@ -170,4 +176,3 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -0,0 +1,37 @@
#include "meta.h"
#include "../coding/coding.h"
#include "ubi_ckd_cwav_streamfile.h"
/* CKD RIFF - UbiArt Framework (v1) audio container [Rayman Origins (3DS)] */
VGMSTREAM* init_vgmstream_ubi_ckd_cwav(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
/* checks */
if (!is_id32be(0x00,sf, "RIFF"))
goto fail;
if (!is_id32be(0x08,sf, "WAVE"))
goto fail;
if (!(is_id32be(0x0c,sf, "dsph") || is_id32be(0x0c,sf, "cwav")))
goto fail;
/* .wav: main (unlike .wav.cdk of other versions) */
if (!check_extensions(sf,"wav,lwav"))
goto fail;
/* inside dsph (header+start, optional) and cwav (body, always) RIFF chunks is a full "CWAV",
* since dsph also contains some data just deblock */
temp_sf = setup_ubi_ckd_cwav_streamfile(sf);
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_rwsd(temp_sf);
if (!vgmstream) goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -0,0 +1,37 @@
#ifndef _UBI_CKD_CWAV_STREAMFILE_H_
#define _UBI_CKD_CWAV_STREAMFILE_H_
#include "deblock_streamfile.h"
static void block_callback(STREAMFILE *sf, deblock_io_data *data) {
uint32_t chunk_type = read_u32be(data->physical_offset + 0x00, sf);
uint32_t chunk_size = read_u32le(data->physical_offset + 0x04, sf);
if (chunk_type == get_id32be("RIFF")) {
data->data_size = 0x0;
data->skip_size = 0x0;
data->block_size = 0x0c;
}
else {
data->data_size = chunk_size;
data->skip_size = 0x08;
data->block_size = data->data_size + data->skip_size;
}
}
/* Deblocks CWAV streams inside RIFF */
static STREAMFILE* setup_ubi_ckd_cwav_streamfile(STREAMFILE* sf) {
STREAMFILE *new_sf = NULL;
deblock_config_t cfg = {0};
cfg.stream_start = 0x00;
cfg.stream_size = get_streamfile_size(sf);
cfg.block_callback = block_callback;
/* setup sf */
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
new_sf = open_fakename_streamfile_f(new_sf, NULL, "bcwav");
return new_sf;
}
#endif /* _UBI_CKD_CWAV_STREAMFILE_H_ */

View File

@ -65,7 +65,7 @@ int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) {
}
void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM* vgmstream, vgmstream_title_t* cfg) {
const char *pos;
const char* pos;
char* pos2;
char temp[1024];
@ -79,6 +79,13 @@ void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM
pos = filename;
else
pos++;
/* special case for foobar that uses a (archive)|(subfile) notation when opening a 7z/zip/etc directly */
if (cfg && cfg->remove_archive) {
const char* subpos = strchr(pos, '|');
if (subpos)
pos = subpos + 1;
}
strncpy(buf, pos, buf_len);
/* name without extension */

View File

@ -72,6 +72,7 @@ typedef struct {
int force_title;
int subsong_range;
int remove_extension;
int remove_archive;
} vgmstream_title_t;
/* get a simple title for plugins */

View File

@ -93,10 +93,10 @@ static STREAMFILE* open_stdio_streamfile_buffer_by_file(FILE *infile, const char
static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) {
size_t read_total = 0;
if (!sf->infile || !dst || length <= 0 || offset < 0)
if (/*!sf->infile ||*/ !dst || length <= 0 || offset < 0)
return 0;
//;VGM_LOG("stdio: read %lx + %x (buf %lx + %x)\n", offset, length, sf->buf_offset, sf->valid_size);
//;VGM_LOG("stdio: read %lx + %x (buf %lx + %x)\n", offset, length, sf->buf_offset, sf->valid_size, sf->buf_size);
/* is the part of the requested length in the buffer? */
if (offset >= sf->buf_offset && offset < sf->buf_offset + sf->valid_size) {
@ -124,6 +124,10 @@ static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size
}
#endif
/* possible if all data was copied to buf and FD closed */
if (!sf->infile)
return read_total;
/* read the rest of the requested length */
while (length > 0) {
size_t length_to_read;
@ -179,12 +183,15 @@ static size_t stdio_read(STDIO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size
sf->offset = offset; /* last fread offset */
return read_total;
}
static size_t stdio_get_size(STDIO_STREAMFILE* sf) {
return sf->file_size;
}
static offv_t stdio_get_offset(STDIO_STREAMFILE* sf) {
return sf->offset;
}
static void stdio_get_name(STDIO_STREAMFILE* sf, char* name, size_t name_size) {
int copy_size = sf->name_len + 1;
if (copy_size > name_size)
@ -221,7 +228,7 @@ static STREAMFILE* stdio_open(STDIO_STREAMFILE* sf, const char* const filename,
/* on failure just close and try the default path (which will probably fail a second time) */
}
#endif
// a normal open, open a new file
return open_stdio_streamfile_buffer(filename, buf_size);
}
@ -270,13 +277,29 @@ static STREAMFILE* open_stdio_streamfile_buffer_by_file(FILE* infile, const char
this_sf->file_size = 0; /* allow virtual, non-existing files */
}
/* Typically fseek(o)/ftell(o) may only handle up to ~2.14GB, signed 32b = 0x7FFFFFFF
* (happens in banks like FSB, though rarely). Should work if configured properly, log otherwise. */
/* Typically fseek(o)/ftell(o) may only handle up to ~2.14GB, signed 32b = 0x7FFFFFFF (rarely
* happens in giant banks like FSB/KTSR). Should work if configured properly using ftell_v, log otherwise. */
if (this_sf->file_size == 0xFFFFFFFF) { /* -1 on error */
vgm_logi("STREAMFILE: file size too big (report)\n");
goto fail; /* can be ignored but may result in strange/unexpected behaviors */
}
/* Rarely a TXTP needs to open *many* streamfiles = many file descriptors = reaches OS limit = error.
* Ideally should detect better and open/close as needed or reuse FDs for files that don't play at
* the same time, but it's complex since every SF is separate (would need some kind of FD manager).
* For the time being, if the file is smaller that buffer we can just read it fully and close the FD,
* that should help since big TXTP usually just need many small files.
* Doubles as an optimization as most files given will be read fully into buf on first read. */
if (this_sf->file_size && this_sf->file_size < this_sf->buf_size && this_sf->infile) {
//;VGM_LOG("stdio: fit filesize %x into buf %x\n", sf->file_size, sf->buf_size);
this_sf->buf_offset = 0;
this_sf->valid_size = fread(this_sf->buf, sizeof(uint8_t), this_sf->file_size, this_sf->infile);
fclose(this_sf->infile);
this_sf->infile = NULL;
}
return &this_sf->vt;
fail:

View File

@ -53,7 +53,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_vpk,
init_vgmstream_genh,
init_vgmstream_ogg_vorbis,
init_vgmstream_sli_ogg,
init_vgmstream_sfl_ogg,
init_vgmstream_sadb,
init_vgmstream_ps2_bmdx,
@ -152,7 +151,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_ish_isd,
init_vgmstream_gsp_gsb,
init_vgmstream_ydsp,
init_vgmstream_msvp,
init_vgmstream_ngc_ssm,
init_vgmstream_ps2_joe,
init_vgmstream_vgs,
@ -519,6 +517,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_wbk,
init_vgmstream_wbk_nslb,
init_vgmstream_dsp_apex,
init_vgmstream_ubi_ckd_cwav,
/* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */
init_vgmstream_agsc,
@ -529,6 +528,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_mic_koei,
init_vgmstream_seb,
init_vgmstream_ps2_pnb,
init_vgmstream_sli_ogg,
/* 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

@ -425,7 +425,6 @@ typedef enum {
meta_FFCC_STR, /* Final Fantasy: Crystal Chronicles */
meta_UBI_JADE, /* Beyond Good & Evil, Rayman Raving Rabbids */
meta_GCA, /* Metal Slug Anthology */
meta_MSVP, /* Popcap Hits */
meta_NGC_SSM, /* Golden Gashbell Full Power */
meta_PS2_JOE, /* Wall-E / Pixar games */
meta_NGC_YMF, /* WWE WrestleMania X8 */
@ -674,7 +673,7 @@ typedef enum {
meta_DSP_ITL, /* Charinko Hero (GC) */
meta_A2M, /* Scooby-Doo! Unmasked (PS2) */
meta_AHV, /* Headhunter (PS2) */
meta_MSV, /* Fight Club (PS2) */
meta_MSV,
meta_SDF,
meta_SVG, /* Hunter - The Reckoning - Wayward (PS2) */
meta_VIS, /* AirForce Delta Strike (PS2) */