mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-18 15:54:05 +01:00
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:
commit
deeb0b4eb5
100
README.md
100
README.md
@ -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
|
||||
|
@ -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"},
|
||||
|
@ -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
|
||||
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
163
src/meta/bfstm.c
163
src/meta/bfstm.c
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
@ -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
54
src/meta/spm.c
Normal 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
63
src/meta/vgs_ps.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
244
src/meta/wvs.c
244
src/meta/wvs.c
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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) */
|
||||
|
Loading…
x
Reference in New Issue
Block a user