Merge pull request #879 from bnnm/misc3

- Fix some .vgs [Ishikura Noboru no Igo Kouza: Chuukyuuhen (PS2)]
- Fix .wvs glitches [Metal Arms (GC)]
- Add ADX keycodes
- Enable .bfstm region subsongs for testing (via .txtp)
- Cleanup
This commit is contained in:
bnnm 2021-06-26 00:49:50 +02:00 committed by GitHub
commit deeb0b4eb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 585 additions and 548 deletions

100
README.md
View File

@ -210,7 +210,7 @@ https://github.com/vgmstream/vgmstream/tree/master/cli/tools/txtp_maker.py
Put in the same dir as test.exe/vgmstream_cli, then to drag-and-drop files with
subsongs to `txtp_maker.py` (it has CLI options to control output too).
### Renamed files
### Common and unknown extensions
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 to make
them playable through vgmstream.
@ -219,7 +219,7 @@ them playable through vgmstream.
- `.aif` to `.laif` (standard Mac AIF, Asobo AIF, Ogg)
- `.aiff/aifc` to `.laiffl/laifc` (standard Mac AIF)
- `.asf` to `.lasf` (EA games, Argonaut ASF)
- `.bin` to `.lbin` (various)
- `.bin` to `.lbin` (various formats)
- `.flac` to `.lflac` (standard FLAC)
- `.mp2` to `.lmp2` (standard MP2)
- `.mp3` to `.lmp3` (standard MP3)
@ -228,34 +228,41 @@ them playable through vgmstream.
- `.ogg` to `.logg` (standard OGG)
- `.opus` to `.lopus` (standard OPUS or Switch OPUS)
- `.stm` to `.lstm` (Rockstar STM)
- `.wav` to `.lwav` (standard WAV)
- `.wav` to `.lwav` (standard WAV, various formats)
- `.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.
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.
The main advantage of renaming here is that vgmstream may use the file's internal
loop info, or apply subtle fixes, but is also limited in some ways (like ignoring
standard tags). `.vgmstream` is a catch-all extension that may work as a last resort
to make a file playable.
Some plugins have options that allow any extension (common or unknown) to be
played, making renaming unnecessary. You may need to adjust plugin priority in
player's options first.
Some plugins have options that allow common extensions to be played, making any
renaming unnecessary. You may need to adjust plugin priority in player's options
first. Note that vgmstream also accepts certain extension-less files as-is too.
Similarly, vgmstream has a curated list of known extensions, that plugins may take
into account and ignore unknowns. Through *TXTH* you can make unknown files playable,
but you also need to either rename or set plugin options to allow "unknown extensions"
(or, preferably, report this new extension so it can be added to the known list).
It's also possible to make a .txtp file that opens files with those common/unknown
extensions as a way to force them into vgmstream without renaming.
#### Related issues
Also be aware that other plugins (not vgmstream's) can tell the player they
handle some extension, then not actually play it. This makes the file unplayable
as vgmstream doesn't even get the chance to parse that file, so you may need to
disable the offending plugin or rename the file (for example this may happen with
.asf and foobar2000).
`.asf` in foobar2000).
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 string may be used instead.
Note that vgmstream also accepts certain extension-less files too.
When extracting from a bigfile, sometimes internal files don't have an proper
extension. Those should be renamed to its correct one when possible, as the
extractor program may guess wrong (like `.wav` instead of `.at3` or `.wem`).
If there is no known extension, usually the header id/magic string may be used instead.
### Demuxed videos
vgmstream also supports audio from videos, but usually must be demuxed (extracted
@ -282,7 +289,6 @@ Some formats have companion files with external info, that should be left togeth
- `.ogg.sfl` : loop info for `.ogg`
- `.opus.sli`: loop info for `.opus`
- `.pos`: loop info for .wav
- `.vgmstream.pos`: loop info for FFmpeg formats
- `.acb`: names for `.awb`
- `.xsb`: names for `.xwb`
@ -303,6 +309,7 @@ Similarly some formats split header+body data in separate files, examples:
- `.wav.str`+`.wav`
- `.wav`+`.dcs`
- `.wbh`+`.wbd`
Both are needed to play and must be together. The usual rule is you open the
bigger file (body), save a few formats where the smaller (header) file is opened
instead for technical reasons (mainly some bank formats).
@ -370,7 +377,7 @@ dynamically during gameplay, or looping metadata is stored externally.
Cases like those can be supported using an artificial files with info vgmstream
needs.
Creation of these files is meant for advanced users, docs can be found in
Creation of these files is meant for advanced users, full docs can be found in
vgmstream source.
#### GENH
@ -389,6 +396,17 @@ file, or static values. This allows vgmstream to play unsupported formats.
*TXTH* is recommended over *GENH* as it's far easier to create and has many
more functions, plus doesn't modify original data.
Usage example (used when opening an unknown file named `bgm_01.pcm`):
**.pcm.txth**
```
codec = PCM16LE
channels = @0x04 #in the file, at offset 4
sample_rate = 48000 #hardcoded
start_offset = 0x10
num_samples = data_size #auto
```
#### TXTP
Text files with player configuration, named `(name).txtp`.
@ -397,14 +415,38 @@ and non-standard ways, like playing multiple small songs as a single
one, or using some channels as a section of the song. For those cases we
can create a *TXTP* file to customize how vgmstream handles songs.
Text inside `.txtp` can contain a list of filenames to play as one (ex.
`intro.vag(line)loop.vag`), a list of single-channel files to join as a single
multichannel file, subsong index (ex. `bgm.sxd#10`), per-file configurations like
number of loops, remove unneeded channels, force looping, and many other features.
Text inside `.txtp` can contain a list of filenames to play as one, a list of
single-channel files to join as a single multichannel file, subsong index,
per-file configurations like number of loops, remove unneeded channels,
force looping, and many other features.
Usage examples (open directly, name can be set freely):
**bgm01-full.txtp**
```
# plays 2 files as a single one
bgm01_intro.vag
bgm01_loop.vag
loop_mode = auto
```
**bgm-subsong10.txtp**
```
# plays subsong number 10
bgm.sxd#10
```
**song01-looped.txtp**
```
# force looping an .mp3 from 10 seconds up to file end
song02.mp3 #I 10.0
```
**music01-demux2.txtp**
```
# plays channels 3 and 4 only, removes rest
music01.bfstm #C3,4
```
For example, to force looping `bgm01.mp3`, make `bgm01-loop.txtp` and inside
write `bgm01.mp3 #I 10.0 90.0`. Open the `.txtp` and vgmstream will loop that
`.mp3` from 10 to 90 seconds.
#### TXTM
A text file named `.txtm` for some formats with companion files. It lists
@ -414,11 +456,11 @@ It is needed for formats where name combos are hardcoded, so vgmstream doesn't
know which companion file(s) to load if its name doesn't match the main file.
Note that companion file order is usually important.
Usage example:
Usage example (used when opening files in the left part of the list):
```
# Harry Potter and the Chamber of Secrets (PS2)
entrance.mpf:entrance.mus,entrance_o.mus
willow.mpf:willow.mus,willow_o.mus
entrance.mpf: entrance.mus,entrance_o.mus
willow.mpf: willow.mus,willow_o.mus
```
```
# Metal Gear Solid: Snake Eater 3D (3DS) names for .awb

View File

@ -1019,8 +1019,7 @@ static const meta_info meta_info_list[] = {
{meta_PS2_ENTH, ".enth Header"},
{meta_SDT, "High Voltage .sdt header"},
{meta_NGC_TYDSP, ".tydsp Header"},
{meta_XBOX_WVS, "Metal Arms WVS Header (XBOX)"},
{meta_NGC_WVS, "Metal Arms WVS Header (GameCube)"},
{meta_WVS, "Swingin' Ape .WVS header"},
{meta_XBOX_MATX, "assumed Matrix file by .matx extension"},
{meta_DEC, "Falcom DEC RIFF header"},
{meta_VS, "Melbourne House .VS header"},
@ -1151,9 +1150,9 @@ static const meta_info meta_info_list[] = {
{meta_PS3_PAST, "SNDP header"},
{meta_SGXD, "Sony SGXD header"},
{meta_WII_RAS, "RAS header"},
{meta_PS2_SPM, "SPM header"},
{meta_SPM, "Square SPM header"},
{meta_X360_TRA, "Terminal Reality .TRA raw header"},
{meta_PS2_VGS, "Princess Soft VGS header"},
{meta_VGS_PS, "Princess Soft VGS header"},
{meta_PS2_IAB, "Runtime .IAB header"},
{meta_VS_STR, "Square .VS STR* header"},
{meta_LSF_N1NJ4N, ".lsf !n1nj4n header"},

View File

@ -1231,7 +1231,7 @@
>
</File>
<File
RelativePath=".\meta\ps2_ads.c"
RelativePath=".\meta\ads.c"
>
</File>
<File
@ -1431,7 +1431,7 @@
>
</File>
<File
RelativePath=".\meta\ps2_spm.c"
RelativePath=".\meta\spm.c"
>
</File>
<File
@ -1475,7 +1475,7 @@
>
</File>
<File
RelativePath=".\meta\ps2_vgs.c"
RelativePath=".\meta\vgs_ps.c"
>
</File>
<File

View File

@ -232,7 +232,7 @@
<ClCompile Include="meta\ps2_iab.c" />
<ClCompile Include="meta\mss.c" />
<ClCompile Include="meta\mtaf.c" />
<ClCompile Include="meta\ps2_spm.c" />
<ClCompile Include="meta\spm.c" />
<ClCompile Include="meta\rad.c" />
<ClCompile Include="meta\sbk.c" />
<ClCompile Include="meta\tgc.c" />
@ -435,7 +435,7 @@
<ClCompile Include="meta\ppst.c" />
<ClCompile Include="meta\ps2_vds_vdm.c" />
<ClCompile Include="meta\ps2_adm.c" />
<ClCompile Include="meta\ps2_ads.c" />
<ClCompile Include="meta\ads.c" />
<ClCompile Include="meta\ps2_ass.c" />
<ClCompile Include="meta\ps2_ast.c" />
<ClCompile Include="meta\aus.c" />
@ -485,7 +485,7 @@
<ClCompile Include="meta\vag.c" />
<ClCompile Include="meta\ps2_vas.c" />
<ClCompile Include="meta\ps2_vbk.c" />
<ClCompile Include="meta\ps2_vgs.c" />
<ClCompile Include="meta\vgs_ps.c" />
<ClCompile Include="meta\ps2_vgv.c" />
<ClCompile Include="meta\ps2_vms.c" />
<ClCompile Include="meta\ps2_voi.c" />

View File

@ -805,7 +805,7 @@
<ClCompile Include="meta\ps2_adm.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_ads.c">
<ClCompile Include="meta\ads.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_ass.c">
@ -955,7 +955,7 @@
<ClCompile Include="meta\ps2_vbk.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_vgs.c">
<ClCompile Include="meta\vgs_ps.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_vgv.c">
@ -1588,7 +1588,7 @@
<ClCompile Include="meta\mtaf.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_spm.c">
<ClCompile Include="meta\spm.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\vs_str.c">

View File

@ -3,8 +3,8 @@
typedef struct {
uint16_t start,mult,add; /* XOR values derived from the actual key */
char* key8; /* keystring used by type 8 encryption */
uint16_t start, mult, add; /* XOR values derived from the actual key */
const char* key8; /* keystring used by type 8 encryption */
uint64_t key9; /* keycode used by type 9 encryption */
} adxkey_info;
@ -20,7 +20,7 @@ static const adxkey_info adxkey8_list[] = {
{0x49e1,0x4a57,0x553d, "karaage",0},
/* Blood+ (PS2) [Grasshopper Manufacture] */
{0x5f5d,0x58bd,0x55ed, "LOVELOVE",0}, // obfuscated keystring is "KNUDKNUD", adds +1 to chars to get final key
{0x5f5d,0x58bd,0x55ed, "LOVELOVE",0}, // obfuscated keystring is "KNUDKNUD", adds +1 to chars to get final key
/* Killer7 (PS2) [Grasshopper Manufacture] */
{0x50fb,0x5803,0x5701, "GHM",0},
@ -70,11 +70,11 @@ static const adxkey_info adxkey8_list[] = {
/* Nogizaka Haruka no Himitsu: Cosplay Hajimemashita (PS2) [Vridge] */
{0x45af,0x5f27,0x52b1, "SKFHSIA",0},
/* Little Anchor (PS2) [D3 Publisher] */
{0x5f65,0x5b3d,0x5f65, NULL,0}, // confirmed unique with guessadx
/* Little Anchor (PS2) [Vridge] */
{0x5f65,0x5b3d,0x5f65, "KHNUJYTG",0},
/* Hanayoi Romanesque: Ai to Kanashimi (PS2) [Marvelous] */
{0x5563,0x5047,0x43ed, NULL,0}, // 2nd from guessadx, other was {0x5562,0x5047,0x1433}
{0x5563,0x5047,0x43ed, "HANAOTM",0},
/* Mobile Suit Gundam: Gundam vs. Gundam NEXT PLUS (PSP) [Capcom] */
{0x4f7b,0x4fdb,0x5cbf, "CS-GGNX+",0},
@ -83,10 +83,10 @@ static const adxkey_info adxkey8_list[] = {
{0x4f7b,0x5071,0x4c61, "ELEMENGAL",0},
/* Rakushou! Pachi-Slot Sengen 6: Rio 2 Cruising Vanadis (PS2) [Net Corporation] */
{0x53e9,0x586d,0x4eaf, NULL,0}, // confirmed unique with guessadx
{0x53e9,0x586d,0x4eaf, "waksde",0},
/* Tears to Tiara Gaiden Avalon no Nazo (PS3) [Aquaplus] */
{0x47e1,0x60e9,0x51c1, NULL,0}, // confirmed unique with guessadx
/* Tears to Tiara Gaiden: Avalon no Nazo (PS3) [Aquaplus] */
{0x47e1,0x60e9,0x51c1, "Hello TtT world!",0}, // obfuscated keystring xors 0xF0 to chars to get final key
/* Neon Genesis Evangelion: Koutetsu no Girlfriend 2nd (PS2) [Broccoli] */
{0x481d,0x4f25,0x5243, "eva2",0},
@ -101,25 +101,25 @@ static const adxkey_info adxkey8_list[] = {
{0x5f5d,0x552b,0x5507, "DATAM-KK2",0},
/* Sakura Taisen: Atsuki Chishio ni (PS2) [Sega] */
{0x645d,0x6011,0x5c29, NULL,0}, // possible key: "[Seq][ADX] illegal cri or libsd status."
{0x645d,0x6011,0x5c29, NULL,0}, // keystring may be printf'd "%08X" + number (unlikely key: "[Seq][ADX] illegal cri or libsd status.")
/* Sakura Taisen Monogatari: Mysterious Paris (PS2) [Sega] */
{0x62ad,0x4b13,0x5957, "inoue4126",0},
/* Sotsugyou 2nd Generation (PS2) [Jinx] */
{0x6305,0x509f,0x4c01, NULL,0}, // First guess from guessadx, other was {0x6307,0x509f,0x2ac5}
{0x6305,0x509f,0x4c01, "MUSUMEG",0},
/* Kin'iro no Corda -La Corda d'Oro- (PSP) [Koei] */
{0x55b7,0x67e5,0x5387, NULL,0}, // keystring not in ELF?
{0x55b7,0x67e5,0x5387, "neo3corda",0}, // keystring as code, char by char
/* Nanatsuiro * Drops Pure!! (PS2) [Media Works] */
{0x6731,0x645d,0x566b, NULL,0}, // confirmed unique with guessadx
{0x6731,0x645d,0x566b, "NANAT",0},
/* Shakugan no Shana (PS2) [Vridge] */
{0x5fc5,0x63d9,0x599f, "FUZETSU",0},
/* Uragiri wa Boku no Namae o Shitteiru (PS2) [Kadokawa Shoten] */
{0x4c73,0x4d8d,0x5827, NULL,0}, // confirmed unique with guessadx
{0x4c73,0x4d8d,0x5827, "URABOKU-penguin",0},
/* StormLover!! (PSP), StormLover Kai!! (PSP) [Vridge] */
{0x5a11,0x67e5,0x6751, "HEXDPFMDKPQW",0}, /* unknown AHX key */
@ -261,7 +261,7 @@ static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list
static const int adxkey9_list_count = sizeof(adxkey9_list) / sizeof(adxkey9_list[0]);
/* preloaded list used to derive keystrings from ADX_Decoder (see VGAudio for how to calculate) */
/* preloaded list used to derive keystrings from ADX_Decoder, found in executables (see VGAudio for how to calculate) */
static const uint16_t key8_primes[0x400] = {
0x401B,0x4021,0x4025,0x402B,0x4031,0x403F,0x4043,0x4045,0x405D,0x4061,0x4067,0x406D,0x4087,0x4091,0x40A3,0x40A9,
0x40B1,0x40B7,0x40BD,0x40DB,0x40DF,0x40EB,0x40F7,0x40F9,0x4109,0x410B,0x4111,0x4115,0x4121,0x4133,0x4135,0x413B,
@ -329,13 +329,15 @@ static const uint16_t key8_primes[0x400] = {
0x6779,0x6781,0x6785,0x6791,0x67AB,0x67BD,0x67C1,0x67CD,0x67DF,0x67E5,0x6803,0x6809,0x6811,0x6817,0x682D,0x6839,
};
static void derive_adx_key8(const char * key8, uint16_t * out_start, uint16_t * out_mult, uint16_t * out_add) {
static void derive_adx_key8(const char* key8, uint16_t* p_start, uint16_t* p_mult, uint16_t* p_add) {
size_t key_size;
uint16_t start = 0, mult = 0, add = 0;
int i;
if (key8 == NULL || key8[0] == '\0')
if (key8 == NULL || key8[0] == '\0') /* strlen >= 1 */
goto end;
/* calcs as found in exes, though there is some unrolling in the original code */
key_size = strlen(key8);
start = key8_primes[0x100];
mult = key8_primes[0x200];
@ -349,9 +351,9 @@ static void derive_adx_key8(const char * key8, uint16_t * out_start, uint16_t *
}
end:
*out_start = start;
*out_mult = mult;
*out_add = add;
*p_start = start;
*p_mult = mult;
*p_add = add;
}

View File

@ -1,48 +1,51 @@
#include "meta.h"
#include "../coding/coding.h"
/* AHV - from Amuze games [Headhunter (PS2)] */
VGMSTREAM * init_vgmstream_ahv(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t data_size, channel_size, interleave;
int loop_flag, channel_count;
/* checks (.ahv: from names in bigfile) */
if ( !check_extensions(streamFile,"ahv") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x41485600) /* "AHV\0" */
goto fail;
start_offset = 0x800;
data_size = get_streamfile_size(streamFile) - start_offset;
interleave = read_32bitLE(0x10,streamFile);
channel_count = (interleave != 0) ? 2 : 1;
channel_size = read_32bitLE(0x08,streamFile);
loop_flag = 0;
/* VAGp header after 0x14 */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_AHV;
vgmstream->sample_rate = read_32bitLE(0x0c,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(channel_size,1);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
if (interleave)
vgmstream->interleave_last_block_size = (data_size % (interleave*channel_count)) / channel_count;
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"
/* AHV - from Amuze games [Headhunter (PS2)] */
VGMSTREAM* init_vgmstream_ahv(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
size_t data_size, channel_size, interleave, sample_rate;
int loop_flag, channels;
/* checks */
/* .ahv: from names in bigfile */
if (!check_extensions(sf,"ahv"))
goto fail;
if (!is_id32be(0x00,sf, "AHV\0"))
goto fail;
start_offset = 0x800;
data_size = get_streamfile_size(sf) - start_offset;
channel_size = read_u32le(0x08,sf);
sample_rate = read_32bitLE(0x0c,sf);
interleave = read_u32le(0x10,sf);
channels = (interleave != 0) ? 2 : 1;
loop_flag = 0;
/* VAGp header after 0x14 */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_AHV;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
if (interleave)
vgmstream->interleave_last_block_size = (data_size % (interleave*channels)) / channels;
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -3,22 +3,21 @@
/* Regions seem mostly for in-game purposes and are not very listenable on its own.
* Also, sample start is slightly off since vgmstream can't start in the middle of block ATM.
* Also, sample start is slightly off since vgmstream can't start in the middle of a frame ATM.
* Otherwise this kinda works, but for now it's just a test. */
#define BFSTM_ENABLE_REGION_SUBSONGS 0
#define BFSTM_ENABLE_REGION_FORCE_LOOPS 0 /* this makes sense in SM3D World, but not in Zelda BotW) */
#define BFSTM_ENABLE_REGION_SUBSONGS 1
#if BFSTM_ENABLE_REGION_SUBSONGS
static off_t bfstm_set_regions(STREAMFILE *streamFile, VGMSTREAM *vgmstream, int region_count, off_t regn_offset, int codec, int big_endian);
static off_t bfstm_set_regions(STREAMFILE* sf, VGMSTREAM* vgmstream, int region_count, off_t regn_offset, int codec, int big_endian);
#endif
/* BFSTM - Nintendo Wii U format */
VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
/* BFSTM - Nintendo Wii U/Switch format */
VGMSTREAM* init_vgmstream_bfstm(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
off_t info_offset = 0, data_offset = 0;
int channel_count, loop_flag, codec;
int channels, loop_flag, codec;
int big_endian;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
@ -29,20 +28,20 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
/* checks */
if ( !check_extensions(streamFile,"bfstm") )
if (!check_extensions(sf,"bfstm"))
goto fail;
/* FSTM header */
if (read_32bitBE(0x00, streamFile) != 0x4653544D) /* "FSTM" */
if (!is_id32be(0x00,sf, "FSTM"))
goto fail;
/* 0x06(2): header size (0x40), 0x08: version (0x00000400), 0x0c: file size */
/* 0x06(2): header size (0x40)
* 0x08: version (0x00000400)
* 0x0c: file size */
/* check BOM */
if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFEFF) { /* Wii U games */
if (read_u16be(0x04, sf) == 0xFEFF) { /* Wii U games */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
big_endian = 1;
} else if ((uint16_t)read_16bitBE(0x04, streamFile) == 0xFFFE) { /* Switch games */
} else if (read_u16be(0x04, sf) == 0xFFFE) { /* Switch games */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
big_endian = 0;
@ -53,18 +52,18 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
/* get sections (should always appear in the same order) */
{
int i;
int section_count = read_16bit(0x10, streamFile);
int section_count = read_16bit(0x10, sf);
for (i = 0; i < section_count; i++) {
/* 0x00: id, 0x02(2): padding, 0x04(4): offset, 0x08(4): size */
uint16_t section_id = read_16bit(0x14 + i*0xc+0x00, streamFile);
uint16_t section_id = read_16bit(0x14 + i*0xc+0x00, sf);
switch(section_id) {
case 0x4000: info_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break;
case 0x4001: /* seek_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); */ break;
case 0x4002: data_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break;
case 0x4000: info_offset = read_32bit(0x14+i*0x0c+0x04, sf); break;
case 0x4001: /* seek_offset = read_32bit(0x14+i*0x0c+0x04, sf); */ break;
case 0x4002: data_offset = read_32bit(0x14+i*0x0c+0x04, sf); break;
#if BFSTM_ENABLE_REGION_SUBSONGS
case 0x4003: regn_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break;
case 0x4003: regn_offset = read_32bit(0x14+i*0x0c+0x04, sf); break;
#endif
case 0x4004: /* pdat_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); */ break; /* prefetch data */
case 0x4004: /* pdat_offset = read_32bit(0x14+i*0x0c+0x04, sf); */ break; /* prefetch data */
default:
break;
}
@ -75,13 +74,13 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
}
/* INFO section */
if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */
if (read_32bitBE(info_offset, sf) != 0x494E464F) /* "INFO" */
goto fail;
codec = read_8bit(info_offset + 0x20, streamFile);
loop_flag = read_8bit(info_offset + 0x21, streamFile);
channel_count = read_8bit(info_offset + 0x22, streamFile);
codec = read_8bit(info_offset + 0x20, sf);
loop_flag = read_8bit(info_offset + 0x21, sf);
channels = read_8bit(info_offset + 0x22, sf);
#if BFSTM_ENABLE_REGION_SUBSONGS
region_count = read_8bit(info_offset + 0x23, streamFile);
region_count = read_8bit(info_offset + 0x23, sf);
#endif
@ -89,18 +88,18 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bit(info_offset + 0x24, streamFile);
vgmstream->num_samples = read_32bit(info_offset + 0x2c, streamFile);
vgmstream->loop_start_sample = read_32bit(info_offset + 0x28, streamFile);
vgmstream->sample_rate = read_32bit(info_offset + 0x24, sf);
vgmstream->num_samples = read_32bit(info_offset + 0x2c, sf);
vgmstream->loop_start_sample = read_32bit(info_offset + 0x28, sf);
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->meta_type = meta_FSTM;
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = read_32bit(info_offset + 0x34, streamFile);
vgmstream->interleave_last_block_size = read_32bit(info_offset + 0x44, streamFile);
vgmstream->layout_type = (channels == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = read_32bit(info_offset + 0x34, sf);
vgmstream->interleave_last_block_size = read_32bit(info_offset + 0x44, sf);
switch(codec) {
case 0x00:
@ -116,13 +115,13 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
int i, c;
off_t channel_indexes, channel_info_offset, coefs_offset;
channel_indexes = info_offset+0x08 + read_32bit(info_offset + 0x1C, streamFile);
channel_indexes = info_offset+0x08 + read_32bit(info_offset + 0x1C, sf);
for (i = 0; i < vgmstream->channels; i++) {
channel_info_offset = channel_indexes + read_32bit(channel_indexes+0x04+(i*0x08)+0x04, streamFile);
coefs_offset = channel_info_offset + read_32bit(channel_info_offset+0x04, streamFile);
channel_info_offset = channel_indexes + read_32bit(channel_indexes+0x04+(i*0x08)+0x04, sf);
coefs_offset = channel_info_offset + read_32bit(channel_info_offset+0x04, sf);
for (c = 0; c < 16; c++) {
vgmstream->ch[i].adpcm_coef[c] = read_16bit(coefs_offset + c*2, streamFile);
vgmstream->ch[i].adpcm_coef[c] = read_16bit(coefs_offset + c*2, sf);
}
}
}
@ -134,10 +133,10 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
#if BFSTM_ENABLE_REGION_SUBSONGS
start_offset += bfstm_set_regions(streamFile, vgmstream, region_count, regn_offset, codec, big_endian);
start_offset += bfstm_set_regions(sf, vgmstream, region_count, regn_offset, codec, big_endian);
#endif
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
goto fail;
return vgmstream;
@ -151,17 +150,16 @@ fail:
/* Newer .bfstm may have multiple regions, that are sample sections of some meaning,
* like loop parts (Super Mario 3D World), or dynamic subsongs (Zelda: BotW)
* We'll hack them in as subsongs (though seem mostly activated by game events) */
static off_t bfstm_set_regions(STREAMFILE *streamFile, VGMSTREAM *vgmstream, int region_count, off_t regn_offset, int codec, int big_endian) {
static off_t bfstm_set_regions(STREAMFILE* sf, VGMSTREAM* vgmstream, int region_count, off_t regn_offset, int codec, int big_endian) {
off_t start_offset;
size_t stream_size;
int total_subsongs, target_subsong = streamFile->stream_index;
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE;
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitLE : read_16bitLE;
int total_subsongs, target_subsong = sf->stream_index;
int32_t (*read_s32)(off_t,STREAMFILE*) = big_endian ? read_s32be : read_s32le;
int16_t (*read_s16)(off_t,STREAMFILE*) = big_endian ? read_s16be : read_s16le;
if (region_count <= 0 && regn_offset == 0 && codec != 0x02)
goto fail;
if (read_32bitBE(regn_offset, streamFile) != 0x5245474E) /* "REGN" */
if (!is_id32be(regn_offset, sf, "REGN"))
goto fail;
/* pretend each region is a subsong, but use first subsong as the whole file,
@ -171,61 +169,74 @@ static off_t bfstm_set_regions(STREAMFILE *streamFile, VGMSTREAM *vgmstream, int
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
if (target_subsong > 1) {
int sample_aligned = 0, sample_skip = 0;
int i;
off_t region_start, region_end;
size_t block_size;
size_t sample_start = read_32bit(regn_offset + 0x20 + (target_subsong-2)*0x100+0x00, streamFile);
size_t sample_end = read_32bit(regn_offset + 0x20 + (target_subsong-2)*0x100+0x04, streamFile) + 1;
off_t adpcm_offset = regn_offset + 0x20 + (target_subsong-2)*0x100+0x08;
int ch;
off_t region_start, region_end, block_size;
/* target region info */
int32_t sample_start = read_s32(regn_offset + 0x20 + (target_subsong-2)*0x100 + 0x00, sf);
int32_t sample_end = read_s32(regn_offset + 0x20 + (target_subsong-2)*0x100 + 0x04, sf) + 1;
off_t adpcm_offset = regn_offset + 0x20 + (target_subsong-2)*0x100 + 0x08;
/* rest is padding up to 0x100 */
/* samples-to-bytes, approximate since samples could land in the middle of a 0x08 frame */
/* samples-to-bytes, approximate since regions' samples can land in the middle of a 0x08 frame */
if (sample_start % 14) {
/* can't decode in the middle of a nibble ATM so align to frame */
//VGM_LOG("BFSTM: sample align %i, %i\n", sample_start % 14, sample_end % 14);
sample_start -= sample_start % 14;
sample_end -= sample_start % 14;
}
region_start = sample_start / 14 * vgmstream->channels * 0x08;
region_end = sample_end / 14 * vgmstream->channels * 0x08;
stream_size = region_end - region_start;
//;VGM_LOG("BFSTM: region offset start=%lx, end=%lx\n", region_start, region_end);
/* align to block start or interleave causes funny sounds, but the bigger the interleave
* the less accurate this is (with 0x2000 align can be off by ~4600 samples per channel) */
//todo could be fixed with interleave_first_block
block_size = (vgmstream->interleave_block_size*vgmstream->channels);
/* adjust region to closest block + use interleave first to correctly skip to first sample */
block_size = (vgmstream->interleave_block_size * vgmstream->channels);
if (region_start % block_size) {
region_start -= region_start % block_size; /* now aligned */
//;VGM_LOG("BFSTM: new region start=%lx\n", region_start);
off_t region_skip = (region_start % block_size);
//;VGM_LOG("BFSTM: new region start=%lx - %lx\n", region_start, region_skip);
/* get position of our block (close but smaller than sample_start) */
sample_aligned = dsp_bytes_to_samples(region_start, vgmstream->channels);
/* and how many samples to skip until actual sample_start */
sample_skip = (sample_start - sample_aligned);
/* use interleave first to skip to correct offset */
vgmstream->interleave_first_block_size = (block_size - region_skip) / vgmstream->channels;
vgmstream->interleave_first_skip = (region_skip) / vgmstream->channels;
region_start = region_start - region_skip + vgmstream->interleave_first_skip; /* now aligned */
}
//;VGM_LOG("BFSTM: region align=%i, skip=%i, start=%i, end=%i\n", sample_aligned, sample_skip, sample_start, sample_end);
//;VGM_LOG("BFSTM: region=%lx, adpcm=%lx, start=%i, end=%i\n", region_start, adpcm_offset, sample_start, sample_end);
start_offset = region_start;
if (sample_end != vgmstream->num_samples) /* not exact but... */
vgmstream->interleave_last_block_size = 0;
/* sample_end doesn't fall in last block, interleave last doesn't apply */
{
int32_t block_samples = dsp_bytes_to_samples(block_size, vgmstream->channels);
int32_t samples_align = vgmstream->num_samples / block_samples * block_samples;
if (sample_end < samples_align)
vgmstream->interleave_last_block_size = 0;
}
vgmstream->num_samples = sample_skip + (sample_end - sample_start);
vgmstream->loop_start_sample = sample_skip;
vgmstream->num_samples = /*sample_skip +*/ (sample_end - sample_start);
#if 0
/* this makes sense in SM3D World, but not in Zelda BotW */
vgmstream->loop_start_sample = 0;;
vgmstream->loop_end_sample = vgmstream->num_samples;
#if BFSTM_ENABLE_REGION_FORCE_LOOPS
vgmstream_force_loop(vgmstream, 1, vgmstream->loop_start_sample, vgmstream->loop_end_sample);
#endif
/* maybe loops should be disabled with some regions? */
/* this won't make sense after aligning, whatevs, doesn't sound too bad */
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].adpcm_history1_16 = read_16bit(adpcm_offset+0x02+0x00, streamFile);
vgmstream->ch[i].adpcm_history2_16 = read_16bit(adpcm_offset+0x02+0x02, streamFile);
/* region_start points to correct frame (when compared to ADPCM predictor), but not sure if hist
* is for exact nibble rather than first (sounds ok though) */
for (ch = 0; ch < vgmstream->channels; ch++) {
/* 0x00: ADPCM predictor */
vgmstream->ch[ch].adpcm_history1_16 = read_s16(adpcm_offset + 0x02 + 0x06*ch, sf);
vgmstream->ch[ch].adpcm_history2_16 = read_s16(adpcm_offset + 0x04 + 0x06*ch, sf);
}
}
else {
start_offset = 0;
stream_size = get_streamfile_size(streamFile);
stream_size = get_streamfile_size(sf);
}
vgmstream->num_streams = total_subsongs;
/* for now don't show subsongs since some regions are a bit strange */
//vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
return start_offset;

View File

@ -264,8 +264,8 @@ VGMSTREAM * init_vgmstream_ngc_tydsp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_capdsp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xbox_wvs(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_wvs(STREAMFILE *streamFile);
VGMSTREAM* init_vgmstream_wvs_xbox(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_wvs_ngc(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_dc_str(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dc_str_v2(STREAMFILE *streamFile);
@ -390,7 +390,7 @@ VGMSTREAM * init_vgmstream_ps2_mcg(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_zsd(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_vgs(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_vgs_ps(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_redspark(STREAMFILE *streamFile);
@ -510,7 +510,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_wii_ras(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_spm(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_spm(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_x360_tra(STREAMFILE* streamFile);

View File

@ -8,33 +8,37 @@
static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t offset, int32_t num_samples, int32_t loop_start, int32_t loop_end) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag = 0, channel_count;
off_t data_offset, samples_offset, multichannel_offset = 0;
int loop_flag = 0, channels, sample_rate;
off_t data_offset, context_offset, multistream_offset = 0;
size_t data_size, skip = 0;
/* header chunk */
if (read_u32le(offset + 0x00,sf) != 0x80000001)
if (read_u32le(offset + 0x00,sf) != 0x80000001) /* 'basic info' chunk */
goto fail;
/* 0x04: chunk size */
/* 0x04: chunk size (should be 0x24) */
/* 0x08: null */
channel_count = read_u8(offset + 0x09, sf);
/* 0x0a: packet size if CBR, 0 if VBR */
/* 0x08: version (0) */
channels = read_u8(offset + 0x09, sf);
/* 0x0a: frame size if CBR, 0 if VBR */
sample_rate = read_u32le(offset + 0x0c,sf);
data_offset = read_u32le(offset + 0x10, sf);
/* 0x14: null/reserved? */
samples_offset = read_u32le(offset + 0x18, sf);
skip = read_u16le(offset + 0x1c, sf);
/* 0x1e: ? (seen in Lego Movie 2 (Switch)) */
/* 0x14: 'frame data offset' (seek table? not seen) */
context_offset = read_u32le(offset + 0x18, sf);
skip = read_u16le(offset + 0x1c, sf); /* pre-skip sample count */
/* 0x1e: officially padding (non-zero in Lego Movie 2 (Switch)) */
/* (no offset to multistream chunk, maybe meant to go after seek/context chunks?) */
/* samples chunk, rare [Famicom Detective Club (Switch)] */
if (samples_offset && read_u32le(offset + samples_offset, sf) == 0x80000003) {
/* 0x80000002: 'offset info' chunk (seek table?), not seen */
/* 'context info' chunk, rare [Famicom Detective Club (Switch)] */
if (context_offset && read_u32le(offset + context_offset, sf) == 0x80000003) {
/* maybe should give priority to external info? */
samples_offset += offset;
context_offset += offset;
/* 0x08: null*/
loop_flag = read_u8 (samples_offset + 0x09, sf);
num_samples = read_s32le(samples_offset + 0x0c, sf); /* slightly smaller than manual count */
loop_start = read_s32le(samples_offset + 0x10, sf);
loop_end = read_s32le(samples_offset + 0x14, sf);
loop_flag = read_u8 (context_offset + 0x09, sf);
num_samples = read_s32le(context_offset + 0x0c, sf); /* slightly smaller than manual count */
loop_start = read_s32le(context_offset + 0x10, sf);
loop_end = read_s32le(context_offset + 0x14, sf);
/* rest (~0x38) reserved/alignment? */
/* values seem to take encoder delay into account */
}
@ -43,13 +47,13 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
}
/* multichannel chunk, rare [Clannad (Switch)] */
/* 'multistream info' chunk, rare [Clannad (Switch)] */
if (read_u32le(offset + 0x20, sf) == 0x80000005) {
multichannel_offset = offset + 0x20;
multistream_offset = offset + 0x20;
}
/* data chunk */
/* 'data info' chunk */
data_offset += offset;
if (read_u32le(data_offset, sf) != 0x80000004)
goto fail;
@ -60,13 +64,13 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_type;
vgmstream->sample_rate = read_u32le(offset + 0x0c,sf);
vgmstream->sample_rate = sample_rate;
if (vgmstream->sample_rate == 16000)
vgmstream->sample_rate = 48000; // Grandia HD Collection contains a false sample_rate in header
vgmstream->sample_rate = 48000; // Grandia HD Collection contains a false sample_rate in header
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
@ -80,12 +84,12 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
cfg.skip = skip;
cfg.sample_rate = vgmstream->sample_rate;
if (multichannel_offset && vgmstream->channels <= 8) {
if (multistream_offset && vgmstream->channels <= 8) {
int i;
cfg.stream_count = read_u8(multichannel_offset + 0x08,sf);
cfg.coupled_count = read_u8(multichannel_offset + 0x09,sf);
cfg.stream_count = read_u8(multistream_offset + 0x08,sf);
cfg.coupled_count = read_u8(multistream_offset + 0x09,sf); /* stereo streams */
for (i = 0; i < vgmstream->channels; i++) {
cfg.channel_mapping[i] = read_u8(multichannel_offset + 0x0a + i,sf);
cfg.channel_mapping[i] = read_u8(multistream_offset + 0x0a + i,sf);
}
}
@ -174,14 +178,14 @@ VGMSTREAM* init_vgmstream_opus_capcom(STREAMFILE* sf) {
VGMSTREAM *vgmstream = NULL;
off_t offset;
int num_samples, loop_start, loop_end;
int channel_count;
int channels;
/* checks */
if ( !check_extensions(sf,"opus,lopus"))
goto fail;
channel_count = read_32bitLE(0x04,sf);
if (channel_count != 1 && channel_count != 2 && channel_count != 6)
channels = read_32bitLE(0x04,sf);
if (channels != 1 && channels != 2 && channels != 6)
goto fail; /* unknown stream layout */
num_samples = read_32bitLE(0x00,sf);
@ -196,17 +200,17 @@ VGMSTREAM* init_vgmstream_opus_capcom(STREAMFILE* sf) {
/* 0x2c: some size? */
/* 0x30+: extra chunks (0x00: 0x7f, 0x04: num_sample), alt loop starts/regions? */
if (channel_count == 6) {
if (channels == 6) {
/* 2ch multistream hacky-hacks in RE:RE, don't try this at home. We'll end up with:
* main vgmstream > N vgmstream layers > substream IO deinterleaver > opus meta > Opus IO transmogrifier (phew) */
layered_layout_data* data = NULL;
int layers = channel_count / 2;
int layers = channels / 2;
int i;
int loop_flag = (loop_end > 0);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
vgmstream->layout_type = layout_layered;

View File

@ -1,65 +0,0 @@
#include "meta.h"
#include "../util.h"
/* SPM (from Lethal Skies Elite Pilot: Team SW) */
VGMSTREAM * init_vgmstream_ps2_spm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("spm",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x53504D00) /* "SPM" */
goto fail;
loop_flag = 1;
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x800;
vgmstream->channels = channel_count;
vgmstream->sample_rate = 48000;
vgmstream->coding_type = coding_PCM16LE;
vgmstream->num_samples = read_32bitLE(0x4,streamFile)/4;
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x8,streamFile);
vgmstream->loop_end_sample = read_32bitLE(0xC,streamFile);
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 2;
vgmstream->meta_type = meta_PS2_SPM;
/* 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

@ -1,51 +0,0 @@
#include "meta.h"
#include "../coding/coding.h"
/* VGS - from Princess Soft games [Gin no Eclipse (PS2), Metal Wolf REV (PS2)] */
VGMSTREAM * init_vgmstream_ps2_vgs(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t data_size, channel_size, interleave;
int loop_flag, channel_count;
int32_t loop_start = 0, loop_end = 0;
/* check */
if ( !check_extensions(streamFile,"vgs") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x56475300) /* "VGS\0" ('VAG stereo', presumably) */
goto fail;
start_offset = 0x30;
data_size = get_streamfile_size(streamFile) - start_offset;
interleave = 0x20000;
channel_count = 2;
channel_size = read_32bitBE(0x0c,streamFile);
loop_flag = 0; /* all files have loop flags but simply fade out normally and repeat */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_PS2_VGS;
vgmstream->sample_rate = read_32bitBE(0x10,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(channel_size,1);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
if (vgmstream->interleave_block_size)
vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels;
read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); /* always, can be null */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

54
src/meta/spm.c Normal file
View File

@ -0,0 +1,54 @@
#include "meta.h"
#include "../coding/coding.h"
/* SPM - Seq-PCM stream Square Sounds Co. games [Lethal Skies Elite Pilot: Team SW (PS2)] */
VGMSTREAM* init_vgmstream_spm(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag, channels;
size_t data_size;
int32_t loop_start, loop_end;
/* checks */
/* .spm: extension from debug strings */
if (!check_extensions(sf, "spm"))
goto fail;
if (!is_id32be(0x00,sf,"SPM\0"))
goto fail;
data_size = read_u32le(0x04,sf);
loop_start = read_s32le(0x08,sf);
loop_end = read_s32le(0x0c,sf);
/* 0x10: volume? */
/* rest: null */
start_offset = 0x20;
channels = 2;
loop_flag = 1;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_SPM;
vgmstream->sample_rate = 48000;
vgmstream->num_samples = pcm16_bytes_to_samples(data_size - start_offset, channels);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

63
src/meta/vgs_ps.c Normal file
View File

@ -0,0 +1,63 @@
#include "meta.h"
#include "../coding/coding.h"
/* VGS - from Princess Soft games [Gin no Eclipse (PS2), Metal Wolf REV (PS2)] */
VGMSTREAM* init_vgmstream_vgs_ps(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
size_t data_size, channel_size, interleave, sample_rate;
int loop_flag, channels;
int32_t loop_start = 0, loop_end = 0;
/* check */
if (!check_extensions(sf,"vgs"))
goto fail;
if (!is_id32be(0x00,sf, "VGS\0")) /* 'VAG stereo', presumably (simple VAG clone) */
goto fail;
start_offset = 0x30;
data_size = get_streamfile_size(sf) - start_offset;
/* test PS-ADPCM null frame for 2nd channel to detect interleave */
if (read_u32be(0x20000 + start_offset,sf) == 0) {
interleave = 0x20000; /* common */
}
else if (read_u32be(0x8000 + start_offset,sf) == 0) {
interleave = 0x8000; /* Ishikura Noboru no Igo Kouza: Chuukyuuhen (PS2) */
}
else {
goto fail;
}
channels = 2;
channel_size = read_u32be(0x0c,sf);
sample_rate = read_s32be(0x10,sf);
loop_flag = 0; /* all files have loop flags but simply fade out normally and repeat */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_VGS_PS;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
if (vgmstream->interleave_block_size)
vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size * channels)) / channels;
read_string(vgmstream->stream_name,0x10+1, 0x20,sf); /* always, can be null */
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,66 +1,66 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
/* VS - VagStream from Square games [Final Fantasy X (PS2) voices, Unlimited Saga (PS2) voices, All Star Pro-Wrestling 2/3 (PS2) music] */
VGMSTREAM * init_vgmstream_vs_square(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int channel_count, loop_flag, pitch, flags;
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;
flags = read_32bitLE(0x04,streamFile);
/* 0x08: block number */
/* 0x0c: blocks left in the subfile */
pitch = read_32bitLE(0x10,streamFile); /* usually 0x1000 = 48000 */
/* 0x14: volume, usually 0x64 = 100 but may be bigger/smaller (up to 128?) */
/* 0x18: null */
/* 0x1c: null */
/* some Front Mission 4 voices have flag 0x100, no idea */
if (flags != 0x00 && flags != 0x01) {
VGM_LOG("VS: unknown flags %x\n", flags);
}
loop_flag = 0;
channel_count = (flags & 1) ? 2 : 1;
start_offset = 0x00;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_VS_SQUARE;
vgmstream->sample_rate = round10((48000 * pitch) / 4096); /* needed for rare files */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_blocked_vs_square;
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;
}
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
/* VS - VagStream from Square Sounds Co. games [Final Fantasy X (PS2) voices, Unlimited Saga (PS2) voices, All Star Pro-Wrestling 2/3 (PS2) music] */
VGMSTREAM* init_vgmstream_vs_square(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
int channels, loop_flag, pitch, flags;
off_t start_offset;
/* checks */
/* .vs: extension from debug strings (probably like The Bouncer's .vs, very similar) */
if (!check_extensions(sf, "vs"))
goto fail;
if (!is_id32be(0x00,sf,"VS\0\0"))
goto fail;
flags = read_u32le(0x04,sf);
/* 0x08: block number */
/* 0x0c: blocks left in the subfile */
pitch = read_u32le(0x10,sf); /* usually 0x1000 = 48000 */
/* 0x14: volume, usually 0x64 = 100, up to 128 [Lethal Skies / Sidewinder F (PS2)] */
/* 0x18: null */
/* 0x1c: null */
/* some Front Mission 4 voices have flag 0x100, no idea */
if (flags != 0x00 && flags != 0x01) {
VGM_LOG("VS: unknown flags %x\n", flags);
}
loop_flag = 0;
channels = (flags & 1) ? 2 : 1;
start_offset = 0x00;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_VS_SQUARE;
vgmstream->sample_rate = round10((48000 * pitch) / 4096); /* needed for rare files */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_blocked_vs_square;
if (!vgmstream_open_stream(vgmstream, sf, 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(sf));
block_update(start_offset, vgmstream);
}
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,134 +1,110 @@
#include "meta.h"
#include "../coding/coding.h"
/* WVS - found in Metal Arms - Glitch in the System (Xbox) */
VGMSTREAM * init_vgmstream_xbox_wvs(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
size_t data_size;
/* check extension */
if (!check_extensions(streamFile,"wvs"))
goto fail;
if (read_16bitLE(0x0C,streamFile) != 0x69 && /* codec */
read_16bitLE(0x08,streamFile) != 0x4400)
goto fail;
start_offset = 0x20;
data_size = read_32bitLE(0x00,streamFile);
loop_flag = (read_16bitLE(0x0a,streamFile) == 0x472C); /* loop seems to be like this */
channel_count = read_16bitLE(0x0e,streamFile); /* always stereo files */
if (data_size + start_offset != get_streamfile_size(streamFile))
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x10,streamFile);
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels);
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_XBOX_WVS;
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/*
WVS (found in Metal Arms - Glitch in the System)
*/
VGMSTREAM * init_vgmstream_ngc_wvs(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("wvs",filename_extension(filename))) goto fail;
if ((read_32bitBE(0x14,streamFile)*read_32bitBE(0x00,streamFile)+0x60)
!= (get_streamfile_size(streamFile)))
{
goto fail;
}
loop_flag = read_32bitBE(0x10,streamFile);
channel_count = read_32bitBE(0x00,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x60;
if (channel_count == 1) {
vgmstream->sample_rate = 22050;
} else if (channel_count == 2) {
vgmstream->sample_rate = 44100;
}
vgmstream->channels = channel_count;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->num_samples = (get_streamfile_size(streamFile)-start_offset)/8/channel_count*14; //(read_32bitBE(0x0C,streamFile)-start_offset)/8/channel_count*14;
if (loop_flag) {
vgmstream->loop_start_sample = (read_32bitBE(0x10,streamFile)*2)/8/channel_count*14;
vgmstream->loop_end_sample = (read_32bitBE(0x14,streamFile)*2)/8/channel_count*14;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitBE(0x0C,streamFile);
vgmstream->meta_type = meta_NGC_WVS;
if (vgmstream->coding_type == coding_NGC_DSP) {
int i,c;
for (c=0;c<channel_count;c++) {
for (i=0;i<16;i++) {
vgmstream->ch[c].adpcm_coef[i] =
read_16bitBE(0x18+c*0x20 +i*2,streamFile);
}
}
}
/* 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;
}
#include "meta.h"
#include "../coding/coding.h"
/* .WVS - found in Metal Arms - Glitch in the System (Xbox) */
VGMSTREAM* init_vgmstream_wvs_xbox(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag, channels, sample_rate;
size_t data_size;
/* checks */
if (!check_extensions(sf,"wvs"))
goto fail;
data_size = read_u32le(0x00,sf);
/* 0x04: float seconds (slightly bigger than max num_samples) */
sample_rate = read_f32le(0x08,sf);
if (read_u16le(0x0c,sf) != 0x0069) /* codec */
goto fail;
channels = read_s16le(0x0e,sf);
sample_rate = read_s32le(0x10,sf);
/* 0x10: sample rate (int) */
/* 0x14: bitrate */
/* 0x18: block size / bps */
/* 0x1c: size? / block samples */
loop_flag = (channels > 1 && sample_rate >= 44100); /* bgm full loops */
start_offset = 0x20;
if (data_size + start_offset != get_streamfile_size(sf))
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_WVS;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, channels);
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* .WVS - found in Metal Arms - Glitch in the System (GC) */
VGMSTREAM* init_vgmstream_wvs_ngc(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag, channels, sample_rate, interleave;
size_t data_size;
/* checks */
if (!check_extensions(sf,"wvs"))
goto fail;
channels = read_s32be(0x00,sf);
/* 0x04: float seconds (slightly bigger than max num_samples) */
sample_rate = read_f32be(0x08,sf);
interleave = read_u32be(0x0C,sf); /* even in mono */
/* 0x10: number of interleave blocks */
data_size = read_s32be(0x14,sf) * channels;
loop_flag = (channels > 1 && sample_rate >= 44100); /* bgm full loops */
start_offset = 0x60;
if (data_size + start_offset != get_streamfile_size(sf))
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_WVS;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channels);
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
if (interleave)
vgmstream->interleave_last_block_size = (data_size % (interleave * channels)) / channels;
dsp_read_coefs_be(vgmstream, sf, 0x18, 0x20);
//dsp_read_hist_be(vgmstream, sf, 0x18 + 0x20*channels, 0x04); /* not seen */
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -127,8 +127,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_aix,
init_vgmstream_ngc_tydsp,
init_vgmstream_capdsp,
init_vgmstream_xbox_wvs,
init_vgmstream_ngc_wvs,
init_vgmstream_wvs_xbox,
init_vgmstream_wvs_ngc,
init_vgmstream_dc_str,
init_vgmstream_dc_str_v2,
init_vgmstream_xbox_matx,
@ -196,7 +196,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_ads_midway,
init_vgmstream_ps2_mcg,
init_vgmstream_zsd,
init_vgmstream_ps2_vgs,
init_vgmstream_vgs_ps,
init_vgmstream_redspark,
init_vgmstream_ivaud,
init_vgmstream_wii_wsd,
@ -265,7 +265,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_ps3_past,
init_vgmstream_sgxd,
init_vgmstream_wii_ras,
init_vgmstream_ps2_spm,
init_vgmstream_spm,
init_vgmstream_x360_tra,
init_vgmstream_ps2_iab,
init_vgmstream_vs_str,

View File

@ -461,8 +461,7 @@ typedef enum {
meta_NGC_DSP_KONAMI, /* Konami DSP header, found in various games */
meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */
meta_RAW_WAVM,
meta_XBOX_WVS, /* XBOX WVS */
meta_NGC_WVS, /* Metal Arms - Glitch in the System */
meta_WVS,
meta_XBOX_MATX, /* XBOX MATX */
meta_XMU,
meta_XVAS,
@ -564,9 +563,9 @@ typedef enum {
meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */
meta_SGXD, /* Sony: Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */
meta_WII_RAS, /* Donkey Kong Country Returns (Wii) */
meta_PS2_SPM, /* Lethal Skies Elite Pilot: Team SW */
meta_SPM,
meta_X360_TRA, /* Def Jam Rapstar */
meta_PS2_VGS, /* Princess Soft PS2 games */
meta_VGS_PS,
meta_PS2_IAB, /* Ueki no Housoku - Taosu ze Robert Juudan!! (PS2) */
meta_VS_STR, /* The Bouncer */
meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */