diff --git a/README.md b/README.md index 451c81e7..f0a71746 100644 --- a/README.md +++ b/README.md @@ -121,39 +121,80 @@ standard, whose docs discuss extending M3U with arbitrary tags. ## Special cases -vgmstream aims to support most file formats as-is, but some files require extra +vgmstream aims to support most audio formats as-is, but some files require extra handling. ### Renamed files A few extensions that vgmstream supports clash with common ones. Since players -like foobar or Winamp don't react well to that, they may be renamed for -vgmstream (mainly to get looping in some cases). -- .aac to .laac -- .ac3 to .lac3 -- .aif to .aiffl or .aifcl -- .asf to .sng (EA formats) -- .flac to .lflac -- .mp4 to .lmp4 -- .ogg to .logg -- .opus to .lopus -- .stm to .lstm -- .wav to .lwav +like foobar or Winamp don't react well to that, they may be renamed to make +them playable through vgmstream. +- .aac to .laac (tri-Ace games) +- .ac3 to .lac3 (standard AC3) +- .aif to .aiffl or .aifcl (standard Mac AIF) +- .asf to .sng (EA games) +- .flac to .lflac (standard FLAC) +- .mp2 to .lmp2 (standard MP2) +- .mp3 to .lmp3 (standard MP3) +- .mp4 to .lmp4 (standard M4A) +- .mpc to .lmpc (standard MPC) +- .ogg to .logg (standard OGG) +- .opus to .lopus (standard OPUS or Switch OPUS) +- .stm to .lstm (Rockstar STM) +- .wav to .lwav (standard WAV) +- .wma to .lwma (standard WMA) +- .(any) to .vgmstream (FFmpeg formats or TXTH) Command line tools don't have this restriction and will accept the original filename. -When extracting from a bigfile sometimes internal files don't have an actual +The main advantage to rename them is that vgmstream may use the file's +internal loop info, or apply subtle fixes, but is also limited in some ways +(like standard/player's tagging). + +.vgmstream is a catch-all extension that may work as a last resort to make +a file playable. + +When extracting from a bigfile, sometimes internal files don't have an actual name+extension. Those should be renamed to its proper/common extension, as the extractor program may guess wrong (like .wav instead of .at3 or .wem). If -there is no known extension usually the header id is used instead. +there is no known extension, usually the header id string may be used instead. -### Loop assists +Note that vgmstream also accepts certain extension-less files too. + +### Demuxed videos +vgmstream also supports audio from videos, but usually must be demuxed (extracted +without modification) first, since vgmstream doesn't attempt to support them. + +The easiest way to do this is using VGMToolBox's "Video Demultiplexer" option +for common game video formats (.bik, .vp6, .pss, .pam, .pmf, .usm, .xmv, etc). + +For standard videos formats (.avi, .mp4, .webm, .m2v, .ogv, etc) not supported +by VGMToolBox FFmpeg binary may work: +- `ffmpeg.exe -i (input file) -vn -acodec copy (output file)` +Output extension may need to be adjusted to some appropriate audio file depending +on the audio codec used. ffprobe.exe can list this codec, though the correct audio +extension depends on the video itself (like .avi to .wav/mp2/mp3 or .ogv to .ogg). + +Some games use custom video formats, demuxer scripts in .bms format may be found +on the internet. + +### Companion files Some formats have companion files with external looping info, and should be left together. - .mus (playlist for .acm) -- .pos (loop info for .wav: 32 bit LE loop start sample + loop end sample) -- .sli (loop info for .ogg) -- .sfl (loop info for .ogg) -- .vgmstream + .pos (FFmpeg formats + loop assist) +- .pos (loop info for .wav, and sometimes .ogg) +- .ogg.sli or .sli (loop info for .ogg) +- .ogg.sfl (loop info for .ogg) +- .vgmstream.pos (loop info for FFmpeg formats) + - also possible for certain extensions like .lflac.pos + +Similarly some formats split header and/or data in separate files (.sgh+sgd, +.wav.str+.wav, (file)_L.dsp+(file)_R.dsp, etc). vgmstream will also detect +and use those as needed and must be tegether, even if only one of the two +will be used to play. + +.pos is a small file with 32 bit little endian values: loop start sample +and loop end sample. For FFmpeg formats (.vgmstream.pos) it may optionally +have total samples after those. ### Decryption keys Certain formats have encrypted data, and need a key to decrypt. vgmstream @@ -162,7 +203,7 @@ a companion file: - .adx: .adxkey (derived 6 byte key, in start/mult/add format) - .ahx: .ahxkey (derived 6 byte key, in start/mult/add format) - .hca: .hcakey (8 byte decryption key, a 64-bit number) - - May be followed by 2 byte AWB derivation value for newer HCA + - May be followed by 2 byte AWB scramble key for newer HCA - .fsb: .fsbkey (decryption key, in hex) The key file can be ".(ext)key" (for the whole folder), or "(name).(ext)key" @@ -182,11 +223,14 @@ Programs like VGMToolbox can help to create GENH. ".txth" or ".(ext).txth" (for the whole folder), or "(name.ext).txth" (for a single file). Contains dynamic text commands to read data from the original file, or static values. - + **TXTP**: a text playlist that works as a single song. Can contain a list of filenames to play as one (ex. "intro.vag" "loop.vag"), name with subsong index (ex. bgm.sxd#10), or mask channels to only play some (ex. "song.adx#c1,2"). +Creation of those files is meant for advanced users, docs can be found in +vgmstream source. + ## Supported codec types Quick list of codecs vgmstream supports, including many obscure ones that @@ -241,7 +285,7 @@ are used in few games. - AAC - Bink - AC3/SPDIF -- Xiph Opus (Ogg, Switch, EA, UE4) +- Xiph Opus (Ogg, Switch, EA, UE4, Exient) - Xiph CELT (FSB) - Musepack - FLAC @@ -250,7 +294,7 @@ are used in few games. Note that vgmstream doesn't (can't) reproduce in-game music 1:1, as internal resampling, filters, volume, etc, are not replicated. Some codecs are not fully accurate compared to the games due to minor bugs, but in most cases -it isn't audible. +it isn't audible. ## Supported file types @@ -494,6 +538,6 @@ This list is not complete and many other files are supported. - .ahxkey (decryption key for .ahx, in start/mult/add format) - .hcakey (decryption key for .hca, in HCA Decoder format) - .fsbkey (decryption key for .fsb, in hex) - - .vgmstream + .pos (FFmpeg formats + loop assist) + - .vgmstream + .vgmstream.pos (FFmpeg formats + loop assist) Enjoy! *hcs* diff --git a/src/coding/coding.h b/src/coding/coding.h index 9070c43f..6dcd1784 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -288,7 +288,6 @@ size_t switch_opus_get_samples(off_t offset, size_t data_size, STREAMFILE *strea size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); -size_t x_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile); #endif diff --git a/src/coding/ffmpeg_decoder_custom_opus.c b/src/coding/ffmpeg_decoder_custom_opus.c index 15707495..1203ece0 100644 --- a/src/coding/ffmpeg_decoder_custom_opus.c +++ b/src/coding/ffmpeg_decoder_custom_opus.c @@ -594,9 +594,6 @@ size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) { size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) { return custom_opus_get_encoder_delay(offset, streamFile, OPUS_EA); } -size_t x_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) { - return custom_opus_get_encoder_delay(offset, streamFile, OPUS_X); -} static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile) { diff --git a/src/formats.c b/src/formats.c index 30d15c1b..fb2fc8bd 100644 --- a/src/formats.c +++ b/src/formats.c @@ -1,11 +1,13 @@ #include "vgmstream.h" -/* defines the list of accepted extensions. vgmstream doesn't use it internally so it's here +/* Defines the list of accepted extensions. vgmstream doesn't use it internally so it's here * to inform plugins that need it. Common extensions are commented out to avoid stealing them. */ -/* some extensions require external libraries and could be #ifdef, not really needed */ -/* some formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension is not parsed */ +/* Some extensions require external libraries and could be #ifdef, not worth. */ + +/* Formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension isn't + * parsed by vgmstream and typically won't not be fully accurate. May have a .ext.pos pair for fun. */ static const char* extension_list[] = { @@ -16,20 +18,21 @@ static const char* extension_list[] = { "2pfs", "800", - //"aac", //common, also tri-Ace's - "aa3", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA) + //"aac", //common + "aa3", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA) "aaap", "aax", "abk", - //"ac3", //FFmpeg, not parsed //common? - "ace", //fake, for tri-Ace's formats (to be removed) + //"ac3", //common, FFmpeg/not parsed (AC3) + "ace", //fake extension for tri-Ace's .aac (renamed, to be removed) "acm", "ad", //txth/reserved [Xenosaga Freaks (PS2)] - "adc", //txth/reserved [Tomb Raider The Last Revelation(DC), Tomb Raider Chronicles(DC)] + "adc", //txth/reserved [Tomb Raider The Last Revelation (DC), Tomb Raider Chronicles (DC)] "adm", "adp", "adpcm", "ads", + "adw", "adx", "afc", "agsc", @@ -38,13 +41,13 @@ static const char* extension_list[] = { "ai", //"aif", //common "aifc", //common? - "aifcl", //fake extension, for AIF??? + "aifcl", //fake extension for .aif??? //"aiff", //common - "aiffl", //fake extension, for AIF??? + "aiffl", //fake extension for .aif??? "aix", "akb", "al2", - "amts", //fake extension/header id for .stm (to be removed) + "amts", //fake extension/header id for .stm (renamed? to be removed?) "ao", "apc", "as4", @@ -73,7 +76,7 @@ static const char* extension_list[] = { "bdsp", "bfstm", "bfwav", - "bfwavnsmbu", + "bfwavnsmbu", //fake extension for New Super Smash Bros U (renamed to fix bug) "bg00", "bgm", "bgw", @@ -98,7 +101,7 @@ static const char* extension_list[] = { "cbd2", "ccc", "cd", - "cfn", //fake extension/header id for .caf (to be removed) + "cfn", //fake extension for CAF (renamed, to be removed?) "ckb", "ckd", "cks", @@ -133,6 +136,7 @@ static const char* extension_list[] = { "fag", "ffw", "filp", + //"flac", //common "flx", "fsb", "fsv", @@ -163,7 +167,7 @@ static const char* extension_list[] = { "iab", "iadp", "idsp", - "idvi", //fake extension for .pcm (to be removed) + "idvi", //fake extension/header id for .pcm (renamed, to be removed) "idx", "ikm", "ild", @@ -180,30 +184,34 @@ static const char* extension_list[] = { "jstm", "kces", - "kcey", //fake extension/header id (to be removed) + "kcey", //fake extension/header id for .pcm (renamed, to be removed) "khv", "km9", - "kovs", //.kvs header id + "kovs", //fake extension/header id for .kvs "kns", "kraw", - "ktss", //.kns header id + "ktss", //fake extension/header id for .kns "kvs", "l", - "laac", //fake extension, for AAC (tri-Ace/FFmpeg) - "lac3", //fake extension, for AC3 + "laac", //fake extension for .aac (tri-Ace) + "lac3", //fake extension for .ac3, FFmpeg/not parsed "leg", - "lflac", //fake extension, FFmpeg, not parsed, use with .pos pair for fun - "lmp4", //fake extension, for MP4s - "logg", //fake extension, for OGGs - "lopus", //fake extension, for OPUS + "lflac", //fake extension for .flac, FFmpeg/not parsed + "lmp2", //fake extension for .mp2, FFmpeg/not parsed + "lmp3", //fake extension for .mp3, FFmpeg/not parsed + "lmp4", //fake extension for .mp4 + "lmpc", //fake extension for .mpc, FFmpeg/not parsed + "logg", //fake extension for .ogg + "lopus", //fake extension for .opus "lpcm", "lpk", "lps", "lse", "lsf", - "lstm", //fake extension, for STMs - "lwav", //fake extension, for WAVs + "lstm", //fake extension for .stm + "lwav", //fake extension for .wav + "lwma", //fake extension for .wma, FFmpeg/not parsed "mab", "matx", @@ -220,9 +228,10 @@ static const char* extension_list[] = { "mihb", "mnstr", "mogg", + //"mp2", //common //"mp3", //common //"mp4", //common - //"mpc", //FFmpeg, not parsed (musepack) //common + //"mpc", //common "mpdsp", "mpds", "mps", //txth/reserved [Scandal (PS2)] @@ -239,7 +248,7 @@ static const char* extension_list[] = { "mus", "musc", "musx", - "mvb", //txth/reserved [Porsche Challenge(PS1)] + "mvb", //txth/reserved [Porsche Challenge (PS1)] "mwv", "mxst", "myspd", @@ -250,14 +259,14 @@ static const char* extension_list[] = { "nlsd", "nop", "nps", - "npsf", //fake extension/header id for .nps (to be removed) + "npsf", //fake extension/header id for .nps (in bigfiles) "nus3bank", "nwa", "nxa", //"ogg", //common "ogl", - "oma", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA) + "oma", //FFmpeg/not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA) "omu", //"opus", //common "otm", @@ -274,10 +283,10 @@ static const char* extension_list[] = { "pnb", "pona", "pos", - "ps2stm", //fake extension for .stm (to be removed) + "ps2stm", //fake extension for .stm (renamed? to be removed?) "psh", // fake extension for VSV(?) Dawn of Mana needs to be checked again "psnd", - "psw", //fake extension for .wam + "psw", //fake extension for .wam (renamed, to be removed) "r", "rac", //txth/reserved [Manhunt (Xbox)] @@ -293,7 +302,7 @@ static const char* extension_list[] = { "rsd", "rsf", "rsm", - "rstm", //rsm header id + "rstm", //fake extension/header id for .rstm (in bigfiles) "rvws", "rwar", "rwav", @@ -335,7 +344,7 @@ static const char* extension_list[] = { "sli", "smc", "smp", - "smpl", //fake extension (to be removed) + "smpl", //fake extension/header id for .v0/v1 (renamed, to be removed) "smv", "snd", "snds", @@ -357,7 +366,7 @@ static const char* extension_list[] = { "ster", "sth", //"stm", //common - "stma", //fake extension (to be removed) + "stma", //fake extension/header id for .stm "str", "stream", "strm", @@ -393,7 +402,7 @@ static const char* extension_list[] = { "v0", //"v1", //dual channel with v0 - "va3", //konami atrac3, FFMPEG - DDR Supernova 2 AC + "va3", "vag", "vai", "vas", @@ -430,6 +439,7 @@ static const char* extension_list[] = { "wem", "wii", "wip", //txth/reserved [Colin McRae DiRT (PC)] + "wma", //common "wmus", "wp2", "wpd", @@ -456,7 +466,7 @@ static const char* extension_list[] = { "xss", "xvag", "xvas", - "xwav",//fake, to be removed + "xwav",//fake extension for .wav (renamed, to be removed) "xwb", "xmd", "xopus", @@ -676,6 +686,7 @@ static const layout_info layout_info_list[] = { {layout_blocked_sthd, "blocked (STHD)"}, {layout_blocked_h4m, "blocked (H4M)"}, {layout_blocked_xa_aiff, "blocked (XA AIFF)"}, + {layout_blocked_vs_ffx, "blocked (Final Fantasy X VS)"}, }; static const meta_info meta_info_list[] = { @@ -1094,8 +1105,9 @@ static const meta_info meta_info_list[] = { {meta_ADPCM_CAPCOM, "Capcom .ADPCM header"}, {meta_UE4OPUS, "Epic Games UE4OPUS header"}, {meta_XWMA, "Microsoft XWMA RIFF header"}, - {meta_VA3, "Konami / Sony ATRAC3 Header" }, - {meta_XOPUS, "Rovio XOPUS header"}, + {meta_VA3, "Konami VA3 header" }, + {meta_XOPUS, "Exient XOPUS header"}, + {meta_VS_FFX, "Final Fantasy X VS header"}, }; diff --git a/src/layout/blocked.c b/src/layout/blocked.c index fe88a5cf..abe7e595 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -205,6 +205,9 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) { case layout_blocked_xa_aiff: block_update_xa_aiff(block_offset,vgmstream); break; + case layout_blocked_vs_ffx: + block_update_vs_ffx(block_offset,vgmstream); + break; default: /* not a blocked layout */ break; } diff --git a/src/layout/blocked_vs_ffx.c b/src/layout/blocked_vs_ffx.c new file mode 100644 index 00000000..7c7c620b --- /dev/null +++ b/src/layout/blocked_vs_ffx.c @@ -0,0 +1,26 @@ +#include "layout.h" +#include "../vgmstream.h" + +/* Final Fantasy X VS headered blocks */ +void block_update_vs_ffx(off_t block_offset, VGMSTREAM * vgmstream) { + int i; + size_t block_size = 0x800; + + /* 0x00: header id + * 0x04: null + * 0x08: block number + * 0x0c: blocks left in the subfile + * 0x10: always 0x1000 + * 0x14: always 0x64 + * 0x18: null + * 0x1c: null */ + + vgmstream->current_block_offset = block_offset; + vgmstream->current_block_size = block_size - 0x20; + vgmstream->next_block_offset = block_offset + block_size; + /* 0x08: number of remaning blocks, 0x10: some id/size? (shared in all blocks) */ + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + 0x20 + 0x800*i; + } +} diff --git a/src/layout/layout.h b/src/layout/layout.h index f547d677..a1431a17 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -46,6 +46,7 @@ void block_update_ea_wve_ad10(off_t block_offset, VGMSTREAM * vgmstream); void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream); void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream); void block_update_xa_aiff(off_t block_offset, VGMSTREAM * vgmstream); +void block_update_vs_ffx(off_t block_offset, VGMSTREAM * vgmstream); /* other layouts */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index e35bbf21..660cb833 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -1190,10 +1190,14 @@ RelativePath=".\meta\ps2_voi.c" > - - + + + + @@ -1946,6 +1950,10 @@ RelativePath=".\layout\blocked_vgs.c" > + + diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 6894f9f1..02a9376c 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -391,6 +391,7 @@ + @@ -534,6 +535,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index b18770fa..74f584ab 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -742,6 +742,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files @@ -1165,6 +1168,9 @@ layout\Source Files + + layout\Source Files + layout\Source Files diff --git a/src/meta/hca_keys.h b/src/meta/hca_keys.h index e8aa7520..35b2e029 100644 --- a/src/meta/hca_keys.h +++ b/src/meta/hca_keys.h @@ -4,8 +4,8 @@ #include "hca_keys_awb.h" typedef struct { - uint64_t key; /* hca key or seed key */ - const uint16_t *subkeys; /* derivation subkey table for seed key */ + uint64_t key; /* hca key or seed ('user') key */ + const uint16_t *subkeys; /* scramble subkey table for seed key */ size_t subkeys_size; /* size of the derivation subkey table */ } hcakey_info; @@ -15,7 +15,7 @@ typedef struct { * CRI's tools expect an unsigned 64 bit number string, but keys are commonly found online in hex form. * Keys only use 56 bits though, so the upper 8 bits can be ignored. * - * ACB+AWB after mid 2018 use a master seed key + a derivation subkey in the AWB (normally 16b LE at 0x0e) + * ACB+AWB after mid 2018 use a user seed key + a scramble subkey in the AWB (normally 16b LE at 0x0e) * to create the final HCA key, which means there is one key per AWB (so most HCA have a unique key). * vgmstream derives the key if subkey table is provided. */ diff --git a/src/meta/meta.h b/src/meta/meta.h index f8b805d5..08ce7489 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -799,4 +799,6 @@ VGMSTREAM * init_vgmstream_xwma(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_xopus(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_vs_ffx(STREAMFILE * streamFile); + #endif /*_META_H*/ diff --git a/src/meta/ps2_ads.c b/src/meta/ps2_ads.c index a20aedc7..4b280dd5 100644 --- a/src/meta/ps2_ads.c +++ b/src/meta/ps2_ads.c @@ -150,10 +150,11 @@ VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) { loop_start_sample = loop_start / 2 / channel_count; is_loop_samples = 1; } - else if ((loop_start % 0x800 == 0) && loop_start > 0) {/* sector-aligned, min is 0x800 */ + else if ((loop_start % 0x800 == 0) && loop_start > 0) {/* sector-aligned, min/0 is 0x800 */ /* cavia games: loop_start is offset [Drakengard 1/2, GITS: Stand Alone Complex] */ + /* offset is absolute from the "cavia stream format" container that adjusts ADS start */ loop_flag = 1; - loop_start_offset = loop_start; + loop_start_offset = loop_start - 0x800; ignore_silent_frame_cavia = 1; } else if (loop_start % 0x800 != 0 || loop_start == 0) { /* not sector aligned */ diff --git a/src/meta/riff.c b/src/meta/riff.c index eb18e431..6da5f1b8 100644 --- a/src/meta/riff.c +++ b/src/meta/riff.c @@ -273,8 +273,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { * .adp: Headhunter (DC) * .xss: Spider-Man The Movie (Xbox) * .xsew: Mega Man X Legacy Collections (PC) - * .adpcm: Angry Birds Transformers (Android) */ - if ( check_extensions(streamFile, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm") ) { + * .adpcm: Angry Birds Transformers (Android) + * .adw: Dead Rising 2 (PC) */ + if ( check_extensions(streamFile, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw") ) { ; } else if ( check_extensions(streamFile, "mwv") ) { diff --git a/src/meta/vs_ffx.c b/src/meta/vs_ffx.c new file mode 100644 index 00000000..a7e71e94 --- /dev/null +++ b/src/meta/vs_ffx.c @@ -0,0 +1,53 @@ +#include "meta.h" +#include "../layout/layout.h" +#include "../coding/coding.h" + +/* .vs/VS - from Final Fantasy X voices (PS2) */ +VGMSTREAM * init_vgmstream_vs_ffx(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + int channel_count, loop_flag; + off_t start_offset; + + + /* checks */ + /* .vs: header id (probably ok like The Bouncer's .vs, very similar) */ + if (!check_extensions(streamFile, "vs")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x56530000) /* "VS\0\0" */ + goto fail; + + + loop_flag = 0; + channel_count = 1; + start_offset = 0x00; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_VS_FFX; + vgmstream->sample_rate = 48000; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_blocked_vs_ffx; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + + /* calc num_samples */ + { + vgmstream->next_block_offset = start_offset; + do { + block_update(vgmstream->next_block_offset,vgmstream); + vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); + } + while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset, vgmstream); + } + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/xopus.c b/src/meta/xopus.c index 63790b40..8ef98813 100644 --- a/src/meta/xopus.c +++ b/src/meta/xopus.c @@ -2,7 +2,7 @@ #include "../coding/coding.h" -/* XOPUS - from Rovio games [Angry Birds: Transformers (Android), Angry Birds: Go (Android)] */ +/* XOPUS - from Exient games [Angry Birds: Transformers (Android), Angry Birds: Go (Android)] */ VGMSTREAM * init_vgmstream_xopus(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; @@ -17,24 +17,21 @@ VGMSTREAM * init_vgmstream_xopus(STREAMFILE *streamFile) { if (read_32bitBE(0x00, streamFile) != 0x584F7075) /* "XOpu" */ goto fail; - /* 0x04: always 1? 0x30? */ + /* 0x04: always 0x01? */ channel_count = read_8bit(0x05, streamFile); /* 0x06: always 0x30? */ - /* 0x08: always 0xc8? encoder delay? */ + /* 0x08: always 0xc8? max allowed packet size? */ num_samples = read_32bitLE(0x0c, streamFile); - /* 0x10: always 0x138? max packet size? */ - entries = read_32bitLE(0x14, streamFile); /* packet sizes */ - /* 0x18: data size */ + skip = read_32bitLE(0x10, streamFile); + entries = read_32bitLE(0x14, streamFile); + data_size = read_32bitLE(0x18, streamFile); /* 0x1c: unused */ /* 0x20+: packet sizes table */ - sample_rate = 48000; loop_flag = 0; start_offset = 0x20 + 0x02*entries; - data_size = get_streamfile_size(streamFile) - start_offset; - skip = x_opus_get_encoder_delay(start_offset, streamFile); /* build the VGMSTREAM */ @@ -43,7 +40,7 @@ VGMSTREAM * init_vgmstream_xopus(STREAMFILE *streamFile) { vgmstream->meta_type = meta_XOPUS; vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples /*- skip*/; + vgmstream->num_samples = num_samples; #ifdef VGM_USE_FFMPEG { diff --git a/src/vgmstream.c b/src/vgmstream.c index 4a6b4676..9bda15d1 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -444,7 +444,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ue4opus, init_vgmstream_xwma, init_vgmstream_xopus, - + init_vgmstream_vs_ffx, /* 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 */ @@ -1018,6 +1018,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre case layout_blocked_sthd: case layout_blocked_h4m: case layout_blocked_xa_aiff: + case layout_blocked_vs_ffx: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_aix: diff --git a/src/vgmstream.h b/src/vgmstream.h index 15bb12b0..3a1d2ec6 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -253,6 +253,7 @@ typedef enum { layout_blocked_sthd, /* Dream Factory STHD */ layout_blocked_h4m, /* H4M video */ layout_blocked_xa_aiff, /* XA in AIFF files [Crusader: No Remorse (SAT), Road Rash (3DO)] */ + layout_blocked_vs_ffx, /* otherwise odd */ layout_aix, /* CRI AIX's wheels within wheels */ @@ -701,6 +702,7 @@ typedef enum { meta_XWMA, meta_VA3, /* DDR Supernova 2 AC */ meta_XOPUS, + meta_VS_FFX, } meta_t;