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:
bnnm 2021-06-21 00:53:49 +02:00 committed by GitHub
commit becac80999
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1233 additions and 1382 deletions

167
README.md
View File

@ -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)

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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"},

View File

@ -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;
}
}

View File

@ -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"
>

View File

@ -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" />

View File

@ -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>

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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))

View File

@ -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 */

View File

@ -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) */

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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_ */

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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:

View File

@ -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);

View File

@ -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

View File

@ -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 */