mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-18 15:54:05 +01:00
Merge pull request #877 from bnnm/misc2
- Fix mono .thp [WarioWare, Inc. Mega Party Game$! (GC)] - Fix SQEX .scd XMA total samples + cleanup - Fix some .ktsl2asbin [Samurai Warriors 5 Demo (Switch)] - Support multi-awb .acb names using .txtm [Snack World (Switch)] - Fix XMA2 .wbd+wbh [Bladestorm Nightmare (PC)] - Improve MSADPCM accuracy - Cleanup
This commit is contained in:
commit
becac80999
167
README.md
167
README.md
@ -8,7 +8,7 @@ Some of vgmstream's features:
|
||||
obscure single-game codecs, aiming for high accuracy and compatibility.
|
||||
- support for looped BGM, using file's internal metadata for smooth transitions,
|
||||
with accurate sample counts
|
||||
- subsongs, playing internal songs separatedly
|
||||
- subsongs, playing a format's multiple internal songs separatedly
|
||||
- encryption keys, audio split in multiple files, internal stream names, and many
|
||||
other unusual cases found in game audio
|
||||
- TXTH function, to support extra formats (including raw audio in many forms)
|
||||
@ -17,6 +17,16 @@ Some of vgmstream's features:
|
||||
- simple external tagging via .m3u files
|
||||
- plugins available for various common players and O.S.
|
||||
|
||||
Latest development is here: https://github.com/vgmstream/vgmstream/
|
||||
|
||||
Automated builds with the latest changes: https://vgmstream.org/downloads
|
||||
|
||||
Help can be found here: https://www.hcs64.com/
|
||||
|
||||
More technical docs: https://github.com/vgmstream/vgmstream/tree/master/doc
|
||||
|
||||
|
||||
## Usage
|
||||
There are multiple end-user bits:
|
||||
- a command line decoder called "test.exe/vgmstream-cli"
|
||||
- a Winamp plugin called "in_vgmstream"
|
||||
@ -25,24 +35,30 @@ There are multiple end-user bits:
|
||||
- an Audacious plugin called "libvgmstream"
|
||||
- a command line player called "vgmstream123"
|
||||
|
||||
Help can be found here: https://www.hcs64.com/
|
||||
Main lib (plain vgmstream) is the code that handles internal conversion, while the
|
||||
above components are what you use to actually get sound. See *components* below for
|
||||
explanations about each one.
|
||||
|
||||
Latest development is usually here: https://github.com/vgmstream/vgmstream/
|
||||
### Files
|
||||
On Windows, after compiling with the build scripts you should get `vgmstream-win.zip`
|
||||
(bundle of various components) and `foo_input_vgmstream.fb2k-component` (installable
|
||||
foobar2000 plugin).
|
||||
|
||||
Automated builds with the latest changes: https://vgmstream.org/downloads
|
||||
For Linux and similar O.S., you need to build them manually.
|
||||
|
||||
Releases are here: https://github.com/vgmstream/vgmstream/releases
|
||||
You can find automatically pre-built binaries in https://vgmstream.org/downloads
|
||||
|
||||
You can find further info about other details in https://github.com/vgmstream/vgmstream/tree/master/doc
|
||||
If the above link fails you may find alt, recent-ish versions here:
|
||||
https://github.com/bnnm/vgmstream-builds/raw/master/bin/vgmstream-latest-test-u.zip
|
||||
|
||||
|
||||
## Needed extra files (for Windows)
|
||||
Support for some codecs (Ogg Vorbis, MPEG audio, etc) is done with external
|
||||
### Needed extra files (for Windows)
|
||||
On Windows support for some codecs (Ogg Vorbis, MPEG audio, etc) is done with external
|
||||
libraries, so you will need to have certain DLL files.
|
||||
|
||||
In the case of the foobar2000 component they are all bundled for convenience,
|
||||
or you can get them here: https://github.com/vgmstream/vgmstream/tree/master/ext_libs
|
||||
(bundled here: https://f.losno.co/vgmstream-win32-deps.zip, may not be latest).
|
||||
In the case of components like foobar2000 they are all bundled for convenience,
|
||||
while other components include them but must be installed manually.
|
||||
You can also get them here: https://github.com/vgmstream/vgmstream/tree/master/ext_libs
|
||||
or compile them manually, even (see tech docs).
|
||||
|
||||
Put the following files somewhere Windows can find them:
|
||||
- `libvorbis.dll`
|
||||
@ -57,21 +73,26 @@ Put the following files somewhere Windows can find them:
|
||||
- `libcelt-0110.dll`
|
||||
- `libspeex.dll`
|
||||
|
||||
For Winamp/XMPlay/command line this means in the directory with the main .exe,
|
||||
For Winamp/XMPlay/command line (`test.exe`) this means in the directory with the main `.exe`,
|
||||
or in a system directory, or any other directory in the PATH variable.
|
||||
|
||||
On other OSs like Linux/Mac, libs need to be installed before compiling, then should be used
|
||||
automatically, though not all may enabled at the moment due to build scripts issues.
|
||||
|
||||
|
||||
## Components
|
||||
|
||||
### test.exe/vgmstream-cli
|
||||
*Installation*: unzip the file and follow the above instructions for installing
|
||||
the other files needed.
|
||||
### test.exe/vgmstream-cli (command line decoder)
|
||||
*Windows*: unzip `test.exe` and follow the above instructions for installing needed extra files.
|
||||
|
||||
Converts playable files to wav. Typical usage would be:
|
||||
*Others*: build instructions can be found in doc/BUILD.md document in vgmstream's source
|
||||
code (can be compiled with CMake/Make/autotools).
|
||||
|
||||
Converts playable files to `.wav`. Typical usage would be:
|
||||
- `test.exe -o happy.wav happy.adx` to decode `happy.adx` to `happy.wav`.
|
||||
|
||||
If command-line isn't your thing you can simply drag and drop one or multiple
|
||||
files to the executable to decode them as `(filename).wav`.
|
||||
files to the executable to decode them as `(filename.ext).wav`.
|
||||
|
||||
There are multiple options that alter how the file is converted, for example:
|
||||
- `test.exe -m file.adx`: print info but don't decode
|
||||
@ -100,41 +121,55 @@ Output filename in `-o` may use multiple wildcards:
|
||||
For example `test.exe -s 2 -o ?04s_?n.wav file.fsb` could generate `0002_song1.wav`
|
||||
|
||||
|
||||
### in_vgmstream
|
||||
*Installation*: drop the `in_vgmstream.dll` in your Winamp plugins directory,
|
||||
### in_vgmstream (Winamp plugin)
|
||||
*Windows*: drop the `in_vgmstream.dll` in your Winamp plugins directory,
|
||||
and follow the above instructions for installing needed extra files.
|
||||
|
||||
*Others*: may be possible to use through *Wine*
|
||||
|
||||
Once installed, supported files should be playable. There is a simple config
|
||||
menu to tweak some options too.
|
||||
|
||||
|
||||
### xmp-vgmstream (XMPlay plugin)
|
||||
*Windows*: drop the `xmp-vgmstream.dll` in your XMPlay plugins directory,
|
||||
and follow the above instructions for installing the other files needed.
|
||||
|
||||
Once installed supported files should be playable.
|
||||
*Others*: may be possible to use through *Wine*
|
||||
|
||||
### xmp-vgmstream
|
||||
*Installation*: drop the `xmp-vgmstream.dll` in your XMPlay plugins directory,
|
||||
and follow the above instructions for installing the other files needed.
|
||||
|
||||
Note that this has less features compared to in_vgmstream and has no configuration.
|
||||
Note that this has less features compared to *in_vgmstream* and has no config.
|
||||
Since XMPlay supports Winamp plugins you may also use `in_vgmstream.dll` instead.
|
||||
|
||||
Because the XMPlay MP3 decoder incorrectly tries to play some vgmstream extensions,
|
||||
you need to manually fix it by going to **options > plugins > input > vgmstream**
|
||||
and in the "priority filetypes" put: `ahx,asf,awc,ckd,fsb,genh,msf,p3d,rak,scd,txth,xvag`
|
||||
|
||||
XMPlay cannot support subsongs due to player limitations, try using *TXTP* instead
|
||||
(explained below).
|
||||
XMPlay cannot support subsongs due to player limitations (with any plugin), try
|
||||
using *TXTP* instead (explained below).
|
||||
|
||||
### foo_input_vgmstream
|
||||
*Installation*: every file should be installed automatically by the `.fb2k-component`
|
||||
bundle.
|
||||
|
||||
### foo_input_vgmstream (foobar2000 plugin)
|
||||
*Windows*: every file should be installed automatically when opening the `.fb2k-component`
|
||||
bundle
|
||||
|
||||
*Others*: may be possible to use through *Wine*
|
||||
|
||||
A known quirk is that when loop options or tags change, playlist info won't refresh
|
||||
automatically. You need to manually refresh it by selecting songs and doing
|
||||
**shift + right click > Tagging > Reload info from file(s)**.
|
||||
|
||||
|
||||
### Audacious plugin
|
||||
*Installation*: needs to be manually built. Instructions can be found in doc/BUILD.md
|
||||
*Windows*: not possible at the moment.
|
||||
|
||||
*Others*: needs to be manually built. Instructions can be found in doc/BUILD.md
|
||||
document in vgmstream's source code (can be done with CMake or autotools).
|
||||
|
||||
### vgmstream123
|
||||
*Installation*: needs to be manually built. Instructions can be found in doc/BUILD.md
|
||||
|
||||
### vgmstream123 (command line player)
|
||||
*Windows/Linux*: needs to be manually built. Instructions can be found in doc/BUILD.md
|
||||
document in vgmstream's source code (can be done with CMake or autotools).
|
||||
On Windows it needs `libao.dll` and appropriate includes.
|
||||
|
||||
Usage: `vgmstream123 [options] INFILE ...`
|
||||
|
||||
@ -312,16 +347,6 @@ hex-editting), though only a few formats do this, mainly *Ubisoft* banks.
|
||||
|
||||
Regular formats without companion files should work fine in upper/lowercase.
|
||||
|
||||
#### .pos looping
|
||||
`.pos` is a small file with 32 bit little endian values: loop start sample and
|
||||
loop end sample. This is a real format, but is sometimes reused to force loops.
|
||||
|
||||
If you want to force looping consider using *TXTP* instead, as it's much simpler
|
||||
to make and cleaner (plus doesn't hijack a real format). For example, make a text
|
||||
file named `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.
|
||||
|
||||
|
||||
### Decryption keys
|
||||
Certain formats have encrypted data, and need a key to decrypt. vgmstream
|
||||
will try to find the correct key from a list, but it can be provided by
|
||||
@ -345,12 +370,18 @@ dynamically during gameplay, or looping metadata is stored externally.
|
||||
Cases like those can be supported using an artificial files with info vgmstream
|
||||
needs.
|
||||
|
||||
**GENH**: a byte header placed right before the original data, modyfing it.
|
||||
The resulting file must be `(name).genh`. Contains static header data.
|
||||
Programs like VGMToolbox can help to create *GENH*, but consider using *TXTH*
|
||||
instead.
|
||||
Creation of these files is meant for advanced users, docs can be found in
|
||||
vgmstream source.
|
||||
|
||||
**TXTH**: a text header placed in an external file. The TXTH must be named
|
||||
#### GENH
|
||||
A byte header placed right before the original data, modyfing it.
|
||||
The resulting file must be `(name).genh`. Contains static header data.
|
||||
|
||||
Programs like VGMToolbox can help to create *GENH*, but consider using *TXTH*
|
||||
instead, *GENH* is mostly deprecated.
|
||||
|
||||
#### TXTH
|
||||
A text header placed in an external file. The TXTH must be named
|
||||
`.txth` or `.(ext).txth` (for the whole folder), or `(name.ext).txth` (for a
|
||||
single file). Contains dynamic text commands to read data from the original
|
||||
file, or static values. This allows vgmstream to play unsupported formats.
|
||||
@ -358,20 +389,28 @@ 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.
|
||||
|
||||
For files that already play, sometimes they are used by the game in various
|
||||
complex and non-standard ways, like playing multiple small songs as a single
|
||||
#### TXTP
|
||||
Text files with player configuration, named `(name).txtp`.
|
||||
|
||||
For files that already play, sometimes games use them in various complex
|
||||
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.
|
||||
can create a *TXTP* file to customize how vgmstream handles songs.
|
||||
|
||||
**TXTP**: text files with player configuration, named `(name).txtp`. Text inside
|
||||
can contain a list of filenames to play as one (ex. `intro.vag(line)loop.vag`),
|
||||
list of separate 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, make looping files, and many other features.
|
||||
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.
|
||||
|
||||
**TXTM**: text file named `.txtm` for formats with companion files. It lists
|
||||
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
|
||||
name combos determining which companion files to load for each main file.
|
||||
It is useful for formats where name combos are hardcoded so vgmstream doesn't
|
||||
|
||||
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.
|
||||
|
||||
@ -385,9 +424,12 @@ willow.mpf:willow.mus,willow_o.mus
|
||||
# Metal Gear Solid: Snake Eater 3D (3DS) names for .awb
|
||||
bgm_2_streamfiles.awb: bgm_2.acb
|
||||
```
|
||||
```
|
||||
# Snack World (Switch) names for .awb (single .acb for all .awb, order matters)
|
||||
bgm.awb: bgm.acb
|
||||
bgm_DLC1.awb: bgm.acb
|
||||
```
|
||||
|
||||
Creation of those files is meant for advanced users, docs can be found in
|
||||
vgmstream source.
|
||||
|
||||
### Plugin conflicts
|
||||
Since vgmstream supports a huge amount of formats it's possibly that some of
|
||||
@ -947,10 +989,9 @@ This list is not complete and many other files are supported.
|
||||
- .txth (lots)
|
||||
- loop assists:
|
||||
- .mus (playlist for .acm)
|
||||
- .pos (loop info for .wav: 32 bit LE loop start sample + loop end sample)
|
||||
- .pos (loop info for .wav)
|
||||
- .sli (loop info for .ogg)
|
||||
- .sfl (loop info for .ogg)
|
||||
- .vgmstream + .vgmstream.pos (FFmpeg formats + loop assist)
|
||||
- other:
|
||||
- .adxkey (decryption key for .adx)
|
||||
- .ahxkey (decryption key for .ahx)
|
||||
|
@ -90,7 +90,9 @@ void decode_ulaw(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing,
|
||||
void decode_ulaw_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_alaw(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_pcmfloat(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian);
|
||||
size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample);
|
||||
int32_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample);
|
||||
int32_t pcm16_bytes_to_samples(size_t bytes, int channels);
|
||||
int32_t pcm8_bytes_to_samples(size_t bytes, int channels);
|
||||
|
||||
|
||||
/* psx_decoder */
|
||||
@ -159,7 +161,7 @@ STREAMFILE* nwa_get_streamfile(nwa_codec_data* data);
|
||||
#define MSADPCM_MAX_BLOCK_SIZE 0x800 /* known max and RIFF spec seems to concur, while MS's encoders may be lower (typical stereo: 0x8c, 0x2C, 0x48, 0x400) */
|
||||
|
||||
void decode_msadpcm_stereo(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int config);
|
||||
void decode_msadpcm_ck(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
long msadpcm_bytes_to_samples(long bytes, int block_size, int channels);
|
||||
int msadpcm_check_coefs(STREAMFILE* sf, off_t offset);
|
||||
|
@ -22,14 +22,64 @@ static const int16_t msadpcm_coefs[7][2] = {
|
||||
{ 392, -232 }
|
||||
};
|
||||
|
||||
|
||||
/* Decodes MSADPCM as explained in the spec (RIFFNEW / msadpcm.c).
|
||||
* Though RIFFNEW spec uses "predictor / 256", msadpcm.c uses "predictor >> 8" = diffs on negs (silly MS).
|
||||
* SHR is also true in Windows msadp32.acm decoders (up to Win10), that seem to use same code.
|
||||
* Some non-Windows implementations or engines (like UE4) use DIV though (more accurate).
|
||||
/* Decodes MSADPCM as explained in the spec (RIFFNEW doc + msadpcm.c).
|
||||
* Though RIFFNEW writes "predictor / 256" (DIV), msadpcm.c uses "predictor >> 8" (SHR). They may seem the
|
||||
* same but on negative values SHR gets different results (-128 / 256 = 0; -128 >> 8 = -1) = some output diffs.
|
||||
* SHR is true in Windows msadp32.acm decoders (up to Win10), while some non-Windows implementations or
|
||||
* engines (like UE4) may use DIV.
|
||||
*
|
||||
* On invalid coef index, msadpcm.c returns 0 decoded samples but here we clamp and keep on trucking.
|
||||
* In theory blocks may be 0-padded and should use samples_per_frame from header, in practice seems to
|
||||
* decode up to block length or available data. */
|
||||
|
||||
static int16_t msadpcm_adpcm_expand_nibble_shr(VGMSTREAMCHANNEL* stream, uint8_t byte, int shift) {
|
||||
int32_t hist1, hist2, predicted;
|
||||
int code = (shift) ?
|
||||
get_high_nibble_signed(byte) :
|
||||
get_low_nibble_signed (byte);
|
||||
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1 * stream->adpcm_coef[0] + hist2 * stream->adpcm_coef[1];
|
||||
predicted = predicted >> 8; /* 256 = FIXED_POINT_COEF_BASE (uses SHR instead) */
|
||||
predicted = predicted + (code * stream->adpcm_scale);
|
||||
predicted = clamp16(predicted); /* lNewSample */
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = predicted;
|
||||
|
||||
stream->adpcm_scale = (msadpcm_steps[code & 0xf] * stream->adpcm_scale) >> 8; /* not diffs vs DIV here (always >=0) */
|
||||
if (stream->adpcm_scale < 16) /* min delta */
|
||||
stream->adpcm_scale = 16;
|
||||
|
||||
return predicted;
|
||||
}
|
||||
|
||||
static int16_t msadpcm_adpcm_expand_nibble_div(VGMSTREAMCHANNEL* stream, uint8_t byte, int shift) {
|
||||
int32_t hist1, hist2, predicted;
|
||||
|
||||
int code = (shift) ?
|
||||
get_high_nibble_signed(byte) :
|
||||
get_low_nibble_signed (byte);
|
||||
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1 * stream->adpcm_coef[0] + hist2 * stream->adpcm_coef[1];
|
||||
predicted = predicted / 256; /* 256 = FIXED_POINT_COEF_BASE */
|
||||
predicted = predicted + (code * stream->adpcm_scale);
|
||||
predicted = clamp16(predicted); /* lNewSample */
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = predicted;
|
||||
|
||||
stream->adpcm_scale = (msadpcm_steps[code & 0xf] * stream->adpcm_scale) / 256; /* 256 = FIXED_POINT_ADAPTION_BASE */
|
||||
if (stream->adpcm_scale < 16) /* min delta */
|
||||
stream->adpcm_scale = 16;
|
||||
|
||||
return predicted;
|
||||
}
|
||||
|
||||
|
||||
void decode_msadpcm_stereo(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t first_sample, int32_t samples_to_do) {
|
||||
VGMSTREAMCHANNEL *stream1, *stream2;
|
||||
uint8_t frame[MSADPCM_MAX_BLOCK_SIZE] = {0};
|
||||
@ -81,40 +131,20 @@ void decode_msadpcm_stereo(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t first
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int ch;
|
||||
uint8_t byte = get_u8(frame+0x07*2+(i-2));
|
||||
|
||||
for (ch = 0; ch < 2; ch++) {
|
||||
VGMSTREAMCHANNEL* stream = &vgmstream->ch[ch];
|
||||
int32_t hist1, hist2, predicted;
|
||||
uint8_t byte = get_u8(frame+0x07*2+(i-2));
|
||||
int sample_nibble = (ch == 0) ? /* L = high nibble first (iErrorDelta) */
|
||||
get_high_nibble_signed(byte) :
|
||||
get_low_nibble_signed (byte);
|
||||
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1 * stream->adpcm_coef[0] + hist2 * stream->adpcm_coef[1];
|
||||
predicted = predicted / 256; /* 256 = FIXED_POINT_COEF_BASE (though MS code uses SHR) */
|
||||
predicted = predicted + (sample_nibble * stream->adpcm_scale);
|
||||
outbuf[0] = clamp16(predicted); /* lNewSample */
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = outbuf[0];
|
||||
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256; /* 256 = FIXED_POINT_ADAPTION_BASE */
|
||||
if (stream->adpcm_scale < 16) /* min delta */
|
||||
stream->adpcm_scale = 16;
|
||||
|
||||
outbuf++;
|
||||
}
|
||||
*outbuf++ = msadpcm_adpcm_expand_nibble_shr(&vgmstream->ch[0], byte, 1); /* L */
|
||||
*outbuf++ = msadpcm_adpcm_expand_nibble_shr(&vgmstream->ch[1], byte, 0); /* R */
|
||||
}
|
||||
}
|
||||
|
||||
void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int config) {
|
||||
VGMSTREAMCHANNEL* stream = &vgmstream->ch[channel];
|
||||
uint8_t frame[MSADPCM_MAX_BLOCK_SIZE] = {0};
|
||||
int i, frames_in;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
off_t frame_offset;
|
||||
int is_shr = (config == 0);
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = vgmstream->frame_size;
|
||||
@ -150,25 +180,12 @@ void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspac
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t hist1, hist2, predicted;
|
||||
uint8_t byte = get_u8(frame+0x07+(i-2)/2);
|
||||
int sample_nibble = (i & 1) ? /* high nibble first */
|
||||
get_low_nibble_signed (byte) :
|
||||
get_high_nibble_signed(byte);
|
||||
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1 * stream->adpcm_coef[0] + hist2 * stream->adpcm_coef[1];
|
||||
predicted = predicted / 256;
|
||||
predicted = predicted + (sample_nibble * stream->adpcm_scale);
|
||||
outbuf[0] = clamp16(predicted);
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = outbuf[0];
|
||||
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256;
|
||||
if (stream->adpcm_scale < 16) /* min delta */
|
||||
stream->adpcm_scale = 16;
|
||||
int shift = !(i & 1); /* high nibble first */
|
||||
|
||||
outbuf[0] = is_shr ?
|
||||
msadpcm_adpcm_expand_nibble_shr(stream, byte, shift) :
|
||||
msadpcm_adpcm_expand_nibble_div(stream, byte, shift);
|
||||
outbuf += channelspacing;
|
||||
}
|
||||
}
|
||||
@ -215,26 +232,11 @@ void decode_msadpcm_ck(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacin
|
||||
}
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||
int32_t hist1,hist2, predicted;
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
uint8_t byte = get_u8(frame+0x07+(i-2)/2);
|
||||
int sample_nibble = (i & 1) ? /* low nibble first, unlike normal MSADPCM */
|
||||
get_high_nibble_signed(byte) :
|
||||
get_low_nibble_signed (byte);
|
||||
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1 * stream->adpcm_coef[0] + hist2 *stream->adpcm_coef[1];
|
||||
predicted = predicted >> 8; /* not DIV unlike spec */
|
||||
predicted = predicted + (sample_nibble * stream->adpcm_scale);
|
||||
outbuf[0] = clamp16(predicted);
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = outbuf[0];
|
||||
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) >> 8; /* not DIV but same here (always >=0) */
|
||||
if (stream->adpcm_scale < 16)
|
||||
stream->adpcm_scale = 16;
|
||||
int shift = (i & 1); /* low nibble first, unlike normal MSADPCM */
|
||||
|
||||
outbuf[0] = msadpcm_adpcm_expand_nibble_shr(stream, byte, shift);
|
||||
outbuf += channelspacing;
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +216,15 @@ void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelsp
|
||||
}
|
||||
}
|
||||
|
||||
size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample) {
|
||||
int32_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample) {
|
||||
if (channels <= 0 || bits_per_sample <= 0) return 0;
|
||||
return ((int64_t)bytes * 8) / channels / bits_per_sample;
|
||||
}
|
||||
|
||||
int32_t pcm16_bytes_to_samples(size_t bytes, int channels) {
|
||||
return pcm_bytes_to_samples(bytes, channels, 16);
|
||||
}
|
||||
|
||||
int32_t pcm8_bytes_to_samples(size_t bytes, int channels) {
|
||||
return pcm_bytes_to_samples(bytes, channels, 8);
|
||||
}
|
||||
|
@ -1290,7 +1290,8 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
||||
if (vgmstream->channels == 1 || vgmstream->coding_type == coding_MSADPCM_int) {
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_msadpcm_mono(vgmstream,buffer+ch,
|
||||
vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch);
|
||||
vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch,
|
||||
vgmstream->codec_config);
|
||||
}
|
||||
}
|
||||
else if (vgmstream->channels == 2) {
|
||||
|
@ -353,7 +353,6 @@ static const char* extension_list[] = {
|
||||
"naac",
|
||||
"nds",
|
||||
"ndp", //fake extension/header id for .nds
|
||||
"ngca",
|
||||
"nlsd",
|
||||
"nop",
|
||||
"nps",
|
||||
@ -921,7 +920,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_DSP_STD, "Nintendo DSP header"},
|
||||
{meta_DSP_CSTR, "Namco Cstr header"},
|
||||
{meta_GCSW, "MileStone GCSW header"},
|
||||
{meta_PS2_SShd, "Sony ADS header"},
|
||||
{meta_ADS, "Sony ADS header"},
|
||||
{meta_NPS, "Namco NPSF header"},
|
||||
{meta_RWSD, "Nintendo RWSD header (single stream)"},
|
||||
{meta_RWAR, "Nintendo RWAR header (single RWAV stream)"},
|
||||
@ -929,7 +928,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_CWAV, "Nintendo CWAV header"},
|
||||
{meta_FWAV, "Nintendo FWAV header"},
|
||||
{meta_XA, "Sony XA header"},
|
||||
{meta_PS2_RXWS, "Sony RXWS header"},
|
||||
{meta_RXWS, "Sony RXWS header"},
|
||||
{meta_RAW_INT, "PS2 .int raw header"},
|
||||
{meta_PS2_OMU, "Alter Echo OMU Header"},
|
||||
{meta_DSP_STM, "Intelligent Systems STM header"},
|
||||
@ -1080,7 +1079,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_PS2_TK5, "Tekken 5 Stream Header"},
|
||||
{meta_PS2_SND, "Might and Magic SSND Header"},
|
||||
{meta_PS2_VSF_TTA, "VSF with SMSS Header"},
|
||||
{meta_ADS, "dhSS Header"},
|
||||
{meta_ADS_MIDWAY, "Midway ADS header"},
|
||||
{meta_PS2_MCG, "Gunvari MCG Header"},
|
||||
{meta_ZSD, "ZSD Header"},
|
||||
{meta_REDSPARK, "RedSpark Header"},
|
||||
@ -1151,7 +1150,6 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_MSF, "Sony MSF header"},
|
||||
{meta_PS3_PAST, "SNDP header"},
|
||||
{meta_SGXD, "Sony SGXD header"},
|
||||
{meta_NGCA, "NGCA header"},
|
||||
{meta_WII_RAS, "RAS header"},
|
||||
{meta_PS2_SPM, "SPM header"},
|
||||
{meta_X360_TRA, "Terminal Reality .TRA raw header"},
|
||||
|
@ -2,15 +2,15 @@
|
||||
#include "../vgmstream.h"
|
||||
|
||||
/* set up for the block at the given offset */
|
||||
void block_update_thp(off_t block_offset, VGMSTREAM *vgmstream) {
|
||||
void block_update_thp(off_t block_offset, VGMSTREAM* vgmstream) {
|
||||
int i, j;
|
||||
STREAMFILE *streamFile = vgmstream->ch[0].streamfile;
|
||||
STREAMFILE* sf = vgmstream->ch[0].streamfile;
|
||||
off_t audio_offset;
|
||||
size_t next_block_size, video_size;
|
||||
|
||||
next_block_size = read_32bitBE(block_offset + 0x00, streamFile);
|
||||
next_block_size = read_u32be(block_offset + 0x00, sf);
|
||||
/* 0x04: frame size previous */
|
||||
video_size = read_32bitBE(block_offset + 0x08,streamFile);
|
||||
video_size = read_u32be(block_offset + 0x08,sf);
|
||||
/* 0x0c: audio size */
|
||||
|
||||
audio_offset = block_offset + 0x10 + video_size;
|
||||
@ -21,21 +21,21 @@ void block_update_thp(off_t block_offset, VGMSTREAM *vgmstream) {
|
||||
|
||||
/* block samples can be smaller than block size, normally in the last block,
|
||||
* but num_samples already takes that into account, so there is no real difference */
|
||||
vgmstream->current_block_size = read_32bitBE(audio_offset + 0x00, streamFile);
|
||||
vgmstream->current_block_samples = read_32bitBE(audio_offset + 0x04, streamFile);
|
||||
vgmstream->current_block_size = read_u32be(audio_offset + 0x00, sf);
|
||||
vgmstream->current_block_samples = read_u32be(audio_offset + 0x04, sf);
|
||||
|
||||
audio_offset += 0x08;
|
||||
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
off_t coef_offset = audio_offset + i*0x20;
|
||||
off_t hist_offset = audio_offset + vgmstream->channels*0x20 + i*0x04;
|
||||
off_t data_offset = audio_offset + vgmstream->channels*0x24 + i*vgmstream->current_block_size;
|
||||
off_t data_offset = audio_offset + 2*0x24 + i*vgmstream->current_block_size; /* reserved for 2 even in mono [WarioWare Inc. (GC)] */
|
||||
|
||||
for (j = 0; j < 16; j++) {
|
||||
vgmstream->ch[i].adpcm_coef[j] = read_16bitBE(coef_offset + (j*0x02),streamFile);
|
||||
vgmstream->ch[i].adpcm_coef[j] = read_s16be(coef_offset + (j*0x02),sf);
|
||||
}
|
||||
vgmstream->ch[i].adpcm_history1_16 = read_16bitBE(hist_offset + 0x00,streamFile);
|
||||
vgmstream->ch[i].adpcm_history2_16 = read_16bitBE(hist_offset + 0x02,streamFile);
|
||||
vgmstream->ch[i].adpcm_history1_16 = read_s16be(hist_offset + 0x00,sf);
|
||||
vgmstream->ch[i].adpcm_history2_16 = read_s16be(hist_offset + 0x02,sf);
|
||||
vgmstream->ch[i].offset = data_offset;
|
||||
}
|
||||
}
|
||||
|
@ -384,10 +384,6 @@
|
||||
RelativePath=".\meta\sfh_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\sqex_scd_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\sqex_sead_streamfile.h"
|
||||
>
|
||||
@ -517,7 +513,7 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ads.c"
|
||||
RelativePath=".\meta\ads_midway.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
@ -1110,10 +1106,6 @@
|
||||
RelativePath=".\meta\vid1.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ngca.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\opus.c"
|
||||
>
|
||||
@ -1414,14 +1406,14 @@
|
||||
RelativePath=".\meta\ps2_rnd.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps2_rstm.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps2_rxws.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps2_rstm.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\rxws.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps2_sfs.c"
|
||||
>
|
||||
|
@ -138,7 +138,6 @@
|
||||
<ClInclude Include="meta\sab_streamfile.h" />
|
||||
<ClInclude Include="meta\riff_ogg_streamfile.h" />
|
||||
<ClInclude Include="meta\sfh_streamfile.h" />
|
||||
<ClInclude Include="meta\sqex_scd_streamfile.h" />
|
||||
<ClInclude Include="meta\sqex_sead_streamfile.h" />
|
||||
<ClInclude Include="meta\txth_streamfile.h" />
|
||||
<ClInclude Include="meta\ubi_bao_streamfile.h" />
|
||||
@ -223,7 +222,6 @@
|
||||
<ClCompile Include="meta\msf_banpresto.c" />
|
||||
<ClCompile Include="meta\msf_konami.c" />
|
||||
<ClCompile Include="meta\msf_tamasoft.c" />
|
||||
<ClCompile Include="meta\ngca.c" />
|
||||
<ClCompile Include="meta\opus.c" />
|
||||
<ClCompile Include="meta\pc_adp.c" />
|
||||
<ClCompile Include="meta\pc_adp_otns.c" />
|
||||
@ -293,7 +291,7 @@
|
||||
<ClCompile Include="meta\acx.c" />
|
||||
<ClCompile Include="meta\adp_konami.c" />
|
||||
<ClCompile Include="meta\adpcm_capcom.c" />
|
||||
<ClCompile Include="meta\ads.c" />
|
||||
<ClCompile Include="meta\ads_midway.c" />
|
||||
<ClCompile Include="meta\adx.c" />
|
||||
<ClCompile Include="meta\afc.c" />
|
||||
<ClCompile Include="meta\agsc.c" />
|
||||
@ -473,7 +471,7 @@
|
||||
<ClCompile Include="meta\vsv.c" />
|
||||
<ClCompile Include="meta\ps2_rnd.c" />
|
||||
<ClCompile Include="meta\ps2_rstm.c" />
|
||||
<ClCompile Include="meta\ps2_rxws.c" />
|
||||
<ClCompile Include="meta\rxws.c" />
|
||||
<ClCompile Include="meta\ps2_sfs.c" />
|
||||
<ClCompile Include="meta\ps2_sl3.c" />
|
||||
<ClCompile Include="meta\ps2_smpl.c" />
|
||||
|
@ -179,9 +179,6 @@
|
||||
<ClInclude Include="meta\sfh_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\sqex_scd_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\sqex_sead_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -391,7 +388,7 @@
|
||||
<ClCompile Include="meta\adpcm_capcom.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ads.c">
|
||||
<ClCompile Include="meta\ads_midway.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\adx.c">
|
||||
@ -916,7 +913,7 @@
|
||||
<ClCompile Include="meta\ps2_rstm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_rxws.c">
|
||||
<ClCompile Include="meta\rxws.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_sfs.c">
|
||||
@ -1585,9 +1582,6 @@
|
||||
<ClCompile Include="meta\silence.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ngca.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\opus.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -132,6 +132,7 @@ typedef struct {
|
||||
/* config */
|
||||
int is_memory;
|
||||
int target_waveid;
|
||||
int target_port;
|
||||
int has_TrackEventTable;
|
||||
int has_CommandTable;
|
||||
|
||||
@ -221,7 +222,7 @@ static void add_acb_name(acb_header* acb, int8_t Streaming) {
|
||||
/* OBJECT HANDLERS */
|
||||
|
||||
static int load_acb_waveform(acb_header* acb, int16_t Index) {
|
||||
uint16_t Id;
|
||||
uint16_t Id, PortNo;
|
||||
uint8_t Streaming;
|
||||
|
||||
/* read Waveform[Index] */
|
||||
@ -231,18 +232,30 @@ static int load_acb_waveform(acb_header* acb, int16_t Index) {
|
||||
if (acb->is_memory) {
|
||||
if (!utf_query_u16(acb->WaveformTable, Index, "MemoryAwbId", &Id))
|
||||
goto fail;
|
||||
PortNo = 0xFFFF;
|
||||
} else {
|
||||
if (!utf_query_u16(acb->WaveformTable, Index, "StreamAwbId", &Id))
|
||||
goto fail;
|
||||
if (!utf_query_u16(acb->WaveformTable, Index, "StreamAwbPortNo", &PortNo))
|
||||
PortNo = 0; /* assumed */
|
||||
}
|
||||
}
|
||||
else {
|
||||
PortNo = 0xFFFF;
|
||||
}
|
||||
|
||||
if (!utf_query_u8(acb->WaveformTable, Index, "Streaming", &Streaming))
|
||||
goto fail;
|
||||
//;VGM_LOG("ACB: Waveform[%i]: Id=%i, Streaming=%i\n", Index, Id, Streaming);
|
||||
//;VGM_LOG("ACB: Waveform[%i]: Id=%i, PortNo=%i, Streaming=%i\n", Index, Id, PortNo, Streaming);
|
||||
|
||||
/* not found but valid */
|
||||
if (Id != acb->target_waveid)
|
||||
return 1;
|
||||
|
||||
/* correct AWB port (check ignored if set to -1) */
|
||||
if (acb->target_port >= 0 && PortNo != 0xFFFF && PortNo != acb->target_port)
|
||||
return 1;
|
||||
|
||||
/* must match our target's (0=memory, 1=streaming, 2=memory (prefetch)+stream) */
|
||||
if ((acb->is_memory && Streaming == 1) || (!acb->is_memory && Streaming == 0))
|
||||
return 1;
|
||||
@ -694,7 +707,7 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
void load_acb_wave_name(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int is_memory) {
|
||||
void load_acb_wave_name(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int port, int is_memory) {
|
||||
acb_header acb = {0};
|
||||
int i, CueName_rows;
|
||||
|
||||
@ -722,9 +735,12 @@ void load_acb_wave_name(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int is
|
||||
* Atom Craft may only target certain .acb versions so some links are later removed
|
||||
* Not all cues to point to Waveforms, some are just config events/commands.
|
||||
* .acb link to .awb by name (loaded manually), though they have a checksum/hash/header to validate.
|
||||
*
|
||||
* .acb can contain info for multiple .awb, that are loaded sequentially and assigned "port numbers" (0 to N).
|
||||
* Both Wave ID and port number must be passed externally to find appropriate song name.
|
||||
*/
|
||||
|
||||
//;VGM_LOG("ACB: find waveid=%i\n", waveid);
|
||||
//;VGM_LOG("ACB: find waveid=%i, port=%i\n", waveid, port);
|
||||
|
||||
acb.acbFile = sf;
|
||||
|
||||
@ -732,6 +748,7 @@ void load_acb_wave_name(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int is
|
||||
if (!acb.Header) goto fail;
|
||||
|
||||
acb.target_waveid = waveid;
|
||||
acb.target_port = port;
|
||||
acb.is_memory = is_memory;
|
||||
acb.has_TrackEventTable = utf_query_data(acb.Header, 0, "TrackEventTable", NULL,NULL);
|
||||
acb.has_CommandTable = utf_query_data(acb.Header, 0, "CommandTable", NULL,NULL);
|
||||
|
@ -1,78 +1,80 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* ADS - from Gauntlet Dark Legacy (GC/Xbox) */
|
||||
VGMSTREAM * init_vgmstream_ads(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, codec;
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"ads")) goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x64685353) /* "dhSS" */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x20,streamFile) != 0x64625353) /* "dbSS" */
|
||||
goto fail;
|
||||
|
||||
loop_flag = 1;
|
||||
channel_count = read_32bitBE(0x10,streamFile);
|
||||
|
||||
if (channel_count > 2)
|
||||
goto fail;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitBE(0x0c,streamFile);
|
||||
|
||||
codec = read_32bitBE(0x08,streamFile);
|
||||
switch (codec) {
|
||||
case 0x00000020: /* GC */
|
||||
start_offset = 0x28 + 0x60 * channel_count;
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->num_samples = read_32bitBE(0x28,streamFile);
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
|
||||
if (channel_count == 1) {
|
||||
vgmstream->layout_type = layout_none;
|
||||
} else if (channel_count == 2) {
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = read_32bitBE(0x14,streamFile);
|
||||
}
|
||||
|
||||
dsp_read_coefs_be(vgmstream, streamFile, 0x44,0x60);
|
||||
break;
|
||||
|
||||
case 0x00000021: /* Xbox */
|
||||
start_offset = 0x28;
|
||||
vgmstream->coding_type = coding_XBOX_IMA_int;
|
||||
vgmstream->num_samples = xbox_ima_bytes_to_samples(read_32bitBE(0x24,streamFile), vgmstream->channels);
|
||||
vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x24;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vgmstream->meta_type = meta_ADS;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* .ADS - from Gauntlet Dark Legacy (GC/Xbox) */
|
||||
VGMSTREAM* init_vgmstream_ads_midway(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channels, codec;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"ads"))
|
||||
goto fail;
|
||||
|
||||
/* fake PS2 .ads but BE */
|
||||
if (!is_id32be(0x00,sf, "dhSS"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x20,sf, "dbSS"))
|
||||
goto fail;
|
||||
|
||||
loop_flag = 1;
|
||||
channels = read_32bitBE(0x10,sf);
|
||||
if (channels > 2)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitBE(0x0c,sf);
|
||||
|
||||
codec = read_32bitBE(0x08,sf);
|
||||
switch (codec) {
|
||||
case 0x00000020: /* GC */
|
||||
start_offset = 0x28 + 0x60 * channels;
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->num_samples = read_32bitBE(0x28,sf);
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
|
||||
if (channels == 1) {
|
||||
vgmstream->layout_type = layout_none;
|
||||
} else if (channels == 2) {
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = read_32bitBE(0x14,sf);
|
||||
}
|
||||
|
||||
dsp_read_coefs_be(vgmstream, sf, 0x44,0x60);
|
||||
break;
|
||||
|
||||
case 0x00000021: /* Xbox */
|
||||
start_offset = 0x28;
|
||||
vgmstream->coding_type = coding_XBOX_IMA_int;
|
||||
vgmstream->num_samples = xbox_ima_bytes_to_samples(read_32bitBE(0x24,sf), vgmstream->channels);
|
||||
vgmstream->layout_type = channels == 1 ? layout_none : layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x24;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vgmstream->meta_type = meta_ADS_MIDWAY;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -190,6 +190,7 @@ fail:
|
||||
|
||||
static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid) {
|
||||
int is_memory = (sf_acb != NULL);
|
||||
int port = 0;
|
||||
|
||||
/* .acb is passed when loading memory .awb inside .acb */
|
||||
if (!is_memory) {
|
||||
@ -198,7 +199,7 @@ static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre
|
||||
int len_name, len_cmp;
|
||||
|
||||
/* try parsing TXTM if present */
|
||||
sf_acb = read_filemap_file(sf, 0);
|
||||
sf_acb = read_filemap_file_pos(sf, 0, &port);
|
||||
|
||||
/* try (name).awb + (name).awb */
|
||||
if (!sf_acb) {
|
||||
@ -233,27 +234,12 @@ static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre
|
||||
}
|
||||
}
|
||||
|
||||
/* try (name)_(name)_R001.awb + (name).acb [Sengoku Basara Battle Party (Mobile)] */
|
||||
if (!sf_acb) {
|
||||
char *cmp = "_R001";
|
||||
get_streamfile_basename(sf, filename, sizeof(filename));
|
||||
len_name = strlen(filename);
|
||||
len_cmp = strlen(cmp);
|
||||
|
||||
if (len_name > len_cmp && strcmp(filename + len_name - len_cmp, cmp) == 0) {
|
||||
filename[(len_name - len_cmp) / 2] = '\0';
|
||||
strcat(filename, ".acb");
|
||||
VGM_LOG("%s\n", filename);
|
||||
sf_acb = open_streamfile_by_filename(sf, filename);
|
||||
}
|
||||
}
|
||||
|
||||
/* probably loaded */
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory);
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory);
|
||||
|
||||
close_streamfile(sf_acb);
|
||||
}
|
||||
else {
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory);
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory);
|
||||
}
|
||||
}
|
||||
|
@ -222,6 +222,7 @@ fail:
|
||||
|
||||
static void load_cpk_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid) {
|
||||
int is_memory = (sf_acb != NULL);
|
||||
int port = -1; /* cpk has no port numbers */
|
||||
|
||||
/* .acb is passed when loading memory .awb inside .acb */
|
||||
if (!is_memory) {
|
||||
@ -238,11 +239,11 @@ static void load_cpk_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre
|
||||
return;
|
||||
|
||||
/* companion .acb probably loaded */
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory);
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory);
|
||||
|
||||
close_streamfile(sf_acb);
|
||||
}
|
||||
else {
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, is_memory);
|
||||
load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory);
|
||||
}
|
||||
}
|
||||
|
110
src/meta/csmp.c
110
src/meta/csmp.c
@ -1,55 +1,55 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* CSMP - Retro Studios sample [Metroid Prime 3 (Wii), Donkey Kong Country Returns (Wii)] */
|
||||
VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, first_offset = 0x08, chunk_offset;
|
||||
int loop_flag, channel_count;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "csmp"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00, streamFile) != 0x43534D50) /* "CSMP" */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x04, streamFile) != 1) /* version? */
|
||||
goto fail;
|
||||
|
||||
if (!find_chunk(streamFile, 0x44415441,first_offset,0, &chunk_offset,NULL, 1, 0)) /*"DATA"*/
|
||||
goto fail;
|
||||
|
||||
/* contains standard DSP header, but somehow some validations (start/loop ps)
|
||||
* don't seem to work, so no point to handle as standard DSP */
|
||||
|
||||
channel_count = 1;
|
||||
loop_flag = read_16bitBE(chunk_offset+0x0c,streamFile);
|
||||
start_offset = chunk_offset + 0x60;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_CSMP;
|
||||
vgmstream->sample_rate = read_32bitBE(chunk_offset+0x08,streamFile);
|
||||
vgmstream->num_samples = read_32bitBE(chunk_offset+0x00,streamFile);
|
||||
vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bitBE(chunk_offset+0x10,streamFile));
|
||||
vgmstream->loop_end_sample = dsp_nibbles_to_samples(read_32bitBE(chunk_offset+0x14,streamFile))+1;
|
||||
if (vgmstream->loop_end_sample > vgmstream->num_samples) /* ? */
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_none;
|
||||
dsp_read_coefs_be(vgmstream, streamFile, chunk_offset+0x1c, 0x00);
|
||||
dsp_read_hist_be(vgmstream, streamFile, chunk_offset+0x40, 0x00);
|
||||
|
||||
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"
|
||||
|
||||
|
||||
/* CSMP - Retro Studios sample [Metroid Prime 3 (Wii), Donkey Kong Country Returns (Wii)] */
|
||||
VGMSTREAM* init_vgmstream_csmp(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, first_offset = 0x08, chunk_offset;
|
||||
int loop_flag, channels;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "csmp"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00, sf, "CSMP"))
|
||||
goto fail;
|
||||
if (read_u32be(0x04, sf) != 1)
|
||||
goto fail;
|
||||
|
||||
if (!find_chunk(sf, 0x44415441,first_offset,0, &chunk_offset,NULL, 1, 0)) /*"DATA"*/
|
||||
goto fail;
|
||||
|
||||
/* contains standard DSP header, but somehow some validations (start/loop ps)
|
||||
* don't seem to work, so no point to handle as standard DSP */
|
||||
|
||||
channels = 1;
|
||||
loop_flag = read_s16be(chunk_offset+0x0c,sf);
|
||||
start_offset = chunk_offset + 0x60;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_CSMP;
|
||||
vgmstream->sample_rate = read_s32be(chunk_offset+0x08,sf);
|
||||
vgmstream->num_samples = read_s32be(chunk_offset+0x00,sf);
|
||||
vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_u32be(chunk_offset+0x10,sf));
|
||||
vgmstream->loop_end_sample = dsp_nibbles_to_samples(read_u32be(chunk_offset+0x14,sf)) + 1;
|
||||
if (vgmstream->loop_end_sample > vgmstream->num_samples) /* ? */
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_none;
|
||||
dsp_read_coefs_be(vgmstream, sf, chunk_offset+0x1c, 0x00);
|
||||
dsp_read_hist_be(vgmstream, sf, chunk_offset+0x40, 0x00);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -44,7 +44,6 @@ VGMSTREAM * init_vgmstream_cstr(STREAMFILE* sf) {
|
||||
loop_flag = (loop_start >= 0);
|
||||
start_offset = 0x20 + 0x60 * channels + first_skip;
|
||||
|
||||
#if 1
|
||||
/* nonlooped tracks may not set first skip for no reason, but can be tested with initial p/s */
|
||||
if (!loop_flag && channels == 2 && first_skip == 0) {
|
||||
while (first_skip < 0x800) {
|
||||
@ -61,7 +60,7 @@ VGMSTREAM * init_vgmstream_cstr(STREAMFILE* sf) {
|
||||
}
|
||||
if (first_skip > 0 && loop_start >= (interleave - first_skip))
|
||||
loop_start = loop_start - (interleave - first_skip);
|
||||
#endif
|
||||
|
||||
loop_start = loop_start * 2;
|
||||
|
||||
/* Mr. Driller oddity, unreliable loop flag */
|
||||
@ -97,7 +96,7 @@ VGMSTREAM * init_vgmstream_cstr(STREAMFILE* sf) {
|
||||
vgmstream->interleave_first_block_size = interleave - first_skip;
|
||||
vgmstream->interleave_first_skip = first_skip;
|
||||
vgmstream->meta_type = meta_DSP_CSTR;
|
||||
VGM_LOG("1=%x, =%x\n",vgmstream->interleave_first_block_size, vgmstream->interleave_first_skip);
|
||||
|
||||
dsp_read_coefs_be(vgmstream, sf, 0x3c, 0x60);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
|
@ -35,6 +35,10 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) {
|
||||
if (get_streamfile_size(sf) <= 0x1000)
|
||||
goto fail;
|
||||
|
||||
/* reject some formats handled elsewhere (better fail and check there than let buggy FFmpeg take over) */
|
||||
if (check_extensions(sf, "at3"))
|
||||
goto fail;
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
|
||||
/* init ffmpeg */
|
||||
|
@ -53,7 +53,7 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) {
|
||||
* This accepts ktsl2asbin with internal data, or opening external streams as subsongs.
|
||||
* Some info from KTSR.bt */
|
||||
|
||||
if (read_u32be(0x00, sf) != 0x4B545352) /* "KTSR" */
|
||||
if (!is_id32be(0x00, sf, "KTSR"))
|
||||
goto fail;
|
||||
if (read_u32be(0x04, sf) != 0x777B481A) /* hash(?) id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */
|
||||
goto fail;
|
||||
@ -482,6 +482,7 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) {
|
||||
case 0xBD888C36: /* config (floats, stream id, etc, may have extended name) */
|
||||
case 0xC9C48EC1: /* unknown (has some string inside like "boss") */
|
||||
case 0xA9D23BF1: /* "state container", some kind of config/floats, witgh names like 'State_bgm01'..N */
|
||||
case 0x836FBECA: /* unknown (~0x300, encrypted? table + data) */
|
||||
break;
|
||||
|
||||
case 0xC5CCCB70: /* sound (internal data or external stream) */
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
typedef enum { PCM16, MSADPCM, DSP_HEAD, DSP_BODY, AT9, MSF } kwb_codec;
|
||||
typedef enum { PCM16, MSADPCM, DSP_HEAD, DSP_BODY, AT9, MSF, XMA2 } kwb_codec;
|
||||
|
||||
typedef struct {
|
||||
int big_endian;
|
||||
@ -187,6 +187,27 @@ static VGMSTREAM* init_vgmstream_koei_wavebank(kwb_header* kwb, STREAMFILE* sf_h
|
||||
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case XMA2: {
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes, block_size, block_count;
|
||||
|
||||
if (kwb->channels > 1) goto fail;
|
||||
|
||||
block_size = 0x800; /* ? */
|
||||
block_count = kwb->stream_size / block_size;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf, sizeof(buf), vgmstream->num_samples, kwb->stream_size, kwb->channels, kwb->sample_rate, block_count, block_size);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf_b, buf,bytes, kwb->stream_offset, kwb->stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, sf_b, kwb->stream_offset, kwb->stream_size, 0, 0,0); /* assumed */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case AT9: {
|
||||
atrac9_config cfg = {0};
|
||||
@ -357,7 +378,7 @@ static int parse_type_k4hd(kwb_header* kwb, off_t offset, off_t body_offset, STR
|
||||
ppva_offset += offset;
|
||||
|
||||
/* PPVA table: */
|
||||
if (read_u32be(ppva_offset + 0x00, sf_h) != 0x50505641) /* "PPVA" */
|
||||
if (!is_id32be(ppva_offset + 0x00, sf_h, "PPVA"))
|
||||
goto fail;
|
||||
|
||||
entry_size = read_u32le(ppva_offset + 0x08, sf_h);
|
||||
@ -403,8 +424,57 @@ fail:
|
||||
}
|
||||
|
||||
static int parse_type_sdsd(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) {
|
||||
/* has Vers, Head, Prog, Smpl sections (like Sony VABs)
|
||||
unknown codec, blocked with some common start, variable sized */
|
||||
off_t smpl_offset, header_offset;
|
||||
int entries, current_subsongs, relative_subsong;
|
||||
size_t entry_size;
|
||||
|
||||
|
||||
/* format somewhat similar to Sony VABs */
|
||||
/* 00: SDsdVers */
|
||||
/* 08: chunk size */
|
||||
/* 0c: null */
|
||||
/* 10: SDsdHead */
|
||||
/* 18: chunk size */
|
||||
/* 1c: ? size */
|
||||
/* 20: null */
|
||||
/* 24: SDsdProg offset ('program'? cues?) */
|
||||
/* 28: SDsdSmpl offset ('samples'? waves?) */
|
||||
/* rest: ? */
|
||||
smpl_offset = read_u32le(offset + 0x28, sf_h);
|
||||
smpl_offset += offset;
|
||||
|
||||
/* Smpl table: */
|
||||
if (!is_id64be(smpl_offset + 0x00, sf_h, "SDsdSmpl"))
|
||||
goto fail;
|
||||
|
||||
/* 0x08: ? */
|
||||
entries = read_u32le(smpl_offset + 0x0c, sf_h);
|
||||
entry_size = 0x9c;
|
||||
|
||||
current_subsongs = kwb->total_subsongs;
|
||||
kwb->total_subsongs += entries;
|
||||
if (kwb->target_subsong - 1 < current_subsongs || kwb->target_subsong > kwb->total_subsongs)
|
||||
return 1;
|
||||
kwb->found = 1;
|
||||
|
||||
relative_subsong = kwb->target_subsong - current_subsongs;
|
||||
header_offset = smpl_offset + 0x10 + (relative_subsong-1) * entry_size;
|
||||
|
||||
kwb->stream_offset = read_u32le(header_offset + 0x00, sf_h);
|
||||
/* 08: ? + channels? */
|
||||
/* 0c: bps? */
|
||||
kwb->sample_rate = read_u32le(header_offset + 0x0c, sf_h);
|
||||
kwb->num_samples = read_u32le(header_offset + 0x10, sf_h) / sizeof(int16_t); /* PCM */
|
||||
/* rest: ? (flags, etc) */
|
||||
kwb->stream_size = read_u32le(header_offset + 0x44, sf_h);
|
||||
|
||||
kwb->codec = XMA2;
|
||||
kwb->channels = 1;
|
||||
|
||||
kwb->stream_offset += body_offset;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -429,8 +499,7 @@ static int parse_type_sdwi(kwb_header* kwb, off_t offset, off_t body_offset, STR
|
||||
smpl_offset += offset;
|
||||
|
||||
/* Smpl table: */
|
||||
if (read_u32be(smpl_offset + 0x00, sf_h) != 0x53447364 && /* "SDsd" */
|
||||
read_u32be(smpl_offset + 0x04, sf_h) != 0x536D706C) /* "Smpl" */
|
||||
if (!is_id64be(smpl_offset + 0x00, sf_h, "SDsdSmpl"))
|
||||
goto fail;
|
||||
|
||||
/* 0x08: ? */
|
||||
@ -534,7 +603,7 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case 0x53447364: /* "SDsd" (PS3? leftover files) */
|
||||
case 0x53447364: /* "SDsd" [Bladestorm Nightmare (PC)-X360 leftover files] */
|
||||
if (!parse_type_sdsd(kwb, head_offset, body_offset, sf_h))
|
||||
goto fail;
|
||||
break;
|
||||
|
@ -66,8 +66,8 @@ VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_rfrm(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile);
|
||||
VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_ads_container(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_nps(STREAMFILE *streamFile);
|
||||
|
||||
@ -79,8 +79,8 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ps2_rxw(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_rxws(STREAMFILE* sf);
|
||||
VGMSTREAM * init_vgmstream_rxws_badrip(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_raw_int(STREAMFILE *streamFile);
|
||||
|
||||
@ -384,7 +384,7 @@ VGMSTREAM * init_vgmstream_ps2_tk1(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_vsf_tta(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ads(STREAMFILE *streamFile);
|
||||
VGMSTREAM* init_vgmstream_ads_midway(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_mcg(STREAMFILE *streamFile);
|
||||
|
||||
@ -508,8 +508,6 @@ VGMSTREAM * init_vgmstream_ps3_past(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_sgxd(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ngca(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_wii_ras(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_spm(STREAMFILE* streamFile);
|
||||
@ -848,7 +846,7 @@ VGMSTREAM * init_vgmstream_awb(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_awb_memory(STREAMFILE * streamFile, STREAMFILE *acbFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_acb(STREAMFILE * streamFile);
|
||||
void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int is_memory);
|
||||
void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int port, int is_memory);
|
||||
|
||||
VGMSTREAM * init_vgmstream_rad(STREAMFILE * streamFile);
|
||||
|
||||
|
@ -1,62 +1,62 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* WMSF - Banpresto MSFx wrapper [Dai-2-Ji Super Robot Taisen OG: The Moon Dwellers (PS3)] */
|
||||
VGMSTREAM * init_vgmstream_msf_banpresto_wmsf(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
off_t subfile_offset = 0x10;
|
||||
size_t subfile_size = get_streamfile_size(streamFile) - subfile_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
if ( !check_extensions(streamFile,"msf"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x574D5346) /* "WMSF" */
|
||||
goto fail;
|
||||
/* 0x04: size, 0x08: flags? 0x0c: null? */
|
||||
|
||||
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_msf(temp_streamFile);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(temp_streamFile);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 2MSF - Banpresto RIFF wrapper [Dai-2-Ji Super Robot Taisen OG: The Moon Dwellers (PS4)] */
|
||||
VGMSTREAM * init_vgmstream_msf_banpresto_2msf(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
off_t subfile_offset = 0x14;
|
||||
size_t subfile_size = get_streamfile_size(streamFile) - subfile_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
if ( !check_extensions(streamFile,"at9"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x324D5346) /* "2MSF" */
|
||||
goto fail;
|
||||
/* 0x04: size, 0x08: flags? 0x0c: null?, 0x10: 0x01? (BE values even though RIFF is LE) */
|
||||
|
||||
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_riff(temp_streamFile);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(temp_streamFile);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* WMSF - Banpresto MSFx wrapper [Dai-2-Ji Super Robot Taisen OG: The Moon Dwellers (PS3)] */
|
||||
VGMSTREAM* init_vgmstream_msf_banpresto_wmsf(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t subfile_offset = 0x10;
|
||||
size_t subfile_size = get_streamfile_size(sf) - subfile_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"msf"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf,"WMSF"))
|
||||
goto fail;
|
||||
/* 0x04: size, 0x08: flags? 0x0c: null? */
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, NULL);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_msf(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 2MSF - Banpresto RIFF wrapper [Dai-2-Ji Super Robot Taisen OG: The Moon Dwellers (PS4)] */
|
||||
VGMSTREAM* init_vgmstream_msf_banpresto_2msf(STREAMFILE *sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE*temp_sf = NULL;
|
||||
off_t subfile_offset = 0x14;
|
||||
size_t subfile_size = get_streamfile_size(sf) - subfile_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
if ( !check_extensions(sf,"at9"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf,"2MSF"))
|
||||
goto fail;
|
||||
/* 0x04: size, 0x08: flags? 0x0c: null?, 0x10: 0x01? (BE values even though RIFF is LE) */
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, NULL);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_riff(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,58 +1,58 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* MSFC - Konami (Armature?) variation [Metal Gear Solid 2 HD (PS3), Metal Gear Solid 3 HD (PS3)] */
|
||||
VGMSTREAM * init_vgmstream_msf_konami(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
uint32_t codec;
|
||||
int loop_flag, channel_count, sample_rate;
|
||||
size_t data_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"msf"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x4D534643) /* "MSFC" */
|
||||
goto fail;
|
||||
|
||||
start_offset = 0x20;
|
||||
|
||||
codec = read_32bitBE(0x04,streamFile);
|
||||
channel_count = read_32bitBE(0x08,streamFile);
|
||||
sample_rate = read_32bitBE(0x0c,streamFile);
|
||||
data_size = read_32bitBE(0x10,streamFile); /* without header */
|
||||
if (data_size + start_offset != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MSF_KONAMI;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
switch (codec) {
|
||||
case 0x01:
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
|
||||
/* MSFC - Konami (Armature?) variation [Metal Gear Solid 2 HD (PS3), Metal Gear Solid 3 HD (PS3)] */
|
||||
VGMSTREAM* init_vgmstream_msf_konami(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
uint32_t codec;
|
||||
int loop_flag, channels, sample_rate;
|
||||
size_t data_size;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"msf"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf,"MSFC"))
|
||||
goto fail;
|
||||
|
||||
start_offset = 0x20;
|
||||
|
||||
codec = read_u32be(0x04,sf);
|
||||
channels = read_s32be(0x08,sf);
|
||||
sample_rate = read_s32be(0x0c,sf);
|
||||
data_size = read_u32be(0x10,sf); /* without header */
|
||||
if (data_size + start_offset != get_streamfile_size(sf))
|
||||
goto fail;
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MSF_KONAMI;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
switch (codec) {
|
||||
case 0x01:
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channels);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,68 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* NGCA (from GoldenEye 007) */
|
||||
VGMSTREAM * init_vgmstream_ngca(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("ngca",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x4E474341) /* "NGCA" */
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = 1;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = 0x40;
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = 32000;
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->num_samples = (((read_32bitBE(0x4,streamFile))/2) - 1) / 8 * 14;
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_NGCA;
|
||||
vgmstream->allow_dual_stereo = 1;
|
||||
|
||||
if (vgmstream->coding_type == coding_NGC_DSP) {
|
||||
int i;
|
||||
for (i=0;i<16;i++) {
|
||||
vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0xC+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;
|
||||
}
|
@ -1,368 +1,368 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* .ADS - Sony's "Audio Stream" format [Edit Racing (PS2), Evergrace II (PS2), Pri-Saga! Portable (PSP)] */
|
||||
VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, sample_rate, interleave, is_loop_samples = 0;
|
||||
size_t body_size, stream_size, file_size;
|
||||
uint32_t codec, loop_start_sample = 0, loop_end_sample = 0, loop_start_offset = 0, loop_end_offset = 0;
|
||||
coding_t coding_type;
|
||||
int ignore_silent_frame_cavia = 0, ignore_silent_frame_capcom = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .ads: actual extension
|
||||
* .ss2: demuxed videos (fake?)
|
||||
* .pcm: Taisho Mononoke Ibunroku (PS2)
|
||||
* .adx: Armored Core 3 (PS2)
|
||||
* [no actual extension]: MotoGP (PS2)
|
||||
* .800: Mobile Suit Gundam: The One Year War (PS2) */
|
||||
if (!check_extensions(streamFile, "ads,ss2,pcm,adx,,800"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x53536864 && /* "SShd" */
|
||||
read_32bitBE(0x20,streamFile) != 0x53536264) /* "SSbd" */
|
||||
goto fail;
|
||||
if (read_32bitLE(0x04,streamFile) != 0x18 && /* standard header size */
|
||||
read_32bitLE(0x04,streamFile) != 0x20) /* True Fortune (PS2) */
|
||||
goto fail;
|
||||
|
||||
|
||||
/* base values (a bit unorderly since devs hack ADS too much and detection is messy) */
|
||||
{
|
||||
codec = read_32bitLE(0x08,streamFile);
|
||||
sample_rate = read_32bitLE(0x0C,streamFile);
|
||||
channel_count = read_32bitLE(0x10,streamFile); /* up to 4 [Eve of Extinction (PS2)] */
|
||||
interleave = read_32bitLE(0x14,streamFile); /* set even when mono */
|
||||
|
||||
|
||||
switch(codec) {
|
||||
case 0x01: /* official definition */
|
||||
case 0x80000001: /* [Evergrace II (PS2), but not other From Soft games] */
|
||||
coding_type = coding_PCM16LE;
|
||||
|
||||
/* Angel Studios/Rockstar San Diego videos codec hijack [Red Dead Revolver (PS2), Spy Hunter 2 (PS2)] */
|
||||
if (sample_rate == 12000 && interleave == 0x200) {
|
||||
sample_rate = 48000;
|
||||
interleave = 0x40;
|
||||
coding_type = coding_DVI_IMA_int;
|
||||
/* should try to detect IMA data but it's not so easy, this works ok since
|
||||
* no known games use these settings, videos normally are 48000/24000hz */
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x10: /* official definition */
|
||||
case 0x02: /* Capcom games extension, stereo only [Megaman X7 (PS2), Breath of Fire V (PS2), Clock Tower 3 (PS2)] */
|
||||
coding_type = coding_PSX;
|
||||
break;
|
||||
|
||||
case 0x00: /* PCM16BE from official docs, probably never used */
|
||||
default:
|
||||
VGM_LOG("ADS: unknown codec\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* sizes */
|
||||
{
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
body_size = read_32bitLE(0x24,streamFile);
|
||||
|
||||
/* bigger than file_size in rare cases, even if containing all data (ex. Megaman X7's SY04.ADS) */
|
||||
if (body_size + 0x28 > file_size) {
|
||||
body_size = file_size - 0x28;
|
||||
}
|
||||
|
||||
/* True Fortune: weird stream size */
|
||||
if (body_size * 2 == file_size - 0x18) {
|
||||
body_size = (body_size * 2) - 0x10;
|
||||
}
|
||||
|
||||
stream_size = body_size;
|
||||
}
|
||||
|
||||
|
||||
/* offset */
|
||||
{
|
||||
start_offset = 0x28;
|
||||
|
||||
/* start padding (body size is ok, may have end padding) [Evergrace II (PS2), Armored Core 3 (PS2)] */
|
||||
/* detection depends on files being properly ripped, so broken/cut files won't play ok */
|
||||
if (file_size - body_size >= 0x800) {
|
||||
start_offset = 0x800; /* aligned to sector */
|
||||
|
||||
/* too much end padding, happens in Super Galdelic Hour's SEL.ADS, maybe in bad rips too */
|
||||
VGM_ASSERT(file_size - body_size > 0x8000, "ADS: big end padding %x\n", file_size - body_size);
|
||||
}
|
||||
|
||||
/* "ADSC" container */
|
||||
if (coding_type == coding_PSX
|
||||
&& read_32bitLE(0x28,streamFile) == 0x1000 /* real start */
|
||||
&& read_32bitLE(0x2c,streamFile) == 0
|
||||
&& read_32bitLE(0x1008,streamFile) != 0) {
|
||||
int i;
|
||||
int is_adsc = 1;
|
||||
|
||||
/* should be empty up to data start */
|
||||
for (i = 0; i < 0xFDC/4; i++) {
|
||||
if (read_32bitLE(0x2c+(i*4),streamFile) != 0) {
|
||||
is_adsc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_adsc) {
|
||||
start_offset = 0x1000 - 0x08; /* remove "ADSC" alignment */
|
||||
/* stream_size doesn't count start offset padding */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* loops */
|
||||
{
|
||||
uint32_t loop_start, loop_end;
|
||||
|
||||
loop_start = read_32bitLE(0x18,streamFile);
|
||||
loop_end = read_32bitLE(0x1C,streamFile);
|
||||
|
||||
loop_flag = 0;
|
||||
|
||||
/* detect loops the best we can; docs say those are loop block addresses,
|
||||
* but each maker does whatever (no games seem to use PS-ADPCM loop flags though) */
|
||||
|
||||
|
||||
if (loop_start != 0xFFFFFFFF && loop_end == 0xFFFFFFFF) {
|
||||
|
||||
if (codec == 0x02) { /* Capcom codec */
|
||||
/* Capcom games: loop_start is address * 0x10 [Mega Man X7, Breath of Fire V, Clock Tower 3] */
|
||||
loop_flag = ((loop_start * 0x10) + 0x200 < body_size); /* near the end (+0x20~80) means no loop */
|
||||
loop_start_offset = loop_start * 0x10;
|
||||
ignore_silent_frame_capcom = 1;
|
||||
}
|
||||
else if (read_32bitBE(0x28,streamFile) == 0x50414421) { /* "PAD!" padding until 0x800 */
|
||||
/* Super Galdelic Hour: loop_start is PCM bytes */
|
||||
loop_flag = 1;
|
||||
loop_start_sample = loop_start / 2 / channel_count;
|
||||
is_loop_samples = 1;
|
||||
}
|
||||
else if ((loop_start % 0x800 == 0) && loop_start > 0) { /* sector-aligned, min/0 is 0x800 */
|
||||
/* cavia games: loop_start is offset [Drakengard 1/2, GITS: Stand Alone Complex] */
|
||||
/* offset is absolute from the "cavia stream format" container that adjusts ADS start */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start - 0x800;
|
||||
ignore_silent_frame_cavia = 1;
|
||||
}
|
||||
else if (loop_start % 0x800 != 0 || loop_start == 0) { /* not sector aligned */
|
||||
/* Katakamuna: loop_start is address * 0x10 */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start * 0x10;
|
||||
}
|
||||
}
|
||||
else if (loop_start != 0xFFFFFFFF && loop_end != 0xFFFFFFFF
|
||||
&& loop_end > 0) { /* ignore Kamen Rider Blade and others */
|
||||
#if 0
|
||||
//todo improve detection to avoid clashing with address*0x20
|
||||
if (loop_end == body_size / 0x10) { /* always body_size? but not all files should loop */
|
||||
/* Akane Iro ni Somaru Saka - Parallel: loops is address * 0x10 */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start * 0x10;
|
||||
loop_end_offset = loop_end * 0x10;
|
||||
}
|
||||
#endif
|
||||
if (loop_end <= body_size / 0x200 && coding_type == coding_PCM16LE) { /* close to body_size */
|
||||
/* Gofun-go no Sekai: loops is address * 0x200 */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start * 0x200;
|
||||
loop_end_offset = loop_end * 0x200;
|
||||
}
|
||||
else if (loop_end <= body_size / 0x70 && coding_type == coding_PCM16LE) { /* close to body_size */
|
||||
/* Armored Core - Nexus: loops is address * 0x70 */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start * 0x70;
|
||||
loop_end_offset = loop_end * 0x70;
|
||||
}
|
||||
else if (loop_end <= body_size / 0x20 && coding_type == coding_PCM16LE) { /* close to body_size */
|
||||
/* Armored Core - Nine Breaker: loops is address * 0x20 */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start * 0x20;
|
||||
loop_end_offset = loop_end * 0x20;
|
||||
}
|
||||
else if (loop_end <= body_size / 0x20 && coding_type == coding_PSX) {
|
||||
/* various games: loops is address * 0x20 [Fire Pro Wrestling Returns, A.C.E. - Another Century's Episode] */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start * 0x20;
|
||||
loop_end_offset = loop_end * 0x20;
|
||||
}
|
||||
else if (loop_end <= body_size / 0x10 && coding_type == coding_PSX
|
||||
&& (read_32bitBE(0x28 + loop_end*0x10 + 0x10 + 0x00, streamFile) == 0x00077777 ||
|
||||
read_32bitBE(0x28 + loop_end*0x10 + 0x20 + 0x00, streamFile) == 0x00077777)) {
|
||||
/* not-quite-looping sfx, ending with a "non-looping PS-ADPCM end frame" [Kono Aozora ni Yakusoku, Chanter] */
|
||||
loop_flag = 0;
|
||||
}
|
||||
else if ((loop_end > body_size / 0x20 && coding_type == coding_PSX) ||
|
||||
(loop_end > body_size / 0x70 && coding_type == coding_PCM16LE)) {
|
||||
/* various games: loops in samples [Eve of Extinction, Culdcept, WWE Smackdown! 3] */
|
||||
loop_flag = 1;
|
||||
loop_start_sample = loop_start;
|
||||
loop_end_sample = loop_end;
|
||||
is_loop_samples = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//todo Jet Ion Grand Prix seems to have some loop-like values at 0x28
|
||||
//todo Yoake mae yori Ruriiro na has loops in unknown format
|
||||
}
|
||||
|
||||
|
||||
/* most games have empty PS-ADPCM frames in the last interleave block that should be skipped for smooth looping */
|
||||
if (coding_type == coding_PSX) {
|
||||
off_t offset, min_offset;
|
||||
|
||||
offset = start_offset + stream_size;
|
||||
min_offset = offset - interleave;
|
||||
|
||||
do {
|
||||
offset -= 0x10;
|
||||
|
||||
if (read_8bit(offset+0x01,streamFile) == 0x07) {
|
||||
stream_size -= 0x10*channel_count;/* ignore don't decode flag/padding frame (most common) [ex. Capcom games] */
|
||||
}
|
||||
else if (read_32bitBE(offset+0x00,streamFile) == 0x00000000 && read_32bitBE(offset+0x04,streamFile) == 0x00000000 &&
|
||||
read_32bitBE(offset+0x08,streamFile) == 0x00000000 && read_32bitBE(offset+0x0c,streamFile) == 0x00000000) {
|
||||
stream_size -= 0x10*channel_count; /* ignore null frame [ex. A.C.E. Another Century Episode 1/2/3] */
|
||||
}
|
||||
else if (read_32bitBE(offset+0x00,streamFile) == 0x00007777 && read_32bitBE(offset+0x04,streamFile) == 0x77777777 &&
|
||||
read_32bitBE(offset+0x08,streamFile) == 0x77777777 && read_32bitBE(offset+0x0c,streamFile) == 0x77777777) {
|
||||
stream_size -= 0x10*channel_count; /* ignore padding frame [ex. Akane Iro ni Somaru Saka - Parallel] */
|
||||
}
|
||||
else if (read_32bitBE(offset+0x00,streamFile) == 0x0C020000 && read_32bitBE(offset+0x04,streamFile) == 0x00000000 &&
|
||||
read_32bitBE(offset+0x08,streamFile) == 0x00000000 && read_32bitBE(offset+0x0c,streamFile) == 0x00000000 &&
|
||||
ignore_silent_frame_cavia) {
|
||||
stream_size -= 0x10*channel_count; /* ignore silent frame [ex. cavia games] */
|
||||
}
|
||||
else if (read_32bitBE(offset+0x00,streamFile) == 0x0C010000 && read_32bitBE(offset+0x04,streamFile) == 0x00000000 &&
|
||||
read_32bitBE(offset+0x08,streamFile) == 0x00000000 && read_32bitBE(offset+0x0c,streamFile) == 0x00000000 &&
|
||||
ignore_silent_frame_capcom) {
|
||||
stream_size -= 0x10*channel_count; /* ignore silent frame [ex. Capcom games] */
|
||||
}
|
||||
else {
|
||||
break; /* standard frame */
|
||||
}
|
||||
}
|
||||
while(offset > min_offset);
|
||||
|
||||
/* don't bother fixing loop_end_offset since will be adjusted to num_samples later, if needed */
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->coding_type = coding_type;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->meta_type = meta_PS2_SShd;
|
||||
|
||||
switch(coding_type) {
|
||||
case coding_PCM16LE:
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
|
||||
break;
|
||||
case coding_PSX:
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count);
|
||||
break;
|
||||
case coding_DVI_IMA_int:
|
||||
vgmstream->num_samples = ima_bytes_to_samples(stream_size, channel_count);
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (vgmstream->loop_flag) {
|
||||
if (is_loop_samples) {
|
||||
vgmstream->loop_start_sample = loop_start_sample;
|
||||
vgmstream->loop_end_sample = loop_end_sample;
|
||||
}
|
||||
else {
|
||||
switch(vgmstream->coding_type) {
|
||||
case coding_PCM16LE:
|
||||
vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start_offset,channel_count,16);
|
||||
vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end_offset,channel_count,16);
|
||||
break;
|
||||
case coding_PSX:
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start_offset,channel_count);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end_offset,channel_count);
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* when loop_end = 0xFFFFFFFF */
|
||||
if (vgmstream->loop_end_sample == 0)
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
/* happens even when loops are directly samples, loops sound fine (ex. Culdcept) */
|
||||
if (vgmstream->loop_end_sample > vgmstream->num_samples)
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ****************************************************************************** */
|
||||
|
||||
/* ADS in containers */
|
||||
VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
off_t subfile_offset;
|
||||
size_t subfile_size;
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "ads"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) == 0x41445343 && /* "ADSC" */
|
||||
read_32bitBE(0x04,streamFile) == 0x01000000) {
|
||||
/* Kenka Bancho 2, Kamen Rider Hibiki/Kabuto, Shinjuku no Okami */
|
||||
subfile_offset = 0x08;
|
||||
}
|
||||
else if (read_32bitBE(0x00,streamFile) == 0x63617669 && /* "cavi" */
|
||||
read_32bitBE(0x04,streamFile) == 0x61207374 && /* "a st" */
|
||||
read_32bitBE(0x08,streamFile) == 0x7265616D) { /* "ream" */
|
||||
/* cavia games: Drakengard 1/2, Dragon Quest Yangus, GITS: Stand Alone Complex */
|
||||
subfile_offset = 0x7d8;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
subfile_size = get_streamfile_size(streamFile) - subfile_offset;
|
||||
|
||||
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_ps2_ads(temp_streamFile);
|
||||
close_streamfile(temp_streamFile);
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* .ADS - Sony's "Audio Stream" format [Edit Racing (PS2), Evergrace II (PS2), Pri-Saga! Portable (PSP)] */
|
||||
VGMSTREAM* init_vgmstream_ads(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channels, sample_rate, interleave, is_loop_samples = 0;
|
||||
size_t body_size, stream_size, file_size;
|
||||
uint32_t codec, loop_start_sample = 0, loop_end_sample = 0, loop_start_offset = 0, loop_end_offset = 0;
|
||||
coding_t coding_type;
|
||||
int ignore_silent_frame_cavia = 0, ignore_silent_frame_capcom = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .ads: actual extension
|
||||
* .ss2: demuxed videos (fake?)
|
||||
* .pcm: Taisho Mononoke Ibunroku (PS2)
|
||||
* .adx: Armored Core 3 (PS2)
|
||||
* (extensionless): MotoGP (PS2)
|
||||
* .800: Mobile Suit Gundam: The One Year War (PS2) */
|
||||
if (!check_extensions(sf, "ads,ss2,pcm,adx,,800"))
|
||||
goto fail;
|
||||
|
||||
if (!is_id32be(0x00,sf,"SShd") &&
|
||||
!is_id32be(0x20,sf,"SSbd"))
|
||||
goto fail;
|
||||
if (read_32bitLE(0x04,sf) != 0x18 && /* standard header size */
|
||||
read_32bitLE(0x04,sf) != 0x20) /* True Fortune (PS2) */
|
||||
goto fail;
|
||||
|
||||
|
||||
/* base values (a bit unorderly since devs hack ADS too much and detection is messy) */
|
||||
{
|
||||
codec = read_32bitLE(0x08,sf);
|
||||
sample_rate = read_32bitLE(0x0C,sf);
|
||||
channels = read_32bitLE(0x10,sf); /* up to 4 [Eve of Extinction (PS2)] */
|
||||
interleave = read_32bitLE(0x14,sf); /* set even when mono */
|
||||
|
||||
|
||||
switch(codec) {
|
||||
case 0x01: /* official definition */
|
||||
case 0x80000001: /* [Evergrace II (PS2), but not other From Soft games] */
|
||||
coding_type = coding_PCM16LE;
|
||||
|
||||
/* Angel Studios/Rockstar San Diego videos codec hijack [Red Dead Revolver (PS2), Spy Hunter 2 (PS2)] */
|
||||
if (sample_rate == 12000 && interleave == 0x200) {
|
||||
sample_rate = 48000;
|
||||
interleave = 0x40;
|
||||
coding_type = coding_DVI_IMA_int;
|
||||
/* should try to detect IMA data but it's not so easy, this works ok since
|
||||
* no known games use these settings, videos normally are 48000/24000hz */
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x10: /* official definition */
|
||||
case 0x02: /* Capcom games extension, stereo only [Megaman X7 (PS2), Breath of Fire V (PS2), Clock Tower 3 (PS2)] */
|
||||
coding_type = coding_PSX;
|
||||
break;
|
||||
|
||||
case 0x00: /* PCM16BE from official docs, probably never used */
|
||||
default:
|
||||
VGM_LOG("ADS: unknown codec\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* sizes */
|
||||
{
|
||||
file_size = get_streamfile_size(sf);
|
||||
body_size = read_32bitLE(0x24,sf);
|
||||
|
||||
/* bigger than file_size in rare cases, even if containing all data (ex. Megaman X7's SY04.ADS) */
|
||||
if (body_size + 0x28 > file_size) {
|
||||
body_size = file_size - 0x28;
|
||||
}
|
||||
|
||||
/* True Fortune: weird stream size */
|
||||
if (body_size * 2 == file_size - 0x18) {
|
||||
body_size = (body_size * 2) - 0x10;
|
||||
}
|
||||
|
||||
stream_size = body_size;
|
||||
}
|
||||
|
||||
|
||||
/* offset */
|
||||
{
|
||||
start_offset = 0x28;
|
||||
|
||||
/* start padding (body size is ok, may have end padding) [Evergrace II (PS2), Armored Core 3 (PS2)] */
|
||||
/* detection depends on files being properly ripped, so broken/cut files won't play ok */
|
||||
if (file_size - body_size >= 0x800) {
|
||||
start_offset = 0x800; /* aligned to sector */
|
||||
|
||||
/* too much end padding, happens in Super Galdelic Hour's SEL.ADS, maybe in bad rips too */
|
||||
VGM_ASSERT(file_size - body_size > 0x8000, "ADS: big end padding %x\n", file_size - body_size);
|
||||
}
|
||||
|
||||
/* "ADSC" container */
|
||||
if (coding_type == coding_PSX
|
||||
&& read_32bitLE(0x28,sf) == 0x1000 /* real start */
|
||||
&& read_32bitLE(0x2c,sf) == 0
|
||||
&& read_32bitLE(0x1008,sf) != 0) {
|
||||
int i;
|
||||
int is_adsc = 1;
|
||||
|
||||
/* should be empty up to data start */
|
||||
for (i = 0; i < 0xFDC/4; i++) {
|
||||
if (read_32bitLE(0x2c+(i*4),sf) != 0) {
|
||||
is_adsc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_adsc) {
|
||||
start_offset = 0x1000 - 0x08; /* remove "ADSC" alignment */
|
||||
/* stream_size doesn't count start offset padding */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* loops */
|
||||
{
|
||||
uint32_t loop_start, loop_end;
|
||||
|
||||
loop_start = read_32bitLE(0x18,sf);
|
||||
loop_end = read_32bitLE(0x1C,sf);
|
||||
|
||||
loop_flag = 0;
|
||||
|
||||
/* detect loops the best we can; docs say those are loop block addresses,
|
||||
* but each maker does whatever (no games seem to use PS-ADPCM loop flags though) */
|
||||
|
||||
|
||||
if (loop_start != 0xFFFFFFFF && loop_end == 0xFFFFFFFF) {
|
||||
|
||||
if (codec == 0x02) { /* Capcom codec */
|
||||
/* Capcom games: loop_start is address * 0x10 [Mega Man X7, Breath of Fire V, Clock Tower 3] */
|
||||
loop_flag = ((loop_start * 0x10) + 0x200 < body_size); /* near the end (+0x20~80) means no loop */
|
||||
loop_start_offset = loop_start * 0x10;
|
||||
ignore_silent_frame_capcom = 1;
|
||||
}
|
||||
else if (read_32bitBE(0x28,sf) == 0x50414421) { /* "PAD!" padding until 0x800 */
|
||||
/* Super Galdelic Hour: loop_start is PCM bytes */
|
||||
loop_flag = 1;
|
||||
loop_start_sample = loop_start / 2 / channels;
|
||||
is_loop_samples = 1;
|
||||
}
|
||||
else if ((loop_start % 0x800 == 0) && loop_start > 0) { /* sector-aligned, min/0 is 0x800 */
|
||||
/* cavia games: loop_start is offset [Drakengard 1/2, GITS: Stand Alone Complex] */
|
||||
/* offset is absolute from the "cavia stream format" container that adjusts ADS start */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start - 0x800;
|
||||
ignore_silent_frame_cavia = 1;
|
||||
}
|
||||
else if (loop_start % 0x800 != 0 || loop_start == 0) { /* not sector aligned */
|
||||
/* Katakamuna: loop_start is address * 0x10 */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start * 0x10;
|
||||
}
|
||||
}
|
||||
else if (loop_start != 0xFFFFFFFF && loop_end != 0xFFFFFFFF
|
||||
&& loop_end > 0) { /* ignore Kamen Rider Blade and others */
|
||||
#if 0
|
||||
//todo improve detection to avoid clashing with address*0x20
|
||||
if (loop_end == body_size / 0x10) { /* always body_size? but not all files should loop */
|
||||
/* Akane Iro ni Somaru Saka - Parallel: loops is address * 0x10 */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start * 0x10;
|
||||
loop_end_offset = loop_end * 0x10;
|
||||
}
|
||||
#endif
|
||||
if (loop_end <= body_size / 0x200 && coding_type == coding_PCM16LE) { /* close to body_size */
|
||||
/* Gofun-go no Sekai: loops is address * 0x200 */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start * 0x200;
|
||||
loop_end_offset = loop_end * 0x200;
|
||||
}
|
||||
else if (loop_end <= body_size / 0x70 && coding_type == coding_PCM16LE) { /* close to body_size */
|
||||
/* Armored Core - Nexus: loops is address * 0x70 */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start * 0x70;
|
||||
loop_end_offset = loop_end * 0x70;
|
||||
}
|
||||
else if (loop_end <= body_size / 0x20 && coding_type == coding_PCM16LE) { /* close to body_size */
|
||||
/* Armored Core - Nine Breaker: loops is address * 0x20 */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start * 0x20;
|
||||
loop_end_offset = loop_end * 0x20;
|
||||
}
|
||||
else if (loop_end <= body_size / 0x20 && coding_type == coding_PSX) {
|
||||
/* various games: loops is address * 0x20 [Fire Pro Wrestling Returns, A.C.E. - Another Century's Episode] */
|
||||
loop_flag = 1;
|
||||
loop_start_offset = loop_start * 0x20;
|
||||
loop_end_offset = loop_end * 0x20;
|
||||
}
|
||||
else if (loop_end <= body_size / 0x10 && coding_type == coding_PSX
|
||||
&& (read_32bitBE(0x28 + loop_end*0x10 + 0x10 + 0x00, sf) == 0x00077777 ||
|
||||
read_32bitBE(0x28 + loop_end*0x10 + 0x20 + 0x00, sf) == 0x00077777)) {
|
||||
/* not-quite-looping sfx, ending with a "non-looping PS-ADPCM end frame" [Kono Aozora ni Yakusoku, Chanter] */
|
||||
loop_flag = 0;
|
||||
}
|
||||
else if ((loop_end > body_size / 0x20 && coding_type == coding_PSX) ||
|
||||
(loop_end > body_size / 0x70 && coding_type == coding_PCM16LE)) {
|
||||
/* various games: loops in samples [Eve of Extinction, Culdcept, WWE Smackdown! 3] */
|
||||
loop_flag = 1;
|
||||
loop_start_sample = loop_start;
|
||||
loop_end_sample = loop_end;
|
||||
is_loop_samples = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//todo Jet Ion Grand Prix seems to have some loop-like values at 0x28
|
||||
//todo Yoake mae yori Ruriiro na has loops in unknown format
|
||||
}
|
||||
|
||||
|
||||
/* most games have empty PS-ADPCM frames in the last interleave block that should be skipped for smooth looping */
|
||||
if (coding_type == coding_PSX) {
|
||||
off_t offset, min_offset;
|
||||
|
||||
offset = start_offset + stream_size;
|
||||
min_offset = offset - interleave;
|
||||
|
||||
do {
|
||||
offset -= 0x10;
|
||||
|
||||
if (read_8bit(offset+0x01,sf) == 0x07) {
|
||||
stream_size -= 0x10*channels;/* ignore don't decode flag/padding frame (most common) [ex. Capcom games] */
|
||||
}
|
||||
else if (read_32bitBE(offset+0x00,sf) == 0x00000000 && read_32bitBE(offset+0x04,sf) == 0x00000000 &&
|
||||
read_32bitBE(offset+0x08,sf) == 0x00000000 && read_32bitBE(offset+0x0c,sf) == 0x00000000) {
|
||||
stream_size -= 0x10*channels; /* ignore null frame [ex. A.C.E. Another Century Episode 1/2/3] */
|
||||
}
|
||||
else if (read_32bitBE(offset+0x00,sf) == 0x00007777 && read_32bitBE(offset+0x04,sf) == 0x77777777 &&
|
||||
read_32bitBE(offset+0x08,sf) == 0x77777777 && read_32bitBE(offset+0x0c,sf) == 0x77777777) {
|
||||
stream_size -= 0x10*channels; /* ignore padding frame [ex. Akane Iro ni Somaru Saka - Parallel] */
|
||||
}
|
||||
else if (read_32bitBE(offset+0x00,sf) == 0x0C020000 && read_32bitBE(offset+0x04,sf) == 0x00000000 &&
|
||||
read_32bitBE(offset+0x08,sf) == 0x00000000 && read_32bitBE(offset+0x0c,sf) == 0x00000000 &&
|
||||
ignore_silent_frame_cavia) {
|
||||
stream_size -= 0x10*channels; /* ignore silent frame [ex. cavia games] */
|
||||
}
|
||||
else if (read_32bitBE(offset+0x00,sf) == 0x0C010000 && read_32bitBE(offset+0x04,sf) == 0x00000000 &&
|
||||
read_32bitBE(offset+0x08,sf) == 0x00000000 && read_32bitBE(offset+0x0c,sf) == 0x00000000 &&
|
||||
ignore_silent_frame_capcom) {
|
||||
stream_size -= 0x10*channels; /* ignore silent frame [ex. Capcom games] */
|
||||
}
|
||||
else {
|
||||
break; /* standard frame */
|
||||
}
|
||||
}
|
||||
while(offset > min_offset);
|
||||
|
||||
/* don't bother fixing loop_end_offset since will be adjusted to num_samples later, if needed */
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->coding_type = coding_type;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->meta_type = meta_ADS;
|
||||
|
||||
switch(coding_type) {
|
||||
case coding_PCM16LE:
|
||||
vgmstream->num_samples = pcm16_bytes_to_samples(stream_size, channels);
|
||||
break;
|
||||
case coding_PSX:
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels);
|
||||
break;
|
||||
case coding_DVI_IMA_int:
|
||||
vgmstream->num_samples = ima_bytes_to_samples(stream_size, channels);
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (vgmstream->loop_flag) {
|
||||
if (is_loop_samples) {
|
||||
vgmstream->loop_start_sample = loop_start_sample;
|
||||
vgmstream->loop_end_sample = loop_end_sample;
|
||||
}
|
||||
else {
|
||||
switch(vgmstream->coding_type) {
|
||||
case coding_PCM16LE:
|
||||
vgmstream->loop_start_sample = pcm16_bytes_to_samples(loop_start_offset, channels);
|
||||
vgmstream->loop_end_sample = pcm16_bytes_to_samples(loop_end_offset, channels);
|
||||
break;
|
||||
case coding_PSX:
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start_offset, channels);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end_offset, channels);
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* when loop_end = 0xFFFFFFFF */
|
||||
if (vgmstream->loop_end_sample == 0)
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
/* happens even when loops are directly samples, loops sound fine (ex. Culdcept) */
|
||||
if (vgmstream->loop_end_sample > vgmstream->num_samples)
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ****************************************************************************** */
|
||||
|
||||
/* ADS in containers */
|
||||
VGMSTREAM* init_vgmstream_ads_container(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t subfile_offset;
|
||||
size_t subfile_size;
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "ads"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,sf) == 0x41445343 && /* "ADSC" */
|
||||
read_32bitBE(0x04,sf) == 0x01000000) {
|
||||
/* Kenka Bancho 2, Kamen Rider Hibiki/Kabuto, Shinjuku no Okami */
|
||||
subfile_offset = 0x08;
|
||||
}
|
||||
else if (read_32bitBE(0x00,sf) == 0x63617669 && /* "cavi" */
|
||||
read_32bitBE(0x04,sf) == 0x61207374 && /* "a st" */
|
||||
read_32bitBE(0x08,sf) == 0x7265616D) { /* "ream" */
|
||||
/* cavia games: Drakengard 1/2, Dragon Quest Yangus, GITS: Stand Alone Complex */
|
||||
subfile_offset = 0x7d8;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
subfile_size = get_streamfile_size(sf) - subfile_offset;
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, NULL);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_ads(temp_sf);
|
||||
close_streamfile(temp_sf);
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -794,6 +794,7 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
|
||||
/* UE4 uses interleaved mono MSADPCM, try to autodetect without breaking normal MSADPCM */
|
||||
if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, sf, &fmt, fact_sample_count, start_offset)) {
|
||||
vgmstream->coding_type = coding_MSADPCM_int;
|
||||
vgmstream->codec_config = 1; /* mark as UE4 MSADPCM */
|
||||
vgmstream->frame_size = fmt.block_size;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = get_ue4_msadpcm_interleave(sf, &fmt, start_offset, data_size);
|
||||
|
@ -3,52 +3,53 @@
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* RXWS - from Sony SCEI PS2 games (Okage: Shadow King, Genji, Bokura no Kazoku) */
|
||||
VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * streamHeader = NULL;
|
||||
VGMSTREAM* init_vgmstream_rxws(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sh = NULL;
|
||||
off_t start_offset, chunk_offset, name_offset = 0;
|
||||
size_t stream_size, chunk_size;
|
||||
int loop_flag = 0, channel_count, is_separate = 0, type, sample_rate;
|
||||
int loop_flag = 0, channels, is_separate = 0, type, sample_rate;
|
||||
int32_t num_samples, loop_start;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
|
||||
/* checks */
|
||||
/* .xws: header and data
|
||||
* .xwh+xwb: header + data (.bin+dat are also found in Wild Arms 4/5) */
|
||||
if (!check_extensions(streamFile,"xws,xwb"))
|
||||
if (!check_extensions(sf,"xws,xwb"))
|
||||
goto fail;
|
||||
is_separate = check_extensions(streamFile,"xwb");
|
||||
is_separate = check_extensions(sf,"xwb");
|
||||
|
||||
/* xwh+xwb: use xwh as header; otherwise use the current file */
|
||||
if (is_separate) {
|
||||
/* extra check to reject Microsoft's XWB faster */
|
||||
if ((read_32bitBE(0x00,streamFile) == 0x57424E44) || /* "WBND" (LE) */
|
||||
(read_32bitBE(0x00,streamFile) == 0x444E4257)) /* "DNBW" (BE) */
|
||||
if (is_id32be(0x00,sf,"WBND") || /* (LE) */
|
||||
is_id32be(0x00,sf,"DNBW")) /* (BE) */
|
||||
goto fail;
|
||||
|
||||
streamHeader = open_streamfile_by_ext(streamFile, "xwh");
|
||||
if (!streamHeader) goto fail;
|
||||
sh = open_streamfile_by_ext(sf, "xwh");
|
||||
if (!sh) goto fail;
|
||||
} else {
|
||||
streamHeader = streamFile;
|
||||
sh = sf;
|
||||
}
|
||||
if (read_32bitBE(0x00,streamHeader) != 0x52585753) /* "RXWS" */
|
||||
if (!is_id32be(0x00,sh,"RXWS"))
|
||||
goto fail;
|
||||
|
||||
/* file size (just the .xwh/xws) */
|
||||
if (read_32bitLE(0x04,streamHeader)+0x10 != get_streamfile_size(streamHeader))
|
||||
if (read_u32le(0x04,sh) + 0x10 != get_streamfile_size(sh))
|
||||
goto fail;
|
||||
/* 0x08(4): version (0x100/0x200), 0x0C: null */
|
||||
/* 0x08: version (0x100/0x200)
|
||||
* 0x0C: null */
|
||||
|
||||
/* typical chunks: FORM, FTXT, MARK, BODY (for .xws) */
|
||||
if (read_32bitBE(0x10,streamHeader) != 0x464F524D) /* "FORM", main header (always first) */
|
||||
if (!is_id32be(0x10,sh,"FORM")) /* main header (always first) */
|
||||
goto fail;
|
||||
chunk_size = read_32bitLE(0x10+0x04,streamHeader); /* size - 0x10 */
|
||||
chunk_size = read_u32le(0x10+0x04,sh); /* size - 0x10 */
|
||||
/* 0x08 version (0x100), 0x0c: null */
|
||||
chunk_offset = 0x20;
|
||||
|
||||
|
||||
/* check multi-streams */
|
||||
total_subsongs = read_32bitLE(chunk_offset+0x00,streamHeader);
|
||||
total_subsongs = read_s32le(chunk_offset+0x00,sh);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
@ -58,15 +59,18 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
off_t header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong-1); /* position in FORM */
|
||||
off_t stream_offset, next_stream_offset, data_offset = 0;
|
||||
|
||||
type = read_8bit(header_offset+0x00, streamHeader);
|
||||
/* 0x01(1): unknown (always 0x1c), 0x02(2): flags? (usually 8002/0002, & 0x01 if looped) */
|
||||
/* 0x04(4): vol/pan stuff? (0x00007F7F), 0x08(1): null?, 0x0c(4): null? */
|
||||
channel_count = read_8bit(header_offset+0x09, streamHeader);
|
||||
sample_rate = (uint16_t)read_16bitLE(header_offset+0x0a,streamHeader);
|
||||
stream_offset = read_32bitLE(header_offset+0x10,streamHeader);
|
||||
num_samples = read_32bitLE(header_offset+0x14,streamHeader);
|
||||
loop_start = read_32bitLE(header_offset+0x18,streamHeader);
|
||||
loop_flag = (loop_start != 0xFFFFFFFF);
|
||||
type = read_u8(header_offset+0x00, sh);
|
||||
/* 0x01: unknown (always 0x1c) */
|
||||
/* 0x02: flags? (usually 8002/0002, & 0x01 if looped) */
|
||||
/* 0x04: vol/pan stuff? (0x00007F7F) */
|
||||
/* 0x08: null? */
|
||||
channels = read_u8(header_offset+0x09, sh);
|
||||
/* 0x0c: null? */
|
||||
sample_rate = read_u16le(header_offset+0x0a,sh);
|
||||
stream_offset = read_u32le(header_offset+0x10,sh);
|
||||
num_samples = read_s32le(header_offset+0x14,sh);
|
||||
loop_start = read_s32le(header_offset+0x18,sh);
|
||||
loop_flag = (loop_start >= 0);
|
||||
|
||||
/* find data start and size */
|
||||
if (is_separate) {
|
||||
@ -75,21 +79,21 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
else {
|
||||
off_t current_chunk = 0x10;
|
||||
/* note the extra 0x10 in chunk_size/offsets */
|
||||
while (current_chunk < get_streamfile_size(streamFile)) {
|
||||
if (read_32bitBE(current_chunk,streamFile) == 0x424F4459) { /* "BODY" chunk_type */
|
||||
while (current_chunk < get_streamfile_size(sf)) {
|
||||
if (is_id32be(current_chunk,sf, "BODY")) {
|
||||
data_offset = 0x10 + current_chunk;
|
||||
break;
|
||||
}
|
||||
current_chunk += 0x10 + read_32bitLE(current_chunk+4,streamFile);
|
||||
current_chunk += 0x10 + read_u32le(current_chunk+4,sf);
|
||||
}
|
||||
if (!data_offset) goto fail;
|
||||
}
|
||||
|
||||
if (target_subsong == total_subsongs) {
|
||||
next_stream_offset = get_streamfile_size(is_separate ? streamFile : streamHeader) - data_offset;
|
||||
next_stream_offset = get_streamfile_size(is_separate ? sf : sh) - data_offset;
|
||||
} else {
|
||||
off_t next_header_offset = chunk_offset + 0x4 + 0x1c * (target_subsong);
|
||||
next_stream_offset = read_32bitLE(next_header_offset+0x10,streamHeader);
|
||||
next_stream_offset = read_u32le(next_header_offset+0x10,sh);
|
||||
}
|
||||
|
||||
stream_size = next_stream_offset - stream_offset;
|
||||
@ -97,24 +101,24 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
/* get stream name (always follows FORM) */
|
||||
if (read_32bitBE(0x10+0x10 + chunk_size,streamHeader) == 0x46545854) { /* "FTXT" */
|
||||
if (is_id32be(0x10+0x10 + chunk_size,sh, "FTXT")) {
|
||||
chunk_offset = 0x10+0x10 + chunk_size + 0x10;
|
||||
if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_subsongs) {
|
||||
name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_subsong-1)*0x04,streamHeader);
|
||||
if (read_s32le(chunk_offset+0x00,sh) == total_subsongs) {
|
||||
name_offset = chunk_offset + read_u32le(chunk_offset+0x04 + (target_subsong-1)*0x04,sh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_RXWS;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_PS2_RXWS;
|
||||
if (name_offset)
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sh);
|
||||
|
||||
switch (type) {
|
||||
case 0x00: /* PS-ADPCM */
|
||||
@ -122,8 +126,8 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(num_samples, channel_count);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(num_samples, channels);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
break;
|
||||
|
||||
@ -132,8 +136,8 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x2;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(num_samples, channel_count, 16);
|
||||
vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channel_count, 16);
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(num_samples, channels, 16);
|
||||
vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channels, 16);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
break;
|
||||
|
||||
@ -141,11 +145,11 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
case 0x02: { /* ATRAC3 */
|
||||
int block_align, encoder_delay;
|
||||
|
||||
block_align = 0xc0 * channel_count;
|
||||
block_align = 0xc0 * channels;
|
||||
encoder_delay = 1024 + 69*2; /* observed default */
|
||||
vgmstream->num_samples = num_samples - encoder_delay;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamFile, start_offset,stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay);
|
||||
vgmstream->codec_data = init_ffmpeg_atrac3_raw(sf, start_offset,stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
@ -160,57 +164,58 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
/* open the file for reading */
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
|
||||
if (is_separate && streamHeader) close_streamfile(streamHeader);
|
||||
if (is_separate && sh) close_streamfile(sh);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (is_separate && streamHeader) close_streamfile(streamHeader);
|
||||
if (is_separate && sh) close_streamfile(sh);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* .RXW - legacy fake ext/header for poorly split XWH+XWB files generated by old tools (incorrect header/chunk sizes) */
|
||||
VGMSTREAM * init_vgmstream_ps2_rxw(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
int loop_flag=0, channel_count;
|
||||
VGMSTREAM* init_vgmstream_rxws_badrip(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
int loop_flag=0, channels;
|
||||
off_t start_offset;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"rxw")) goto fail;
|
||||
|
||||
/* check RXWS/FORM Header */
|
||||
if (!((read_32bitBE(0x00,streamFile) == 0x52585753) &&
|
||||
(read_32bitBE(0x10,streamFile) == 0x464F524D)))
|
||||
if (!check_extensions(sf,"rxw"))
|
||||
goto fail;
|
||||
|
||||
loop_flag = (read_32bitLE(0x3C,streamFile)!=0xFFFFFFFF);
|
||||
channel_count=2; /* Always stereo files */
|
||||
/* check RXWS/FORM Header */
|
||||
if (!((read_32bitBE(0x00,sf) == 0x52585753) &&
|
||||
(read_32bitBE(0x10,sf) == 0x464F524D)))
|
||||
goto fail;
|
||||
|
||||
loop_flag = (read_32bitLE(0x3C,sf)!=0xFFFFFFFF);
|
||||
channels=2; /* Always stereo files */
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitLE(0x2E,streamFile);
|
||||
vgmstream->num_samples = (read_32bitLE(0x38,streamFile)*28/16)/2;
|
||||
vgmstream->sample_rate = read_32bitLE(0x2E,sf);
|
||||
vgmstream->num_samples = (read_32bitLE(0x38,sf)*28/16)/2;
|
||||
|
||||
/* Get loop point values */
|
||||
if(vgmstream->loop_flag) {
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x3C,streamFile)/16*14;
|
||||
vgmstream->loop_end_sample = read_32bitLE(0x38,streamFile)/16*14;
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x3C,sf)/16*14;
|
||||
vgmstream->loop_end_sample = read_32bitLE(0x38,sf)/16*14;
|
||||
}
|
||||
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x1c,streamFile)+0x10;
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x1c,sf)+0x10;
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->meta_type = meta_PS2_RXWS;
|
||||
vgmstream->meta_type = meta_RXWS;
|
||||
start_offset = 0x40;
|
||||
|
||||
/* open the file for reading */
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
@ -1,38 +1,39 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "sqex_scd_streamfile.h"
|
||||
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
static void scd_ogg_v2_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource);
|
||||
static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource);
|
||||
static void scd_ogg_v2_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource);
|
||||
static void scd_ogg_v3_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource);
|
||||
#endif
|
||||
|
||||
/* SCD - Square-Enix games (FF XIII, XIV) */
|
||||
VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_sqex_scd(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, tables_offset, meta_offset, extradata_offset, name_offset = 0;
|
||||
int32_t stream_size, extradata_size, loop_start, loop_end;
|
||||
|
||||
int loop_flag = 0, channel_count, codec, sample_rate;
|
||||
int loop_flag = 0, channels, codec, sample_rate;
|
||||
int version, target_entry, aux_chunk_count;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
int big_endian;
|
||||
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if ( !check_extensions(streamFile, "scd") )
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "scd"))
|
||||
goto fail;
|
||||
|
||||
/** main header **/
|
||||
if (read_32bitBE(0x00,streamFile) != 0x53454442 && /* "SEDB" */
|
||||
read_32bitBE(0x04,streamFile) != 0x53534346) /* "SSCF" */
|
||||
if (!is_id32be(0x00,sf, "SEDB") &&
|
||||
!is_id32be(0x04,sf, "SSCF"))
|
||||
goto fail;
|
||||
|
||||
if (read_8bit(0x0c,streamFile) == 0x01) { /* big endian flag */
|
||||
big_endian = read_u8(0x0c,sf) == 0x01;
|
||||
if (big_endian) { /* big endian flag */
|
||||
//size_offset = 0x14;
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
@ -42,21 +43,20 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
read_16bit = read_16bitLE;
|
||||
}
|
||||
|
||||
/* SSCF version? (older SSCFs from Crisis Core/FFXI X360 seem to be V3/2) */
|
||||
if (read_8bit(0x0d,streamFile) != 0x04)
|
||||
goto fail;
|
||||
|
||||
/* v2: FFXIII demo (PS3), FFT0 test files (PC); v3: common; v4: Kingdom Hearts 2.8 (PS4) */
|
||||
version = read_32bit(0x08,streamFile);
|
||||
version = read_32bit(0x08,sf);
|
||||
if (version != 2 && version != 3 && version != 4)
|
||||
goto fail;
|
||||
|
||||
tables_offset = read_16bit(0x0e,streamFile); /* usually 0x30 or 0x20 */
|
||||
/* SSCF version? (older SSCFs from Crisis Core/FFXI X360 seem to be V3/2) */
|
||||
if (read_u8(0x0d,sf) != 0x04)
|
||||
goto fail;
|
||||
|
||||
tables_offset = read_16bit(0x0e,sf); /* usually 0x30 or 0x20 */
|
||||
|
||||
#if 0
|
||||
/* never mind, FFXIII music_68tak.ps3.scd is 0x80 shorter */
|
||||
/* check file size with header value */
|
||||
if (read_32bit(size_offset,streamFile) != get_streamfile_size(streamFile))
|
||||
/* FFXIII music_68tak.ps3.scd is 0x80 shorter? */
|
||||
if (read_32bit(size_offset,sf) != get_streamfile_size(sf))
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
@ -80,8 +80,8 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
/* find meta_offset in table3 (headers) and total subsongs */
|
||||
{
|
||||
int i;
|
||||
int headers_entries = read_16bit(tables_offset+0x04,streamFile);
|
||||
off_t headers_offset = read_32bit(tables_offset+0x0c,streamFile);
|
||||
int headers_entries = read_16bit(tables_offset+0x04,sf);
|
||||
off_t headers_offset = read_32bit(tables_offset+0x0c,sf);
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
total_subsongs = 0;
|
||||
@ -89,9 +89,9 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
|
||||
/* manually find subsongs as entries can be dummy (ex. sfx banks in FF XIV or FF Type-0) */
|
||||
for (i = 0; i < headers_entries; i++) {
|
||||
off_t entry_offset = read_32bit(headers_offset + i*0x04,streamFile);
|
||||
off_t entry_offset = read_32bit(headers_offset + i*0x04,sf);
|
||||
|
||||
if (read_32bit(entry_offset+0x0c,streamFile) == -1)
|
||||
if (read_32bit(entry_offset+0x0c,sf) == -1)
|
||||
continue; /* codec -1 when dummy */
|
||||
|
||||
total_subsongs++;
|
||||
@ -105,15 +105,15 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
/** stream header **/
|
||||
stream_size = read_32bit(meta_offset+0x00,streamFile);
|
||||
channel_count = read_32bit(meta_offset+0x04,streamFile);
|
||||
sample_rate = read_32bit(meta_offset+0x08,streamFile);
|
||||
codec = read_32bit(meta_offset+0x0c,streamFile);
|
||||
stream_size = read_32bit(meta_offset+0x00,sf);
|
||||
channels = read_32bit(meta_offset+0x04,sf);
|
||||
sample_rate = read_32bit(meta_offset+0x08,sf);
|
||||
codec = read_32bit(meta_offset+0x0c,sf);
|
||||
|
||||
loop_start = read_32bit(meta_offset+0x10,streamFile);
|
||||
loop_end = read_32bit(meta_offset+0x14,streamFile);
|
||||
extradata_size = read_32bit(meta_offset+0x18,streamFile);
|
||||
aux_chunk_count = read_32bit(meta_offset+0x1c,streamFile);
|
||||
loop_start = read_32bit(meta_offset+0x10,sf);
|
||||
loop_end = read_32bit(meta_offset+0x14,sf);
|
||||
extradata_size = read_32bit(meta_offset+0x18,sf);
|
||||
aux_chunk_count = read_32bit(meta_offset+0x1c,sf);
|
||||
/* 0x01e(2): unknown, seen in some FF XIV sfx (MSADPCM) */
|
||||
|
||||
loop_flag = (loop_end > 0);
|
||||
@ -127,19 +127,19 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
/* skips aux chunks, sometimes needed (Lightning Returns X360, FF XIV PC) */
|
||||
if (aux_chunk_count && read_32bitBE(extradata_offset, streamFile) == 0x4D41524B) { /* "MARK" */
|
||||
extradata_offset += read_32bit(extradata_offset+0x04, streamFile);
|
||||
if (aux_chunk_count && is_id32be(extradata_offset, sf, "MARK")) {
|
||||
extradata_offset += read_32bit(extradata_offset+0x04, sf);
|
||||
}
|
||||
|
||||
/* find name if possible */
|
||||
if (version == 4) {
|
||||
int info_entries = read_16bit(tables_offset+0x00,streamFile);
|
||||
int headers_entries = read_16bit(tables_offset+0x04,streamFile);
|
||||
int info_entries = read_16bit(tables_offset+0x00,sf);
|
||||
int headers_entries = read_16bit(tables_offset+0x04,sf);
|
||||
off_t info_offset = tables_offset+0x20;
|
||||
|
||||
/* not very exact as table1 and table3 entries may differ in V3, not sure about V4 */
|
||||
if (info_entries == headers_entries) {
|
||||
off_t entry_offset = read_16bit(info_offset + 0x04*target_entry,streamFile);
|
||||
off_t entry_offset = read_16bit(info_offset + 0x04*target_entry,sf);
|
||||
name_offset = entry_offset+0x30;
|
||||
}
|
||||
}
|
||||
@ -157,17 +157,17 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
ovmi.disable_reordering = 1; /* already ordered */
|
||||
/* loop values are in bytes, let init_vgmstream_ogg_vorbis find loop comments instead */
|
||||
|
||||
ogg_version = read_8bit(extradata_offset + 0x00, streamFile);
|
||||
ogg_version = read_u8(extradata_offset + 0x00, sf);
|
||||
/* 0x01(1): 0x20 in v2/3, this ogg miniheader size? */
|
||||
ogg_byte = read_8bit(extradata_offset + 0x02, streamFile);
|
||||
ogg_byte = read_u8(extradata_offset + 0x02, sf);
|
||||
/* 0x03(1): ? in v3 */
|
||||
|
||||
if (ogg_version == 0) { /* 0x10? header, then custom Vorbis header before regular Ogg (FF XIV PC v1) */
|
||||
ovmi.stream_size = stream_size;
|
||||
}
|
||||
else { /* 0x20 header, then seek table */
|
||||
size_t seek_table_size = read_32bit(extradata_offset+0x10, streamFile);
|
||||
size_t vorb_header_size = read_32bit(extradata_offset+0x14, streamFile);
|
||||
size_t seek_table_size = read_32bit(extradata_offset+0x10, sf);
|
||||
size_t vorb_header_size = read_32bit(extradata_offset+0x14, sf);
|
||||
/* 0x18(4): ? (can be 0) */
|
||||
|
||||
if ((extradata_offset-meta_offset) + seek_table_size + vorb_header_size != extradata_size)
|
||||
@ -192,16 +192,16 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
/* actual Ogg init */
|
||||
ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi);
|
||||
ogg_vgmstream = init_vgmstream_ogg_vorbis_callbacks(sf, NULL, start_offset, &ovmi);
|
||||
if (ogg_vgmstream && name_offset)
|
||||
read_string(ogg_vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile);
|
||||
read_string(ogg_vgmstream->stream_name, PATH_LIMIT, name_offset, sf);
|
||||
return ogg_vgmstream;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
@ -209,7 +209,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_SQEX_SCD;
|
||||
if (name_offset)
|
||||
read_string(vgmstream->stream_name, PATH_LIMIT, name_offset, streamFile);
|
||||
read_string(vgmstream->stream_name, STREAM_NAME_SIZE, name_offset, sf);
|
||||
|
||||
switch (codec) {
|
||||
case 0x01: /* PCM */
|
||||
@ -217,10 +217,10 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channels, 16);
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channel_count, 16);
|
||||
vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, channel_count, 16);
|
||||
vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start, channels, 16);
|
||||
vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end, channels, 16);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -229,10 +229,10 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels);
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channel_count);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channels);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -246,7 +246,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
cfg.interleave = 0x800; /* for multistream [Final Fantasy XIII-2 (PS3)], otherwise ignored */
|
||||
cfg.data_size = stream_size;
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_SCD, &cfg);
|
||||
vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_SCD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
@ -268,9 +268,9 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
case 0x0C: /* MS ADPCM [Final Fantasy XIV (PC) sfx] */
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = read_16bit(extradata_offset + 0x0c, streamFile);
|
||||
vgmstream->frame_size = read_16bit(extradata_offset + 0x0c, sf);
|
||||
/* WAVEFORMATEX in extradata_offset */
|
||||
if (!msadpcm_check_coefs(streamFile, extradata_offset + 0x14))
|
||||
if (!msadpcm_check_coefs(sf, extradata_offset + 0x14))
|
||||
goto fail;
|
||||
|
||||
vgmstream->num_samples = msadpcm_bytes_to_samples(stream_size, vgmstream->frame_size, vgmstream->channels);
|
||||
@ -282,83 +282,53 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
|
||||
case 0x0A: /* DSP ADPCM [Dragon Quest X (Wii)] */
|
||||
case 0x15: { /* DSP ADPCM [Dragon Quest X (Wii U)] (no apparent differences except higher sample rate) */
|
||||
const off_t interleave_size = 0x800;
|
||||
const off_t stride_size = interleave_size * channel_count;
|
||||
int i;
|
||||
size_t total_size;
|
||||
layered_layout_data * data = NULL;
|
||||
|
||||
/* interleaved DSPs including the header (so the first 0x800 is 0x60 header + 0x740 data)
|
||||
* so interleave layout can't used; we'll setup de-interleaving streamfiles as layers/channels instead */
|
||||
//todo this could be simplified using a block layout or adding interleave_first_block
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_layered;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x800;
|
||||
vgmstream->interleave_first_skip = 0x60;
|
||||
vgmstream->interleave_first_block_size = vgmstream->interleave_block_size - vgmstream->interleave_first_skip;
|
||||
|
||||
/* read from the first DSP header and verify other channel headers */
|
||||
{
|
||||
total_size = (read_32bitBE(start_offset+0x04,streamFile)+1)/2; /* rounded nibbles / 2 */
|
||||
vgmstream->num_samples = read_32bitBE(start_offset+0x00,streamFile);
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end + 1;
|
||||
}
|
||||
/* standard dsp header at start_offset */
|
||||
dsp_read_coefs_be(vgmstream, sf, start_offset+0x1c, vgmstream->interleave_block_size);
|
||||
dsp_read_hist_be(vgmstream, sf, start_offset+0x40, vgmstream->interleave_block_size);
|
||||
|
||||
for (i = 1; i < channel_count; i++) {
|
||||
if ((read_32bitBE(start_offset+4,streamFile)+1)/2 != total_size ||
|
||||
read_32bitBE(start_offset+interleave_size*i+0x00,streamFile) != vgmstream->num_samples) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
vgmstream->num_samples = read_32bit(start_offset+0x00,sf);
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end + 1;
|
||||
}
|
||||
|
||||
/* init layout */
|
||||
data = init_layout_layered(channel_count);
|
||||
if (!data) goto fail;
|
||||
vgmstream->layout_data = data;
|
||||
|
||||
/* open each layer subfile */
|
||||
for (i = 0; i < channel_count; i++) {
|
||||
STREAMFILE* temp_streamFile = setup_scd_dsp_streamfile(streamFile, start_offset+interleave_size*i, interleave_size, stride_size, total_size);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
data->layers[i] = init_vgmstream_ngc_dsp_std(temp_streamFile);
|
||||
close_streamfile(temp_streamFile);
|
||||
if (!data->layers[i]) goto fail;
|
||||
}
|
||||
|
||||
/* setup layered VGMSTREAMs */
|
||||
if (!setup_layout_layered(data))
|
||||
goto fail;
|
||||
|
||||
|
||||
start_offset += vgmstream->interleave_first_skip;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x0B: { /* XMA2 [Final Fantasy (X360), Lightning Returns (X360) sfx, Kingdom Hearts 2.8 (X1)] */
|
||||
ffmpeg_codec_data *ffmpeg_data = NULL;
|
||||
uint8_t buf[200];
|
||||
uint8_t buf[0x100];
|
||||
int32_t bytes;
|
||||
|
||||
/* extradata_offset+0x00: fmt0x166 header (BE), extradata_offset+0x34: seek table */
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, extradata_offset,0x34, stream_size, streamFile, 1);
|
||||
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,stream_size);
|
||||
if (!ffmpeg_data) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
/* extradata:
|
||||
* 0x00: fmt0x166 header (BE X360, LE XBone)
|
||||
* 0x34: seek table */
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, extradata_offset,0x34, stream_size, sf, big_endian);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf, bytes, start_offset, stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = ffmpeg_data->totalSamples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end; //todo +1?
|
||||
xma2_parse_fmt_chunk_extra(sf, extradata_offset, NULL, &vgmstream->num_samples, NULL, NULL, big_endian);
|
||||
vgmstream->loop_start_sample = loop_start; /* same loops in chunk */
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, streamFile, start_offset,stream_size, 0, 0,0); /* samples are ok, loops? */
|
||||
xma_fix_raw_samples(vgmstream, sf, start_offset, stream_size, extradata_offset, 1,1);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0E: { /* ATRAC3/ATRAC3plus [Lord of Arcana (PSP), Final Fantasy Type-0] */
|
||||
int fact_samples = 0;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_atrac3_riff(streamFile, start_offset, &fact_samples);
|
||||
vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, start_offset, &fact_samples);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
@ -377,17 +347,17 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
|
||||
/* post header has various typical ATRAC9 values */
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.config_data = read_32bit(extradata_offset+0x0c,streamFile);
|
||||
cfg.encoder_delay = read_32bit(extradata_offset+0x18,streamFile);
|
||||
cfg.config_data = read_32bit(extradata_offset+0x0c,sf);
|
||||
cfg.encoder_delay = read_32bit(extradata_offset+0x18,sf);
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = read_32bit(extradata_offset+0x10,streamFile); /* loop values above are also weird and ignored */
|
||||
vgmstream->loop_start_sample = read_32bit(extradata_offset+0x20, streamFile);
|
||||
vgmstream->loop_end_sample = read_32bit(extradata_offset+0x24, streamFile) + 1;
|
||||
vgmstream->num_samples = read_32bit(extradata_offset+0x10,sf); /* loop values above are also weird and ignored */
|
||||
vgmstream->loop_start_sample = read_32bit(extradata_offset+0x20, sf);
|
||||
vgmstream->loop_end_sample = read_32bit(extradata_offset+0x24, sf) + 1;
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample -= cfg.encoder_delay;
|
||||
vgmstream->loop_end_sample -= cfg.encoder_delay;
|
||||
@ -403,7 +373,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
||||
@ -415,7 +385,7 @@ fail:
|
||||
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
static void scd_ogg_v2_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
|
||||
static void scd_ogg_v2_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) {
|
||||
uint8_t *ptr8 = ptr;
|
||||
size_t bytes_read = size * nmemb;
|
||||
ogg_vorbis_io *io = datasource;
|
||||
@ -438,7 +408,7 @@ static void scd_ogg_v2_decryption_callback(void *ptr, size_t size, size_t nmemb,
|
||||
}
|
||||
}
|
||||
|
||||
static void scd_ogg_v3_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
|
||||
static void scd_ogg_v3_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) {
|
||||
/* V3 decryption table found in the .exe of FF XIV Heavensward */
|
||||
static const uint8_t scd_ogg_v3_lookuptable[256] = {
|
||||
0x3A, 0x32, 0x32, 0x32, 0x03, 0x7E, 0x12, 0xF7, 0xB2, 0xE2, 0xA2, 0x67, 0x32, 0x32, 0x22, 0x32, // 00-0F
|
||||
|
@ -1,92 +0,0 @@
|
||||
#ifndef _SQEX_SCD_STREAMFILE_H_
|
||||
#define _SQEX_SCD_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
off_t start_physical_offset; /* interleaved data start, for this substream */
|
||||
size_t interleave_block_size; /* max size that can be read before encountering other substreams */
|
||||
size_t stride_size; /* step size between interleave blocks (interleave*channels) */
|
||||
size_t total_size; /* final size of the deinterleaved substream */
|
||||
} scd_dsp_io_data;
|
||||
|
||||
|
||||
/* Handles deinterleaving of complete files, skipping portions or other substreams. */
|
||||
static size_t scd_dsp_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, scd_dsp_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
|
||||
while (length > 0) {
|
||||
size_t to_read;
|
||||
size_t length_available;
|
||||
off_t block_num;
|
||||
off_t intrablock_offset;
|
||||
off_t physical_offset;
|
||||
|
||||
block_num = offset / data->interleave_block_size;
|
||||
intrablock_offset = offset % data->interleave_block_size;
|
||||
physical_offset = data->start_physical_offset + block_num*data->stride_size + intrablock_offset;
|
||||
length_available = data->interleave_block_size - intrablock_offset;
|
||||
|
||||
if (length < length_available) {
|
||||
to_read = length;
|
||||
}
|
||||
else {
|
||||
to_read = length_available;
|
||||
}
|
||||
|
||||
if (to_read > 0) {
|
||||
size_t bytes_read;
|
||||
|
||||
bytes_read = read_streamfile(dest, physical_offset, to_read, streamfile);
|
||||
total_read += bytes_read;
|
||||
|
||||
if (bytes_read != to_read) {
|
||||
return total_read;
|
||||
}
|
||||
|
||||
dest += bytes_read;
|
||||
offset += bytes_read;
|
||||
length -= bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t scd_dsp_io_size(STREAMFILE *streamfile, scd_dsp_io_data* data) {
|
||||
return data->total_size;
|
||||
}
|
||||
|
||||
|
||||
static STREAMFILE* setup_scd_dsp_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t interleave_block_size, size_t stride_size, size_t total_size) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
scd_dsp_io_data io_data = {0};
|
||||
size_t io_data_size = sizeof(scd_dsp_io_data);
|
||||
|
||||
io_data.start_physical_offset = start_offset;
|
||||
io_data.interleave_block_size = interleave_block_size;
|
||||
io_data.stride_size = stride_size;
|
||||
io_data.total_size = total_size;
|
||||
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, scd_dsp_io_read,scd_dsp_io_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"dsp");
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _SCD_STREAMFILE_H_ */
|
@ -2,12 +2,12 @@
|
||||
#include "../layout/layout.h"
|
||||
|
||||
/* THP - Nintendo movie format found in GC/Wii games */
|
||||
VGMSTREAM* init_vgmstream_thp(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_thp(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, component_type_offset, component_data_offset;
|
||||
uint32_t version, max_audio_size;
|
||||
int num_components;
|
||||
int loop_flag, channel_count;
|
||||
int loop_flag, channels;
|
||||
int i;
|
||||
|
||||
|
||||
@ -15,14 +15,14 @@ VGMSTREAM* init_vgmstream_thp(STREAMFILE *streamFile) {
|
||||
/* .thp: actual extension
|
||||
* .dsp: fake extension?
|
||||
* (extensionless): Fragile (Wii) */
|
||||
if (!check_extensions(streamFile, "thp,dsp,"))
|
||||
if (!check_extensions(sf, "thp,dsp,"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x54485000) /* "THP\0" */
|
||||
if (!is_id32be(0x00,sf, "THP\0"))
|
||||
goto fail;
|
||||
|
||||
version = read_32bitBE(0x04,streamFile); /* 16b+16b major/minor */
|
||||
version = read_u32be(0x04,sf); /* 16b+16b major/minor */
|
||||
/* 0x08: max buffer size */
|
||||
max_audio_size = read_32bitBE(0x0C,streamFile);
|
||||
max_audio_size = read_u32be(0x0C,sf);
|
||||
/* 0x10: fps in float */
|
||||
/* 0x14: block count */
|
||||
/* 0x18: first block size */
|
||||
@ -33,19 +33,19 @@ VGMSTREAM* init_vgmstream_thp(STREAMFILE *streamFile) {
|
||||
if (max_audio_size == 0) /* no sound */
|
||||
goto fail;
|
||||
|
||||
component_type_offset = read_32bitBE(0x20,streamFile);
|
||||
component_type_offset = read_u32be(0x20,sf);
|
||||
/* 0x24: block offsets table offset (optional, for seeking) */
|
||||
start_offset = read_32bitBE(0x28,streamFile);
|
||||
start_offset = read_u32be(0x28,sf);
|
||||
/* 0x2c: last block offset */
|
||||
|
||||
/* first component "type" x16 then component headers */
|
||||
num_components = read_32bitBE(component_type_offset,streamFile);
|
||||
num_components = read_u32be(component_type_offset,sf);
|
||||
component_type_offset += 0x04;
|
||||
component_data_offset = component_type_offset + 0x10;
|
||||
|
||||
/* parse "component" (data that goes into blocks) */
|
||||
for (i = 0; i < num_components; i++) {
|
||||
int type = read_8bit(component_type_offset + i,streamFile);
|
||||
int type = read_u8(component_type_offset + i,sf);
|
||||
|
||||
if (type == 0x00) { /* video */
|
||||
if (version == 0x00010000)
|
||||
@ -73,24 +73,24 @@ VGMSTREAM* init_vgmstream_thp(STREAMFILE *streamFile) {
|
||||
* adjusted, but we can't detect Wii (non adjusted) .thp tho */
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = read_32bitBE(component_data_offset + 0x00,streamFile);
|
||||
channels = read_u32be(component_data_offset + 0x00,sf);
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitBE(component_data_offset + 0x04,streamFile);
|
||||
vgmstream->num_samples = read_32bitBE(component_data_offset + 0x08,streamFile);
|
||||
vgmstream->sample_rate = read_u32be(component_data_offset + 0x04,sf);
|
||||
vgmstream->num_samples = read_u32be(component_data_offset + 0x08,sf);
|
||||
|
||||
vgmstream->meta_type = meta_THP;
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_blocked_thp;
|
||||
/* coefs are in every block */
|
||||
|
||||
vgmstream->full_block_size = read_32bitBE(0x18,streamFile); /* next block size */
|
||||
vgmstream->full_block_size = read_u32be(0x18,sf); /* next block size */
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -599,75 +599,32 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
static size_t silence_io_read(STREAMFILE* streamfile, uint8_t *dest, off_t offset, size_t length, void* data) {
|
||||
int i;
|
||||
for (i = 0; i < length; i++) {
|
||||
dest[i] = 0;
|
||||
}
|
||||
return length; /* pretend we read zeroes */
|
||||
}
|
||||
static size_t silence_io_size(STREAMFILE* streamfile, void* data) {
|
||||
return 0x7FFFFFF; /* whatevs */
|
||||
}
|
||||
static STREAMFILE* setup_silence_streamfile(STREAMFILE* sf) {
|
||||
STREAMFILE* temp_sf = NULL, *new_sf = NULL;
|
||||
|
||||
/* setup custom streamfile */
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
if (!new_sf) goto fail;
|
||||
temp_sf = new_sf;
|
||||
|
||||
new_sf = open_io_streamfile(temp_sf, NULL,0, silence_io_read,silence_io_size);
|
||||
if (!new_sf) goto fail;
|
||||
temp_sf = new_sf;
|
||||
|
||||
return temp_sf;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static VGMSTREAM* init_vgmstream_ubi_bao_silence(ubi_bao_header* bao, STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
int channel_count, sample_rate;
|
||||
int channels, sample_rate;
|
||||
int32_t num_samples;
|
||||
|
||||
channel_count = bao->channels;
|
||||
/* by default silences don't have settings */
|
||||
channels = bao->channels;
|
||||
if (channels == 0)
|
||||
channels = 2;
|
||||
sample_rate = bao->sample_rate;
|
||||
|
||||
/* by default silences don't have settings so let's pretend */
|
||||
if (channel_count == 0)
|
||||
channel_count = 2;
|
||||
if (sample_rate == 0)
|
||||
sample_rate = 48000;
|
||||
num_samples = bao->duration * sample_rate;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, 0);
|
||||
/* init the VGMSTREAM */
|
||||
vgmstream = init_vgmstream_silence(channels, sample_rate, num_samples);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_UBI_BAO;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->num_samples = bao->duration * sample_rate;
|
||||
vgmstream->num_streams = bao->total_subsongs;
|
||||
vgmstream->stream_size = vgmstream->num_samples * channel_count * 0x02; /* PCM size */
|
||||
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
|
||||
temp_sf = setup_silence_streamfile(sf);
|
||||
if ( !vgmstream_open_stream(vgmstream, temp_sf, 0x00) )
|
||||
goto fail;
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
}
|
||||
|
||||
|
@ -1526,74 +1526,33 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static size_t silence_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t length, void* data) {
|
||||
int i;
|
||||
for (i = 0; i < length; i++) {
|
||||
dest[i] = 0;
|
||||
}
|
||||
return length; /* pretend we read zeroes */
|
||||
}
|
||||
static size_t silence_io_size(STREAMFILE* sf, void* data) {
|
||||
return 0x7FFFFFF; /* whatevs */
|
||||
}
|
||||
static STREAMFILE* setup_silence_streamfile(STREAMFILE* sf) {
|
||||
STREAMFILE *temp_sf = NULL, *new_sf = NULL;
|
||||
|
||||
/* setup custom streamfile */
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
if (!new_sf) goto fail;
|
||||
temp_sf = new_sf;
|
||||
|
||||
new_sf = open_io_streamfile(temp_sf, NULL,0, silence_io_read,silence_io_size);
|
||||
if (!new_sf) goto fail;
|
||||
temp_sf = new_sf;
|
||||
|
||||
return temp_sf;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static VGMSTREAM* init_vgmstream_ubi_sb_silence(ubi_sb_header* sb, STREAMFILE* sf_index, STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
int channel_count, sample_rate;
|
||||
int channels, sample_rate;
|
||||
int32_t num_samples;
|
||||
|
||||
channel_count = sb->channels;
|
||||
/* by default silences don't have settings */
|
||||
channels = sb->channels;
|
||||
if (channels == 0)
|
||||
channels = 2;
|
||||
sample_rate = sb->sample_rate;
|
||||
|
||||
/* by default silences don't have settings so let's pretend */
|
||||
if (channel_count == 0)
|
||||
channel_count = 2;
|
||||
if (sample_rate == 0)
|
||||
sample_rate = 48000;
|
||||
num_samples = sb->duration * sample_rate;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, 0);
|
||||
/* init the VGMSTREAM */
|
||||
vgmstream = init_vgmstream_silence(channels, sample_rate, num_samples);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_UBI_SB;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->num_samples = (int32_t)(sb->duration * (float)sample_rate);
|
||||
vgmstream->num_streams = sb->total_subsongs;
|
||||
vgmstream->stream_size = vgmstream->num_samples * channel_count * 0x02; /* PCM size */
|
||||
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
|
||||
temp_sf = setup_silence_streamfile(sf);
|
||||
if ( !vgmstream_open_stream(vgmstream, temp_sf, 0x00) )
|
||||
goto fail;
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
}
|
||||
|
||||
|
@ -1,62 +1,61 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* UE4OPUS - from Unreal Engine 4 games [ARK: Survival Evolved (PC), Fortnite (PC)] */
|
||||
VGMSTREAM * init_vgmstream_ue4opus(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0, channel_count, sample_rate, num_samples, skip;
|
||||
size_t data_size;
|
||||
|
||||
|
||||
/* checks*/
|
||||
/* .opus/lopus: possible real extension
|
||||
* .ue4opus: header id */
|
||||
if (!check_extensions(streamFile, "opus,lopus,ue4opus"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00, streamFile) != 0x5545344F && /* "UE4O" */
|
||||
read_32bitBE(0x00, streamFile) != 0x50555300) /* "PUS\0" */
|
||||
goto fail;
|
||||
|
||||
|
||||
sample_rate = (uint16_t)read_16bitLE(0x08, streamFile);
|
||||
num_samples = read_32bitLE(0x0a, streamFile); /* may be less or equal to file num_samples */
|
||||
channel_count = read_8bit(0x0e, streamFile);
|
||||
/* 0x0f(2): frame count */
|
||||
loop_flag = 0;
|
||||
|
||||
start_offset = 0x11;
|
||||
data_size = get_streamfile_size(streamFile) - start_offset;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_UE4OPUS;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
/* usually uses 60ms for music (delay of 360 samples) */
|
||||
skip = ue4_opus_get_encoder_delay(start_offset, streamFile);
|
||||
vgmstream->num_samples = num_samples - skip;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_ue4_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
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"
|
||||
|
||||
|
||||
/* UE4OPUS - from Unreal Engine 4 games [ARK: Survival Evolved (PC), Fortnite (PC)] */
|
||||
VGMSTREAM * init_vgmstream_ue4opus(STREAMFILE *sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0, channels, sample_rate, num_samples, skip;
|
||||
size_t data_size;
|
||||
|
||||
|
||||
/* checks*/
|
||||
/* .opus/lopus: possible real extension
|
||||
* .ue4opus: header id */
|
||||
if (!check_extensions(sf, "opus,lopus,ue4opus"))
|
||||
goto fail;
|
||||
if (!is_id64be(0x00, sf, "UE4OPUS\0"))
|
||||
goto fail;
|
||||
|
||||
|
||||
sample_rate = read_u16le(0x08, sf);
|
||||
num_samples = read_s32le(0x0a, sf); /* may be less or equal to file num_samples */
|
||||
channels = read_u8(0x0e, sf);
|
||||
/* 0x0f(2): frame count */
|
||||
loop_flag = 0;
|
||||
|
||||
start_offset = 0x11;
|
||||
data_size = get_streamfile_size(sf) - start_offset;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_UE4OPUS;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
/* usually uses 60ms for music (delay of 360 samples) */
|
||||
skip = ue4_opus_get_encoder_delay(start_offset, sf);
|
||||
vgmstream->num_samples = num_samples - skip;
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_ue4_opus(sf, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1123,9 +1123,14 @@ fail:
|
||||
}
|
||||
|
||||
STREAMFILE* read_filemap_file(STREAMFILE* sf, int file_num) {
|
||||
return read_filemap_file_pos(sf, file_num, NULL);
|
||||
}
|
||||
|
||||
STREAMFILE* read_filemap_file_pos(STREAMFILE* sf, int file_num, int* p_pos) {
|
||||
char filename[PATH_LIMIT];
|
||||
off_t txt_offset, file_size;
|
||||
STREAMFILE* sf_map = NULL;
|
||||
int file_pos = 0;
|
||||
|
||||
sf_map = open_streamfile_by_filename(sf, ".txtm");
|
||||
if (!sf_map) goto fail;
|
||||
@ -1136,10 +1141,10 @@ STREAMFILE* read_filemap_file(STREAMFILE* sf, int file_num) {
|
||||
file_size = get_streamfile_size(sf_map);
|
||||
|
||||
/* skip BOM if needed */
|
||||
if ((uint16_t)read_16bitLE(0x00, sf_map) == 0xFFFE ||
|
||||
(uint16_t)read_16bitLE(0x00, sf_map) == 0xFEFF) {
|
||||
if (read_u16le(0x00, sf_map) == 0xFFFE ||
|
||||
read_u16le(0x00, sf_map) == 0xFEFF) {
|
||||
txt_offset = 0x02;
|
||||
} else if (((uint32_t)read_32bitBE(0x00, sf_map) & 0xFFFFFF00) == 0xEFBBBF00) {
|
||||
} else if ((read_u32be(0x00, sf_map) & 0xFFFFFF00) == 0xEFBBBF00) {
|
||||
txt_offset = 0x03;
|
||||
}
|
||||
|
||||
@ -1174,8 +1179,9 @@ STREAMFILE* read_filemap_file(STREAMFILE* sf, int file_num) {
|
||||
if (ok != 1)
|
||||
goto fail;
|
||||
|
||||
if (i == file_num)
|
||||
{
|
||||
if (i == file_num) {
|
||||
if (p_pos) *p_pos = file_pos;
|
||||
|
||||
close_streamfile(sf_map);
|
||||
return open_streamfile_by_filename(sf, subval);
|
||||
}
|
||||
@ -1185,6 +1191,7 @@ STREAMFILE* read_filemap_file(STREAMFILE* sf, int file_num) {
|
||||
current++;
|
||||
}
|
||||
}
|
||||
file_pos++;
|
||||
}
|
||||
|
||||
fail:
|
||||
|
@ -361,7 +361,9 @@ size_t read_key_file(uint8_t* buf, size_t buf_size, STREAMFILE* sf);
|
||||
|
||||
/* Opens .txtm file containing file:companion file(-s) mappings and tries to see if there's a match
|
||||
* then loads the associated companion file if one is found */
|
||||
STREAMFILE *read_filemap_file(STREAMFILE *sf, int file_num);
|
||||
STREAMFILE* read_filemap_file(STREAMFILE *sf, int file_num);
|
||||
STREAMFILE* read_filemap_file_pos(STREAMFILE *sf, int file_num, int* p_pos);
|
||||
|
||||
|
||||
/* hack to allow relative paths in various OSs */
|
||||
void fix_dir_separators(char* filename);
|
||||
|
@ -40,12 +40,11 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_rfrm,
|
||||
init_vgmstream_cstr,
|
||||
init_vgmstream_gcsw,
|
||||
init_vgmstream_ps2_ads,
|
||||
init_vgmstream_ads,
|
||||
init_vgmstream_nps,
|
||||
init_vgmstream_rwsd,
|
||||
init_vgmstream_xa,
|
||||
init_vgmstream_ps2_rxws,
|
||||
init_vgmstream_ps2_rxw,
|
||||
init_vgmstream_rxws,
|
||||
init_vgmstream_ngc_dsp_stm,
|
||||
init_vgmstream_exst,
|
||||
init_vgmstream_svag_kcet,
|
||||
@ -194,7 +193,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_nds_rrds,
|
||||
init_vgmstream_ps2_tk5,
|
||||
init_vgmstream_ps2_vsf_tta,
|
||||
init_vgmstream_ads,
|
||||
init_vgmstream_ads_midway,
|
||||
init_vgmstream_ps2_mcg,
|
||||
init_vgmstream_zsd,
|
||||
init_vgmstream_ps2_vgs,
|
||||
@ -262,11 +261,9 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_sqex_scd,
|
||||
init_vgmstream_ngc_nst_dsp,
|
||||
init_vgmstream_baf,
|
||||
init_vgmstream_baf_badrip,
|
||||
init_vgmstream_msf,
|
||||
init_vgmstream_ps3_past,
|
||||
init_vgmstream_sgxd,
|
||||
init_vgmstream_ngca,
|
||||
init_vgmstream_wii_ras,
|
||||
init_vgmstream_ps2_spm,
|
||||
init_vgmstream_x360_tra,
|
||||
@ -395,7 +392,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_dsp_switch_audio,
|
||||
init_vgmstream_sadf,
|
||||
init_vgmstream_h4m,
|
||||
init_vgmstream_ps2_ads_container,
|
||||
init_vgmstream_ads_container,
|
||||
init_vgmstream_asf,
|
||||
init_vgmstream_xmd,
|
||||
init_vgmstream_cks,
|
||||
@ -539,6 +536,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
||||
init_vgmstream_s14_sss, /* .s14/sss raw siren14 */
|
||||
init_vgmstream_raw_al, /* .al/al2 raw A-LAW */
|
||||
init_vgmstream_zwdsp, /* fake format */
|
||||
init_vgmstream_baf_badrip, /* crap, to be removed */
|
||||
init_vgmstream_rxws_badrip, /* crap, to be removed */
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */
|
||||
#endif
|
||||
|
@ -357,9 +357,9 @@ typedef enum {
|
||||
meta_BNSF, /* Bandai Namco Sound Format */
|
||||
|
||||
meta_XA, /* CD-ROM XA */
|
||||
meta_PS2_SShd, /* .ADS with SShd header */
|
||||
meta_ADS,
|
||||
meta_NPS,
|
||||
meta_PS2_RXWS, /* Sony games (Genji, Okage Shadow King, Arc The Lad Twilight of Spirits) */
|
||||
meta_RXWS,
|
||||
meta_RAW_INT,
|
||||
meta_EXST,
|
||||
meta_SVAG_KCET,
|
||||
@ -455,7 +455,7 @@ typedef enum {
|
||||
meta_SAT_BAKA, /* Crypt Killer */
|
||||
meta_VSF,
|
||||
meta_PS2_VSF_TTA, /* Tiny Toon Adventures: Defenders of the Universe */
|
||||
meta_ADS, /* Gauntlet Dark Legends (GC) */
|
||||
meta_ADS_MIDWAY,
|
||||
meta_PS2_SPS, /* Ape Escape 2 */
|
||||
meta_PS2_XA2_RRP, /* RC Revenge Pro */
|
||||
meta_NGC_DSP_KONAMI, /* Konami DSP header, found in various games */
|
||||
@ -563,7 +563,6 @@ typedef enum {
|
||||
meta_MSF,
|
||||
meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */
|
||||
meta_SGXD, /* Sony: Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */
|
||||
meta_NGCA, /* GoldenEye 007 (Wii) */
|
||||
meta_WII_RAS, /* Donkey Kong Country Returns (Wii) */
|
||||
meta_PS2_SPM, /* Lethal Skies Elite Pilot: Team SW */
|
||||
meta_X360_TRA, /* Def Jam Rapstar */
|
||||
|
Loading…
x
Reference in New Issue
Block a user