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 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). 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 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 like foobar or Winamp don't react well to that, they may be renamed to make
them playable through vgmstream. them playable through vgmstream.
@ -219,7 +219,7 @@ them playable through vgmstream.
- `.aif` to `.laif` (standard Mac AIF, Asobo AIF, Ogg) - `.aif` to `.laif` (standard Mac AIF, Asobo AIF, Ogg)
- `.aiff/aifc` to `.laiffl/laifc` (standard Mac AIF) - `.aiff/aifc` to `.laiffl/laifc` (standard Mac AIF)
- `.asf` to `.lasf` (EA games, Argonaut ASF) - `.asf` to `.lasf` (EA games, Argonaut ASF)
- `.bin` to `.lbin` (various) - `.bin` to `.lbin` (various formats)
- `.flac` to `.lflac` (standard FLAC) - `.flac` to `.lflac` (standard FLAC)
- `.mp2` to `.lmp2` (standard MP2) - `.mp2` to `.lmp2` (standard MP2)
- `.mp3` to `.lmp3` (standard MP3) - `.mp3` to `.lmp3` (standard MP3)
@ -228,34 +228,41 @@ them playable through vgmstream.
- `.ogg` to `.logg` (standard OGG) - `.ogg` to `.logg` (standard OGG)
- `.opus` to `.lopus` (standard OPUS or Switch OPUS) - `.opus` to `.lopus` (standard OPUS or Switch OPUS)
- `.stm` to `.lstm` (Rockstar STM) - `.stm` to `.lstm` (Rockstar STM)
- `.wav` to `.lwav` (standard WAV) - `.wav` to `.lwav` (standard WAV, various formats)
- `.wma` to `.lwma` (standard WMA) - `.wma` to `.lwma` (standard WMA)
- `.(any)` to `.vgmstream` (FFmpeg formats or TXTH) - `.(any)` to `.vgmstream` (FFmpeg formats or TXTH)
Command line tools don't have this restriction and will accept the original Command line tools don't have this restriction and will accept the original
filename. filename.
The main advantage to rename them is that vgmstream may use the file's The main advantage of renaming here is that vgmstream may use the file's internal
internal loop info, or apply subtle fixes, but is also limited in some ways loop info, or apply subtle fixes, but is also limited in some ways (like ignoring
(like standard/player's tagging). `.vgmstream` is a catch-all extension that standard tags). `.vgmstream` is a catch-all extension that may work as a last resort
may work as a last resort to make a file playable. to make a file playable.
Some plugins have options that allow any extension (common or unknown) to be Some plugins have options that allow common extensions to be played, making any
played, making renaming unnecessary. You may need to adjust plugin priority in renaming unnecessary. You may need to adjust plugin priority in player's options
player's options first. 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 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 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 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 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 When extracting from a bigfile, sometimes internal files don't have an proper
name+extension. Those should be renamed to its proper/common extension, as the 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 extractor program may guess wrong (like `.wav` instead of `.at3` or `.wem`).
there is no known extension, usually the header id string may be used instead. If there is no known extension, usually the header id/magic string may be used instead.
Note that vgmstream also accepts certain extension-less files too.
### Demuxed videos ### Demuxed videos
vgmstream also supports audio from videos, but usually must be demuxed (extracted 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` - `.ogg.sfl` : loop info for `.ogg`
- `.opus.sli`: loop info for `.opus` - `.opus.sli`: loop info for `.opus`
- `.pos`: loop info for .wav - `.pos`: loop info for .wav
- `.vgmstream.pos`: loop info for FFmpeg formats
- `.acb`: names for `.awb` - `.acb`: names for `.awb`
- `.xsb`: names for `.xwb` - `.xsb`: names for `.xwb`
@ -303,6 +309,7 @@ Similarly some formats split header+body data in separate files, examples:
- `.wav.str`+`.wav` - `.wav.str`+`.wav`
- `.wav`+`.dcs` - `.wav`+`.dcs`
- `.wbh`+`.wbd` - `.wbh`+`.wbd`
Both are needed to play and must be together. The usual rule is you open the 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 bigger file (body), save a few formats where the smaller (header) file is opened
instead for technical reasons (mainly some bank formats). 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 Cases like those can be supported using an artificial files with info vgmstream
needs. 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. vgmstream source.
#### GENH #### 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 *TXTH* is recommended over *GENH* as it's far easier to create and has many
more functions, plus doesn't modify original data. 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 #### TXTP
Text files with player configuration, named `(name).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 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. 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. Text inside `.txtp` can contain a list of filenames to play as one, a list of
`intro.vag(line)loop.vag`), a list of single-channel files to join as a single single-channel files to join as a single multichannel file, subsong index,
multichannel file, subsong index (ex. `bgm.sxd#10`), per-file configurations like per-file configurations like number of loops, remove unneeded channels,
number of loops, remove unneeded channels, force looping, and many other features. 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 #### TXTM
A text file named `.txtm` for some formats with companion files. It lists 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. know which companion file(s) to load if its name doesn't match the main file.
Note that companion file order is usually important. 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) # Harry Potter and the Chamber of Secrets (PS2)
entrance.mpf:entrance.mus,entrance_o.mus entrance.mpf: entrance.mus,entrance_o.mus
willow.mpf:willow.mus,willow_o.mus willow.mpf: willow.mus,willow_o.mus
``` ```
``` ```
# Metal Gear Solid: Snake Eater 3D (3DS) names for .awb # 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_PS2_ENTH, ".enth Header"},
{meta_SDT, "High Voltage .sdt header"}, {meta_SDT, "High Voltage .sdt header"},
{meta_NGC_TYDSP, ".tydsp Header"}, {meta_NGC_TYDSP, ".tydsp Header"},
{meta_XBOX_WVS, "Metal Arms WVS Header (XBOX)"}, {meta_WVS, "Swingin' Ape .WVS header"},
{meta_NGC_WVS, "Metal Arms WVS Header (GameCube)"},
{meta_XBOX_MATX, "assumed Matrix file by .matx extension"}, {meta_XBOX_MATX, "assumed Matrix file by .matx extension"},
{meta_DEC, "Falcom DEC RIFF header"}, {meta_DEC, "Falcom DEC RIFF header"},
{meta_VS, "Melbourne House .VS header"}, {meta_VS, "Melbourne House .VS header"},
@ -1151,9 +1150,9 @@ static const meta_info meta_info_list[] = {
{meta_PS3_PAST, "SNDP header"}, {meta_PS3_PAST, "SNDP header"},
{meta_SGXD, "Sony SGXD header"}, {meta_SGXD, "Sony SGXD header"},
{meta_WII_RAS, "RAS 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_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_PS2_IAB, "Runtime .IAB header"},
{meta_VS_STR, "Square .VS STR* header"}, {meta_VS_STR, "Square .VS STR* header"},
{meta_LSF_N1NJ4N, ".lsf !n1nj4n header"}, {meta_LSF_N1NJ4N, ".lsf !n1nj4n header"},

View File

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

View File

@ -232,7 +232,7 @@
<ClCompile Include="meta\ps2_iab.c" /> <ClCompile Include="meta\ps2_iab.c" />
<ClCompile Include="meta\mss.c" /> <ClCompile Include="meta\mss.c" />
<ClCompile Include="meta\mtaf.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\rad.c" />
<ClCompile Include="meta\sbk.c" /> <ClCompile Include="meta\sbk.c" />
<ClCompile Include="meta\tgc.c" /> <ClCompile Include="meta\tgc.c" />
@ -435,7 +435,7 @@
<ClCompile Include="meta\ppst.c" /> <ClCompile Include="meta\ppst.c" />
<ClCompile Include="meta\ps2_vds_vdm.c" /> <ClCompile Include="meta\ps2_vds_vdm.c" />
<ClCompile Include="meta\ps2_adm.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_ass.c" />
<ClCompile Include="meta\ps2_ast.c" /> <ClCompile Include="meta\ps2_ast.c" />
<ClCompile Include="meta\aus.c" /> <ClCompile Include="meta\aus.c" />
@ -485,7 +485,7 @@
<ClCompile Include="meta\vag.c" /> <ClCompile Include="meta\vag.c" />
<ClCompile Include="meta\ps2_vas.c" /> <ClCompile Include="meta\ps2_vas.c" />
<ClCompile Include="meta\ps2_vbk.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_vgv.c" />
<ClCompile Include="meta\ps2_vms.c" /> <ClCompile Include="meta\ps2_vms.c" />
<ClCompile Include="meta\ps2_voi.c" /> <ClCompile Include="meta\ps2_voi.c" />

View File

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

View File

@ -3,8 +3,8 @@
typedef struct { typedef struct {
uint16_t start,mult,add; /* XOR values derived from the actual key */ uint16_t start, mult, add; /* XOR values derived from the actual key */
char* key8; /* keystring used by type 8 encryption */ const char* key8; /* keystring used by type 8 encryption */
uint64_t key9; /* keycode used by type 9 encryption */ uint64_t key9; /* keycode used by type 9 encryption */
} adxkey_info; } adxkey_info;
@ -20,7 +20,7 @@ static const adxkey_info adxkey8_list[] = {
{0x49e1,0x4a57,0x553d, "karaage",0}, {0x49e1,0x4a57,0x553d, "karaage",0},
/* Blood+ (PS2) [Grasshopper Manufacture] */ /* 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] */ /* Killer7 (PS2) [Grasshopper Manufacture] */
{0x50fb,0x5803,0x5701, "GHM",0}, {0x50fb,0x5803,0x5701, "GHM",0},
@ -70,11 +70,11 @@ static const adxkey_info adxkey8_list[] = {
/* Nogizaka Haruka no Himitsu: Cosplay Hajimemashita (PS2) [Vridge] */ /* Nogizaka Haruka no Himitsu: Cosplay Hajimemashita (PS2) [Vridge] */
{0x45af,0x5f27,0x52b1, "SKFHSIA",0}, {0x45af,0x5f27,0x52b1, "SKFHSIA",0},
/* Little Anchor (PS2) [D3 Publisher] */ /* Little Anchor (PS2) [Vridge] */
{0x5f65,0x5b3d,0x5f65, NULL,0}, // confirmed unique with guessadx {0x5f65,0x5b3d,0x5f65, "KHNUJYTG",0},
/* Hanayoi Romanesque: Ai to Kanashimi (PS2) [Marvelous] */ /* 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] */ /* Mobile Suit Gundam: Gundam vs. Gundam NEXT PLUS (PSP) [Capcom] */
{0x4f7b,0x4fdb,0x5cbf, "CS-GGNX+",0}, {0x4f7b,0x4fdb,0x5cbf, "CS-GGNX+",0},
@ -83,10 +83,10 @@ static const adxkey_info adxkey8_list[] = {
{0x4f7b,0x5071,0x4c61, "ELEMENGAL",0}, {0x4f7b,0x5071,0x4c61, "ELEMENGAL",0},
/* Rakushou! Pachi-Slot Sengen 6: Rio 2 Cruising Vanadis (PS2) [Net Corporation] */ /* 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] */ /* Tears to Tiara Gaiden: Avalon no Nazo (PS3) [Aquaplus] */
{0x47e1,0x60e9,0x51c1, NULL,0}, // confirmed unique with guessadx {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] */ /* Neon Genesis Evangelion: Koutetsu no Girlfriend 2nd (PS2) [Broccoli] */
{0x481d,0x4f25,0x5243, "eva2",0}, {0x481d,0x4f25,0x5243, "eva2",0},
@ -101,25 +101,25 @@ static const adxkey_info adxkey8_list[] = {
{0x5f5d,0x552b,0x5507, "DATAM-KK2",0}, {0x5f5d,0x552b,0x5507, "DATAM-KK2",0},
/* Sakura Taisen: Atsuki Chishio ni (PS2) [Sega] */ /* 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] */ /* Sakura Taisen Monogatari: Mysterious Paris (PS2) [Sega] */
{0x62ad,0x4b13,0x5957, "inoue4126",0}, {0x62ad,0x4b13,0x5957, "inoue4126",0},
/* Sotsugyou 2nd Generation (PS2) [Jinx] */ /* 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] */ /* 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] */ /* 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] */ /* Shakugan no Shana (PS2) [Vridge] */
{0x5fc5,0x63d9,0x599f, "FUZETSU",0}, {0x5fc5,0x63d9,0x599f, "FUZETSU",0},
/* Uragiri wa Boku no Namae o Shitteiru (PS2) [Kadokawa Shoten] */ /* 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] */ /* StormLover!! (PSP), StormLover Kai!! (PSP) [Vridge] */
{0x5a11,0x67e5,0x6751, "HEXDPFMDKPQW",0}, /* unknown AHX key */ {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]); 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] = { static const uint16_t key8_primes[0x400] = {
0x401B,0x4021,0x4025,0x402B,0x4031,0x403F,0x4043,0x4045,0x405D,0x4061,0x4067,0x406D,0x4087,0x4091,0x40A3,0x40A9, 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, 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, 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; size_t key_size;
uint16_t start = 0, mult = 0, add = 0; uint16_t start = 0, mult = 0, add = 0;
int i; int i;
if (key8 == NULL || key8[0] == '\0') if (key8 == NULL || key8[0] == '\0') /* strlen >= 1 */
goto end; goto end;
/* calcs as found in exes, though there is some unrolling in the original code */
key_size = strlen(key8); key_size = strlen(key8);
start = key8_primes[0x100]; start = key8_primes[0x100];
mult = key8_primes[0x200]; mult = key8_primes[0x200];
@ -349,9 +351,9 @@ static void derive_adx_key8(const char * key8, uint16_t * out_start, uint16_t *
} }
end: end:
*out_start = start; *p_start = start;
*out_mult = mult; *p_mult = mult;
*out_add = add; *p_add = add;
} }

View File

@ -1,48 +1,51 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
/* AHV - from Amuze games [Headhunter (PS2)] */ /* AHV - from Amuze games [Headhunter (PS2)] */
VGMSTREAM * init_vgmstream_ahv(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_ahv(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
off_t start_offset; off_t start_offset;
size_t data_size, channel_size, interleave; size_t data_size, channel_size, interleave, sample_rate;
int loop_flag, channel_count; int loop_flag, channels;
/* checks (.ahv: from names in bigfile) */ /* checks */
if ( !check_extensions(streamFile,"ahv") ) /* .ahv: from names in bigfile */
goto fail; if (!check_extensions(sf,"ahv"))
if (read_32bitBE(0x00,streamFile) != 0x41485600) /* "AHV\0" */ goto fail;
goto fail; if (!is_id32be(0x00,sf, "AHV\0"))
goto fail;
start_offset = 0x800;
data_size = get_streamfile_size(streamFile) - start_offset; start_offset = 0x800;
interleave = read_32bitLE(0x10,streamFile); data_size = get_streamfile_size(sf) - start_offset;
channel_count = (interleave != 0) ? 2 : 1;
channel_size = read_32bitLE(0x08,streamFile); channel_size = read_u32le(0x08,sf);
loop_flag = 0; sample_rate = read_32bitLE(0x0c,sf);
/* VAGp header after 0x14 */ interleave = read_u32le(0x10,sf);
channels = (interleave != 0) ? 2 : 1;
loop_flag = 0;
/* build the VGMSTREAM */ /* VAGp header after 0x14 */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* build the VGMSTREAM */
vgmstream->meta_type = meta_AHV; vgmstream = allocate_vgmstream(channels, loop_flag);
vgmstream->sample_rate = read_32bitLE(0x0c,streamFile); if (!vgmstream) goto fail;
vgmstream->num_samples = ps_bytes_to_samples(channel_size,1);
vgmstream->meta_type = meta_AHV;
vgmstream->coding_type = coding_PSX; vgmstream->sample_rate = sample_rate;
vgmstream->layout_type = layout_interleave; vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1);
vgmstream->interleave_block_size = interleave;
if (interleave) vgmstream->coding_type = coding_PSX;
vgmstream->interleave_last_block_size = (data_size % (interleave*channel_count)) / channel_count; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) if (interleave)
goto fail; vgmstream->interleave_last_block_size = (data_size % (interleave*channels)) / channels;
return vgmstream;
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
fail: goto fail;
close_vgmstream(vgmstream); return vgmstream;
return NULL;
} 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. /* 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. */ * Otherwise this kinda works, but for now it's just a test. */
#define BFSTM_ENABLE_REGION_SUBSONGS 0 #define BFSTM_ENABLE_REGION_SUBSONGS 1
#define BFSTM_ENABLE_REGION_FORCE_LOOPS 0 /* this makes sense in SM3D World, but not in Zelda BotW) */
#if BFSTM_ENABLE_REGION_SUBSONGS #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 #endif
/* BFSTM - Nintendo Wii U format */ /* BFSTM - Nintendo Wii U/Switch format */
VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_bfstm(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
off_t start_offset; off_t start_offset;
off_t info_offset = 0, data_offset = 0; off_t info_offset = 0, data_offset = 0;
int channel_count, loop_flag, codec; int channels, loop_flag, codec;
int big_endian; int big_endian;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
@ -29,20 +28,20 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
/* checks */ /* checks */
if ( !check_extensions(streamFile,"bfstm") ) if (!check_extensions(sf,"bfstm"))
goto fail; goto fail;
if (!is_id32be(0x00,sf, "FSTM"))
/* FSTM header */
if (read_32bitBE(0x00, streamFile) != 0x4653544D) /* "FSTM" */
goto fail; 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 */ /* 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_32bit = read_32bitBE;
read_16bit = read_16bitBE; read_16bit = read_16bitBE;
big_endian = 1; 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_32bit = read_32bitLE;
read_16bit = read_16bitLE; read_16bit = read_16bitLE;
big_endian = 0; big_endian = 0;
@ -53,18 +52,18 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
/* get sections (should always appear in the same order) */ /* get sections (should always appear in the same order) */
{ {
int i; int i;
int section_count = read_16bit(0x10, streamFile); int section_count = read_16bit(0x10, sf);
for (i = 0; i < section_count; i++) { for (i = 0; i < section_count; i++) {
/* 0x00: id, 0x02(2): padding, 0x04(4): offset, 0x08(4): size */ /* 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) { switch(section_id) {
case 0x4000: info_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, streamFile); */ break; case 0x4001: /* seek_offset = read_32bit(0x14+i*0x0c+0x04, sf); */ break;
case 0x4002: data_offset = read_32bit(0x14+i*0x0c+0x04, streamFile); break; case 0x4002: data_offset = read_32bit(0x14+i*0x0c+0x04, sf); break;
#if BFSTM_ENABLE_REGION_SUBSONGS #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 #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: default:
break; break;
} }
@ -75,13 +74,13 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
} }
/* INFO section */ /* INFO section */
if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */ if (read_32bitBE(info_offset, sf) != 0x494E464F) /* "INFO" */
goto fail; goto fail;
codec = read_8bit(info_offset + 0x20, streamFile); codec = read_8bit(info_offset + 0x20, sf);
loop_flag = read_8bit(info_offset + 0x21, streamFile); loop_flag = read_8bit(info_offset + 0x21, sf);
channel_count = read_8bit(info_offset + 0x22, streamFile); channels = read_8bit(info_offset + 0x22, sf);
#if BFSTM_ENABLE_REGION_SUBSONGS #if BFSTM_ENABLE_REGION_SUBSONGS
region_count = read_8bit(info_offset + 0x23, streamFile); region_count = read_8bit(info_offset + 0x23, sf);
#endif #endif
@ -89,18 +88,18 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag); vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bit(info_offset + 0x24, streamFile); vgmstream->sample_rate = read_32bit(info_offset + 0x24, sf);
vgmstream->num_samples = read_32bit(info_offset + 0x2c, streamFile); vgmstream->num_samples = read_32bit(info_offset + 0x2c, sf);
vgmstream->loop_start_sample = read_32bit(info_offset + 0x28, streamFile); vgmstream->loop_start_sample = read_32bit(info_offset + 0x28, sf);
vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->meta_type = meta_FSTM; vgmstream->meta_type = meta_FSTM;
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; vgmstream->layout_type = (channels == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = read_32bit(info_offset + 0x34, streamFile); vgmstream->interleave_block_size = read_32bit(info_offset + 0x34, sf);
vgmstream->interleave_last_block_size = read_32bit(info_offset + 0x44, streamFile); vgmstream->interleave_last_block_size = read_32bit(info_offset + 0x44, sf);
switch(codec) { switch(codec) {
case 0x00: case 0x00:
@ -116,13 +115,13 @@ VGMSTREAM * init_vgmstream_bfstm(STREAMFILE *streamFile) {
int i, c; int i, c;
off_t channel_indexes, channel_info_offset, coefs_offset; 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++) { for (i = 0; i < vgmstream->channels; i++) {
channel_info_offset = channel_indexes + read_32bit(channel_indexes+0x04+(i*0x08)+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, streamFile); coefs_offset = channel_info_offset + read_32bit(channel_info_offset+0x04, sf);
for (c = 0; c < 16; c++) { 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 #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 #endif
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) if (!vgmstream_open_stream(vgmstream,sf,start_offset))
goto fail; goto fail;
return vgmstream; return vgmstream;
@ -151,17 +150,16 @@ fail:
/* Newer .bfstm may have multiple regions, that are sample sections of some meaning, /* 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) * 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) */ * 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; off_t start_offset;
size_t stream_size; size_t stream_size;
int total_subsongs, target_subsong = streamFile->stream_index; int total_subsongs, target_subsong = sf->stream_index;
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE; int32_t (*read_s32)(off_t,STREAMFILE*) = big_endian ? read_s32be : read_s32le;
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitLE : read_16bitLE; int16_t (*read_s16)(off_t,STREAMFILE*) = big_endian ? read_s16be : read_s16le;
if (region_count <= 0 && regn_offset == 0 && codec != 0x02) if (region_count <= 0 && regn_offset == 0 && codec != 0x02)
goto fail; goto fail;
if (read_32bitBE(regn_offset, streamFile) != 0x5245474E) /* "REGN" */ if (!is_id32be(regn_offset, sf, "REGN"))
goto fail; goto fail;
/* pretend each region is a subsong, but use first subsong as the whole file, /* 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 < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
if (target_subsong > 1) { if (target_subsong > 1) {
int sample_aligned = 0, sample_skip = 0; int ch;
int i; off_t region_start, region_end, block_size;
off_t region_start, region_end; /* target region info */
size_t block_size; int32_t sample_start = read_s32(regn_offset + 0x20 + (target_subsong-2)*0x100 + 0x00, sf);
size_t sample_start = read_32bit(regn_offset + 0x20 + (target_subsong-2)*0x100+0x00, streamFile); int32_t sample_end = read_s32(regn_offset + 0x20 + (target_subsong-2)*0x100 + 0x04, sf) + 1;
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;
off_t adpcm_offset = regn_offset + 0x20 + (target_subsong-2)*0x100+0x08;
/* rest is padding up to 0x100 */ /* 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_start = sample_start / 14 * vgmstream->channels * 0x08;
region_end = sample_end / 14 * vgmstream->channels * 0x08; region_end = sample_end / 14 * vgmstream->channels * 0x08;
stream_size = region_end - region_start; stream_size = region_end - region_start;
//;VGM_LOG("BFSTM: region offset start=%lx, end=%lx\n", region_start, region_end); //;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 /* adjust region to closest block + use interleave first to correctly skip to first sample */
* the less accurate this is (with 0x2000 align can be off by ~4600 samples per channel) */ block_size = (vgmstream->interleave_block_size * vgmstream->channels);
//todo could be fixed with interleave_first_block
block_size = (vgmstream->interleave_block_size*vgmstream->channels);
if (region_start % block_size) { if (region_start % block_size) {
region_start -= region_start % block_size; /* now aligned */ off_t region_skip = (region_start % block_size);
//;VGM_LOG("BFSTM: new region start=%lx\n", region_start); //;VGM_LOG("BFSTM: new region start=%lx - %lx\n", region_start, region_skip);
/* get position of our block (close but smaller than sample_start) */ /* use interleave first to skip to correct offset */
sample_aligned = dsp_bytes_to_samples(region_start, vgmstream->channels); vgmstream->interleave_first_block_size = (block_size - region_skip) / vgmstream->channels;
/* and how many samples to skip until actual sample_start */ vgmstream->interleave_first_skip = (region_skip) / vgmstream->channels;
sample_skip = (sample_start - sample_aligned); 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; start_offset = region_start;
if (sample_end != vgmstream->num_samples) /* not exact but... */ /* sample_end doesn't fall in last block, interleave last doesn't apply */
vgmstream->interleave_last_block_size = 0; {
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->num_samples = /*sample_skip +*/ (sample_end - sample_start);
vgmstream->loop_start_sample = sample_skip; #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; 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); vgmstream_force_loop(vgmstream, 1, vgmstream->loop_start_sample, vgmstream->loop_end_sample);
#endif #endif
/* maybe loops should be disabled with some regions? */ /* maybe loops should be disabled with some regions? */
/* this won't make sense after aligning, whatevs, doesn't sound too bad */ /* region_start points to correct frame (when compared to ADPCM predictor), but not sure if hist
for (i = 0; i < vgmstream->channels; i++) { * is for exact nibble rather than first (sounds ok though) */
vgmstream->ch[i].adpcm_history1_16 = read_16bit(adpcm_offset+0x02+0x00, streamFile); for (ch = 0; ch < vgmstream->channels; ch++) {
vgmstream->ch[i].adpcm_history2_16 = read_16bit(adpcm_offset+0x02+0x02, streamFile); /* 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 { else {
start_offset = 0; 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; vgmstream->stream_size = stream_size;
return start_offset; 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_capdsp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xbox_wvs(STREAMFILE *streamFile); VGMSTREAM* init_vgmstream_wvs_xbox(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_ngc_wvs(STREAMFILE *streamFile); VGMSTREAM* init_vgmstream_wvs_ngc(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_dc_str(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_dc_str(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dc_str_v2(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_zsd(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_vgs(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_vgs_ps(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_redspark(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_wii_ras(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_spm(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_spm(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_x360_tra(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) { 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; VGMSTREAM* vgmstream = NULL;
off_t start_offset; off_t start_offset;
int loop_flag = 0, channel_count; int loop_flag = 0, channels, sample_rate;
off_t data_offset, samples_offset, multichannel_offset = 0; off_t data_offset, context_offset, multistream_offset = 0;
size_t data_size, skip = 0; size_t data_size, skip = 0;
/* header chunk */ /* header chunk */
if (read_u32le(offset + 0x00,sf) != 0x80000001) if (read_u32le(offset + 0x00,sf) != 0x80000001) /* 'basic info' chunk */
goto fail; goto fail;
/* 0x04: chunk size */ /* 0x04: chunk size (should be 0x24) */
/* 0x08: null */ /* 0x08: version (0) */
channel_count = read_u8(offset + 0x09, sf); channels = read_u8(offset + 0x09, sf);
/* 0x0a: packet size if CBR, 0 if VBR */ /* 0x0a: frame size if CBR, 0 if VBR */
sample_rate = read_u32le(offset + 0x0c,sf);
data_offset = read_u32le(offset + 0x10, sf); data_offset = read_u32le(offset + 0x10, sf);
/* 0x14: null/reserved? */ /* 0x14: 'frame data offset' (seek table? not seen) */
samples_offset = read_u32le(offset + 0x18, sf); context_offset = read_u32le(offset + 0x18, sf);
skip = read_u16le(offset + 0x1c, sf); skip = read_u16le(offset + 0x1c, sf); /* pre-skip sample count */
/* 0x1e: ? (seen in Lego Movie 2 (Switch)) */ /* 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)] */ /* 0x80000002: 'offset info' chunk (seek table?), not seen */
if (samples_offset && read_u32le(offset + samples_offset, sf) == 0x80000003) {
/* '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? */ /* maybe should give priority to external info? */
samples_offset += offset; context_offset += offset;
/* 0x08: null*/ /* 0x08: null*/
loop_flag = read_u8 (samples_offset + 0x09, sf); loop_flag = read_u8 (context_offset + 0x09, sf);
num_samples = read_s32le(samples_offset + 0x0c, sf); /* slightly smaller than manual count */ num_samples = read_s32le(context_offset + 0x0c, sf); /* slightly smaller than manual count */
loop_start = read_s32le(samples_offset + 0x10, sf); loop_start = read_s32le(context_offset + 0x10, sf);
loop_end = read_s32le(samples_offset + 0x14, sf); loop_end = read_s32le(context_offset + 0x14, sf);
/* rest (~0x38) reserved/alignment? */ /* rest (~0x38) reserved/alignment? */
/* values seem to take encoder delay into account */ /* 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) { if (read_u32le(offset + 0x20, sf) == 0x80000005) {
multichannel_offset = offset + 0x20; multistream_offset = offset + 0x20;
} }
/* data chunk */ /* 'data info' chunk */
data_offset += offset; data_offset += offset;
if (read_u32le(data_offset, sf) != 0x80000004) if (read_u32le(data_offset, sf) != 0x80000004)
goto fail; goto fail;
@ -60,13 +64,13 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->meta_type = meta_type; vgmstream->meta_type = meta_type;
vgmstream->sample_rate = read_u32le(offset + 0x0c,sf); vgmstream->sample_rate = sample_rate;
if (vgmstream->sample_rate == 16000) 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->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start; vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end; 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.skip = skip;
cfg.sample_rate = vgmstream->sample_rate; cfg.sample_rate = vgmstream->sample_rate;
if (multichannel_offset && vgmstream->channels <= 8) { if (multistream_offset && vgmstream->channels <= 8) {
int i; int i;
cfg.stream_count = read_u8(multichannel_offset + 0x08,sf); cfg.stream_count = read_u8(multistream_offset + 0x08,sf);
cfg.coupled_count = read_u8(multichannel_offset + 0x09,sf); cfg.coupled_count = read_u8(multistream_offset + 0x09,sf); /* stereo streams */
for (i = 0; i < vgmstream->channels; i++) { 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; VGMSTREAM *vgmstream = NULL;
off_t offset; off_t offset;
int num_samples, loop_start, loop_end; int num_samples, loop_start, loop_end;
int channel_count; int channels;
/* checks */ /* checks */
if ( !check_extensions(sf,"opus,lopus")) if ( !check_extensions(sf,"opus,lopus"))
goto fail; goto fail;
channel_count = read_32bitLE(0x04,sf); channels = read_32bitLE(0x04,sf);
if (channel_count != 1 && channel_count != 2 && channel_count != 6) if (channels != 1 && channels != 2 && channels != 6)
goto fail; /* unknown stream layout */ goto fail; /* unknown stream layout */
num_samples = read_32bitLE(0x00,sf); num_samples = read_32bitLE(0x00,sf);
@ -196,17 +200,17 @@ VGMSTREAM* init_vgmstream_opus_capcom(STREAMFILE* sf) {
/* 0x2c: some size? */ /* 0x2c: some size? */
/* 0x30+: extra chunks (0x00: 0x7f, 0x04: num_sample), alt loop starts/regions? */ /* 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: /* 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) */ * main vgmstream > N vgmstream layers > substream IO deinterleaver > opus meta > Opus IO transmogrifier (phew) */
layered_layout_data* data = NULL; layered_layout_data* data = NULL;
int layers = channel_count / 2; int layers = channels / 2;
int i; int i;
int loop_flag = (loop_end > 0); int loop_flag = (loop_end > 0);
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->layout_type = layout_layered; 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 "meta.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../coding/coding.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] */ /* 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 *streamFile) { VGMSTREAM* init_vgmstream_vs_square(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
int channel_count, loop_flag, pitch, flags; int channels, loop_flag, pitch, flags;
off_t start_offset; off_t start_offset;
/* checks */ /* checks */
/* .vs: header id (probably ok like The Bouncer's .vs, very similar) */ /* .vs: extension from debug strings (probably like The Bouncer's .vs, very similar) */
if (!check_extensions(streamFile, "vs")) if (!check_extensions(sf, "vs"))
goto fail; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x56530000) /* "VS\0\0" */ if (!is_id32be(0x00,sf,"VS\0\0"))
goto fail; goto fail;
flags = read_32bitLE(0x04,streamFile); flags = read_u32le(0x04,sf);
/* 0x08: block number */ /* 0x08: block number */
/* 0x0c: blocks left in the subfile */ /* 0x0c: blocks left in the subfile */
pitch = read_32bitLE(0x10,streamFile); /* usually 0x1000 = 48000 */ pitch = read_u32le(0x10,sf); /* usually 0x1000 = 48000 */
/* 0x14: volume, usually 0x64 = 100 but may be bigger/smaller (up to 128?) */ /* 0x14: volume, usually 0x64 = 100, up to 128 [Lethal Skies / Sidewinder F (PS2)] */
/* 0x18: null */ /* 0x18: null */
/* 0x1c: null */ /* 0x1c: null */
/* some Front Mission 4 voices have flag 0x100, no idea */ /* some Front Mission 4 voices have flag 0x100, no idea */
if (flags != 0x00 && flags != 0x01) { if (flags != 0x00 && flags != 0x01) {
VGM_LOG("VS: unknown flags %x\n", flags); VGM_LOG("VS: unknown flags %x\n", flags);
} }
loop_flag = 0; loop_flag = 0;
channel_count = (flags & 1) ? 2 : 1; channels = (flags & 1) ? 2 : 1;
start_offset = 0x00; start_offset = 0x00;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->meta_type = meta_VS_SQUARE; vgmstream->meta_type = meta_VS_SQUARE;
vgmstream->sample_rate = round10((48000 * pitch) / 4096); /* needed for rare files */ vgmstream->sample_rate = round10((48000 * pitch) / 4096); /* needed for rare files */
vgmstream->coding_type = coding_PSX; vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_blocked_vs_square; vgmstream->layout_type = layout_blocked_vs_square;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail; goto fail;
/* calc num_samples */ /* calc num_samples */
{ {
vgmstream->next_block_offset = start_offset; vgmstream->next_block_offset = start_offset;
do { do {
block_update(vgmstream->next_block_offset,vgmstream); block_update(vgmstream->next_block_offset,vgmstream);
vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1);
} }
while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); while (vgmstream->next_block_offset < get_streamfile_size(sf));
block_update(start_offset, vgmstream); block_update(start_offset, vgmstream);
} }
return vgmstream; return vgmstream;
fail: fail:
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

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

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

View File

@ -461,8 +461,7 @@ typedef enum {
meta_NGC_DSP_KONAMI, /* Konami DSP header, found in various games */ meta_NGC_DSP_KONAMI, /* Konami DSP header, found in various games */
meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */ meta_UBI_CKD, /* Ubisoft CKD RIFF header (Rayman Origins Wii) */
meta_RAW_WAVM, meta_RAW_WAVM,
meta_XBOX_WVS, /* XBOX WVS */ meta_WVS,
meta_NGC_WVS, /* Metal Arms - Glitch in the System */
meta_XBOX_MATX, /* XBOX MATX */ meta_XBOX_MATX, /* XBOX MATX */
meta_XMU, meta_XMU,
meta_XVAS, meta_XVAS,
@ -564,9 +563,9 @@ typedef enum {
meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */ meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */
meta_SGXD, /* Sony: Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */ meta_SGXD, /* Sony: Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */
meta_WII_RAS, /* Donkey Kong Country Returns (Wii) */ 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_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_PS2_IAB, /* Ueki no Housoku - Taosu ze Robert Juudan!! (PS2) */
meta_VS_STR, /* The Bouncer */ meta_VS_STR, /* The Bouncer */
meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */ meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */