mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-18 15:54:05 +01:00
Merge pull request #882 from bnnm/txth-strwav
- Add TXT multi txth, name table for subsongs, special subsong value - Fix some .str+wav bugs [Sneak King (Xbox), Fairly OddParents: BTR (PS2)]
This commit is contained in:
commit
4bd91df3c1
21
doc/BUILD.md
21
doc/BUILD.md
@ -93,8 +93,20 @@ In Linux, Makefiles can be used to cross-compile with the MingW headers, but may
|
||||
*Autotools* should build and install it as `vgmstream-cli`, this is explained in detail in the Audacious section. It also build with (some) extra codecs. Some Linux distributions like Arch Linux include pre-patched vgmstream with most libraries, you may want that instead:
|
||||
https://aur.archlinux.org/packages/vgmstream-kode54-git/
|
||||
|
||||
You may try CMake instead as it may be simpler and handle libs better.
|
||||
You may try CMake instead as it may be simpler and handle libs better. Some older distros may not work though (CMake version needs to recognize FILTER command). You may need to install resulting artifacts manually (check ./cli dir).
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libmpg123-dev libvorbis-dev libspeex-dev
|
||||
sudo apt-get install -y libavformat-dev libavcodec-dev libavutil-dev libswresample-dev
|
||||
sudo apt-get install -y libao-dev audacious-dev
|
||||
sudo apt-get install -y cmake
|
||||
|
||||
git clone https://github.com/vgmstream/vgmstream
|
||||
cd vgmstream
|
||||
|
||||
cmake .
|
||||
make
|
||||
```
|
||||
|
||||
Windows CMD example:
|
||||
```
|
||||
@ -189,7 +201,7 @@ pkg-config --modversion audacious
|
||||
```
|
||||
```
|
||||
# base vgmstream build
|
||||
git clone https://github.com/losnoco/vgmstream
|
||||
git clone https://github.com/vgmstream/vgmstream
|
||||
cd vgmstream
|
||||
|
||||
# main vgmstream build (if you get errors here please report)
|
||||
@ -224,7 +236,7 @@ git clean -fd
|
||||
```
|
||||
To update vgmstream it's probably easiest to remove the `vgmstream` folder and start again from *base vgmstream build* step, since updates often require a full rebuild anyway.
|
||||
|
||||
Instead of autotools you can try building with CMake. Some older distros may not work though (CMake version needs to recognize FILTER command). You may need to install resulting artifacts manually.
|
||||
Instead of autotools you can try building with CMake. Some older distros may not work though (CMake version needs to recognize FILTER command). You may need to install resulting artifacts manually (check ./audacious dir).
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libmpg123-dev libvorbis-dev libspeex-dev
|
||||
@ -232,6 +244,9 @@ sudo apt-get install -y libavformat-dev libavcodec-dev libavutil-dev libswresamp
|
||||
sudo apt-get install -y libao-dev audacious-dev
|
||||
sudo apt-get install -y cmake
|
||||
|
||||
git clone https://github.com/vgmstream/vgmstream
|
||||
cd vgmstream
|
||||
|
||||
cmake .
|
||||
make
|
||||
```
|
||||
|
236
doc/TXTH.md
236
doc/TXTH.md
@ -1,18 +1,13 @@
|
||||
# TXTH FORMAT
|
||||
|
||||
TXTH is a simple text file that uses text commands to simulate a header for files unsupported by vgmstream, mainly headerless audio.
|
||||
TXTH is a simple text file with text commands to simulate a header for files unsupported by vgmstream, mainly headerless audio.
|
||||
|
||||
When an unsupported file is loaded (for instance "bgm01.snd"), vgmstream tries to find a TXTH header in the same dir, in this order:
|
||||
When an unsupported file is loaded (for instance `bgm01.snd`), vgmstream tries to find a TXTH header in the same dir, in this order:
|
||||
- `(filename.ext).txth`
|
||||
- `.(ext).txth`
|
||||
- `.txth`
|
||||
|
||||
If found and parsed correctly (the .txth may be rejected if incorrect commands are found) vgmstream will try to play the file as described. Extension must be accepted/added to vgmstream (plugins like foobar2000 only load extensions from a whitelist in formats.c), or one could rename to any supported extension (like .vgmstream), or leave the file extensionless.
|
||||
|
||||
You can also use `.(sub).(ext).txth` (if the file is `filename.sub.ext`), to allow mixing slightly different files in the same folder. The `sub` part doesn't need to be an extension, for example:
|
||||
- `001.1ch.str`, `001.1ch.str` may use `.1ch.txth`
|
||||
- `003.2ch.str`, `003.2ch.str` may use `.2ch.txth`
|
||||
- etc
|
||||
If found and parsed correctly, vgmstream will play the file as described.
|
||||
|
||||
## Example of a TXTH file
|
||||
For an unsupported `bgm01.vag` this would be a simple TXTH for it:
|
||||
@ -29,10 +24,19 @@ A text file with the above commands must be saved as `.vag.txth` or `.txth` (pre
|
||||
|
||||
While the main point is playing the file, many of TXTH's features are aimed towards keeping original data intact, for documentation and preservation purposes; try leaving data as untouched as possible and consider how the game plays the file, as there is a good chance some feature can mimic it.
|
||||
|
||||
Also check the [examples](#examples) section for some quick recipes, of varying complexity.
|
||||
|
||||
|
||||
## Issues
|
||||
The `.txth` may be rejected if incorrect commands are found. Since at the moment vgmstream can't communicate why a file is rejected, try starting with a simple case (see examples) then add more complex commands until it fully works.
|
||||
|
||||
Extension must be accepted/added to vgmstream (plugins like foobar2000 only load extensions from an accepted list in `formats.c`), or one could rename to any supported extension (like `.vgmstream`), or leave the file extensionless. Before renaming consider reporting the unknown extension so it can be added to the list (so similar games benefit, as long as the extension is a good fit). Some plugins allow playing unknown extensions too.
|
||||
|
||||
Note that TXTH has *lower* priority than most (not all) vgmstream formats, by design. This means your `.txth` may be ignored if vgmstream thinks it can play your file better. If vgmstream plays your file somewhat off, rather renaming to force a `.txth`, report the bug so that and similar cases can be fixed. TXTH isn't meant to be a replacement of vgmstream's parsers, but a way to play cases that aren't a good fit be added directly to vgmstream.
|
||||
|
||||
|
||||
## Available commands
|
||||
|
||||
The file is made of lines with `key = value` commands describing a header. Commands are all case sensitive and spaces are optional: `key=value`, `key = value`, and so on are all ok. Comments start with # and can be inlined.
|
||||
The file is made of lines with `key = value` commands describing a header. Commands are all case sensitive and spaces are optional: `key=value`, `key = value`, and so on are all ok, while `Key = VaLuE` is not. Comments start with `#` and can be inlined.
|
||||
|
||||
The parser is fairly simple and may be buggy or unexpected in some cases. The order of keys is variable but some things won't work if others aren't defined (ex. bytes-to-samples may not work without channels or interleave) or need to be done in a certain order (due to technical reasons) as explained below.
|
||||
|
||||
@ -50,9 +54,9 @@ The following can be used in place of `(value)` for `(key) = (value)` commands.
|
||||
* Example: `@0x10:BE$2` means `get big endian 16b value at 0x10`
|
||||
- `(field)`: uses current value of some fields. Accepted strings:
|
||||
- `interleave, interleave_last, channels, sample_rate, start_offset, data_size, num_samples, loop_start_sample, loop_end_sample, subsong_count, subsong_spacing, subfile_offset, subfile_size, base_offset, name_valueX`
|
||||
- `subsong` is a special field for current subsong
|
||||
- `(other)`: other special values for certain keys, described per key
|
||||
|
||||
|
||||
The above may be combined with math operations (+-*/&): `(key) = (number) (op) (offset) (op) (field) (...)`
|
||||
|
||||
### KEYS
|
||||
@ -201,12 +205,12 @@ interleave_last = (value)|auto
|
||||
```
|
||||
|
||||
#### ID VALUES
|
||||
Validates that `id_value` (normally set as constant value) matches value read at `id_offset`. The file will be rejected and won't play if values don't match.
|
||||
Validates that `id_value` (normally set as constant value) matches value read at `id_check`. The file will be rejected and won't play if values don't match.
|
||||
|
||||
Can be redefined several times, it's checked whenever a new id_offset is found.
|
||||
Can be redefined several times, it's checked whenever a new id_offset is found. `id_offset` can be used as an alt for `id_check`
|
||||
```
|
||||
id_value = (value)
|
||||
id_offset = (value)
|
||||
id_check = (value)
|
||||
```
|
||||
|
||||
#### NUMBER OF CHANNELS [REQUIRED]
|
||||
@ -262,7 +266,7 @@ Those tell vgmstream how long the song is. Define loop points for the track to r
|
||||
|
||||
You can use `loop_start` and `loop_end` instead as aliases of `loop_start_sample` and `loop_end_sample` (no difference).
|
||||
|
||||
To activate loops you need to define both `loop_start` and `loop_end` (rather than say, only defining `start` and defaulting `end` to `num_samples`). This is to make the *.txtp* more descriptive, and avoid ambiguity in cases where `start` value is 0. See loop settings below to fine tune when to loop.
|
||||
To activate loops you need to define both `loop_start` and `loop_end` (rather than say, only defining `start` and defaulting `end` to `num_samples`). This is to make the *.txth* more descriptive, and avoid ambiguity in cases where `start` value is 0. See loop settings below to fine tune when to loop.
|
||||
|
||||
Special values:
|
||||
- `data_size`: automatically converts bytes-to-samples (a few codecs don't allow this)
|
||||
@ -397,7 +401,7 @@ File is first "dechunked" then played with using other settings (`start_offset`
|
||||
You need to set:
|
||||
- `chunk_count`: total number of interleaved chunks (ex. 3=3 interleaved songs)
|
||||
- `chunk_number`: first chunk to start (ex. 1=0x00000, 2=0x10000, 3=0x20000...)
|
||||
* If you set `subsong_count` first `chunk_number` will be auto-set per subsong (subsong 1 starts from chunk number 1, subsong 2 from chunk 2, etc)
|
||||
* If you set `subsong_count` and `chunk_count` first, `chunk_number` will be auto-set per subsong (subsong 1 starts from chunk number 1, subsong 2 from chunk 2, etc)
|
||||
- `chunk_start`: absolute offset where chunks start (normally 0x00)
|
||||
- `chunk_size`: amount of data in a single chunk (ex. 0x10000)
|
||||
For fine-tuning you can optionally set (before `chunk_size`, for reasons):
|
||||
@ -442,6 +446,16 @@ bgm*: 2 # 2ch: all other files, notice order matters
|
||||
|
||||
While you can put anything in the values, this feature is meant to be used to store some number that points to the actual data inside a real multi-header, that could be set with `header_file`. If you feel the need to store many constant values per file, there is good chance it can be done in some better, simpler way.
|
||||
|
||||
This function also works with subsongs, with this syntax:
|
||||
```
|
||||
# for subsong 1 in filename
|
||||
(filename)#1: (value 1)
|
||||
# for any subsong 2
|
||||
#2: (value 2)
|
||||
...
|
||||
```
|
||||
Then subsong N would automatically use its own `name_value`.
|
||||
|
||||
|
||||
#### BASE OFFSET MODIFIER
|
||||
You can set a default offset that affects next `@(offset)` reads making them `@(offset + base_offset)`, for cleaner parsing.
|
||||
@ -450,12 +464,40 @@ This is particularly interesting when combined with offsets to some long value.
|
||||
|
||||
It also allows parsing formats that set offsets to another offset, by "chaining" `base_offset`. With `base_offset = @0x10` (pointing to `0x40`) then `base_offset = @0x20`, it reads value at `0x60`. Set to 0 when you want to disable/reset the chain: `base_offset = @0x10` then `base_offset = 0` then `base_offset = @0x20` reads value at `0x20`
|
||||
|
||||
|
||||
```
|
||||
base_offset = (value)
|
||||
```
|
||||
|
||||
|
||||
#### MULTI-TXTH
|
||||
TXTH can't do conditions (`if`) but sometimes you have have variations of the same format in the same dir. You can set multiple `.txth` files to try until one works. Use `id_value/id_check` to reject wrong `.txth` (otherwise the first one will be selected).
|
||||
|
||||
```
|
||||
multi_txth = (filename), (filename), ...
|
||||
```
|
||||
For example:
|
||||
```
|
||||
multi_txth = .2ch.txth, .4ch.txth
|
||||
```
|
||||
|
||||
*.2ch.txth*
|
||||
```
|
||||
id_value = 2
|
||||
id_check = @0x00 # 2ch only
|
||||
|
||||
... #some settings for stereo
|
||||
|
||||
```
|
||||
*.4ch.txth*
|
||||
```
|
||||
id_value = 4
|
||||
id_check = @0x00 # 4ch only
|
||||
|
||||
... #different settings for 4ch
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Complex usages
|
||||
|
||||
### Temporary values
|
||||
@ -701,39 +743,12 @@ sample_rate = @0x04 #reads at 0x1204
|
||||
|
||||
## Examples
|
||||
|
||||
#### Colin McRae DiRT (PC) .wip.txth
|
||||
#### Spy Hunter (GC) .pcm.txth
|
||||
```
|
||||
id_value = 0x00000000 #check that value at 0x00 is really 0x00000000
|
||||
id_offset = @0x00:BE
|
||||
|
||||
codec = PCM16LE
|
||||
channels = 2
|
||||
codec = PCM8
|
||||
sample_rate = 32000
|
||||
start_offset = 0x04
|
||||
num_samples = data_size
|
||||
loop_start_sample = 0
|
||||
loop_end_sample = data_size
|
||||
```
|
||||
|
||||
#### Kim Possible: What's the Switch (PS2) .str.txth
|
||||
```
|
||||
codec = PSX
|
||||
interleave = 0x2000
|
||||
channels = 2
|
||||
sample_rate = 48000
|
||||
num_samples = data_size
|
||||
interleave_last = auto
|
||||
```
|
||||
|
||||
#### Manhunt (Xbox) .rib.txth
|
||||
```
|
||||
codec = XBOX
|
||||
codec_mode = 1 #interleaved XBOX
|
||||
interleave = 0xD800
|
||||
|
||||
channels = 12
|
||||
sample_rate = 44100
|
||||
start_offset = 0x00
|
||||
channels = 1
|
||||
start_offset = 0
|
||||
num_samples = data_size
|
||||
```
|
||||
|
||||
@ -747,12 +762,50 @@ sample_rate = 44100
|
||||
num_samples = data_size
|
||||
```
|
||||
|
||||
#### Spy Hunter (GC) .pcm.txth
|
||||
#### Aladdin in Nasira's Revenge (PS1) .cvs.txth
|
||||
```
|
||||
codec = PCM8
|
||||
sample_rate = 32000
|
||||
codec = PSX
|
||||
interleave = 0x10
|
||||
sample_rate = 24000
|
||||
channels = 1
|
||||
start_offset = 0
|
||||
padding_size = auto-empty
|
||||
num_samples = data_size
|
||||
```
|
||||
|
||||
#### Kim Possible: What's the Switch (PS2) .str.txth
|
||||
```
|
||||
codec = PSX
|
||||
interleave = 0x2000
|
||||
channels = 2
|
||||
sample_rate = 48000
|
||||
num_samples = data_size
|
||||
interleave_last = auto
|
||||
```
|
||||
|
||||
#### Colin McRae DiRT (PC) .wip.txth
|
||||
```
|
||||
# first check that value at 0x00 is really 0x00000000 (rarely needed though)
|
||||
id_value = 0x00000000
|
||||
id_check = @0x00:BE
|
||||
|
||||
codec = PCM16LE
|
||||
channels = 2
|
||||
sample_rate = 32000
|
||||
start_offset = 0x04
|
||||
num_samples = data_size
|
||||
loop_start_sample = 0
|
||||
loop_end_sample = data_size
|
||||
```
|
||||
|
||||
#### Manhunt (Xbox) .rib.txth
|
||||
```
|
||||
codec = XBOX
|
||||
codec_mode = 1 #interleaved XBOX
|
||||
interleave = 0xD800
|
||||
|
||||
channels = 12
|
||||
sample_rate = 44100
|
||||
start_offset = 0x00
|
||||
num_samples = data_size
|
||||
```
|
||||
|
||||
@ -776,16 +829,6 @@ coef_spacing = 0x10000
|
||||
coef_endianness = BE
|
||||
```
|
||||
|
||||
#### Aladdin in Nasira's Revenge (PS1) .cvs.txth
|
||||
```
|
||||
codec = PSX
|
||||
interleave = 0x10
|
||||
sample_rate = 24000
|
||||
channels = 1
|
||||
padding_size = auto-empty
|
||||
num_samples = data_size
|
||||
```
|
||||
|
||||
#### Shikigami no Shiro - Nanayozuki Gensoukyoku (PS2) bgm.txth
|
||||
```
|
||||
codec = PSX
|
||||
@ -1121,3 +1164,72 @@ loop_end = @0x24:BE #0x2c?
|
||||
loop_start = @0x28:BE
|
||||
loop_flag = @0x2c:BE
|
||||
```
|
||||
|
||||
#### Grand Theft Auto: San Andreas .vgmstream.txth
|
||||
```
|
||||
# once extracted from bigfiles there are 2 types of files with hardcoded settings,
|
||||
# so we need 2 .txth
|
||||
multi_txth = .type2.txth, .type4.txth
|
||||
```
|
||||
|
||||
*.type2.txth*
|
||||
```
|
||||
id_value = 2
|
||||
id_check = @0x1f80
|
||||
|
||||
codec = PSX
|
||||
channels = 2
|
||||
sample_rate = @0x1F44
|
||||
interleave = 0x10000
|
||||
start_offset = 0x00
|
||||
|
||||
chunk_count = 1
|
||||
chunk_start = 0x1f84
|
||||
chunk_data_size = 0x20000
|
||||
chunk_size = 0x21000
|
||||
|
||||
num_samples = data_size
|
||||
```
|
||||
|
||||
*.type4.txth*
|
||||
```
|
||||
id_value = 4
|
||||
id_check = @0x1F80
|
||||
|
||||
name_table = .names.txt
|
||||
|
||||
subsong_count = 2
|
||||
|
||||
base_offset = name_value1
|
||||
|
||||
codec = PSX
|
||||
channels = 2
|
||||
sample_rate = @0x04
|
||||
interleave = name_value2
|
||||
start_offset = 0x00
|
||||
|
||||
chunk_count = 1
|
||||
chunk_start = 0x1f84
|
||||
chunk_header_size = name_value3
|
||||
chunk_data_size = name_value4
|
||||
chunk_size = 0x21000
|
||||
|
||||
num_samples = data_size
|
||||
|
||||
# base_offset = 0x1F40
|
||||
# 00: stream size without padding
|
||||
# 04: stream 1 sample rate
|
||||
# 08: stream size without padding (same)
|
||||
# 0c: stream 2 sample rate (same)
|
||||
# repeat for stream 3 and 4 if any
|
||||
# 1/2 are mini streams (interleave 0x800, chunk size 0x1000, padding 0x20000)
|
||||
# 1/2 are standard streams (interleave 0x10000, padding 0x1000, chunk size 0x20000)
|
||||
# (mini streams are muffled versions of the standard ones)
|
||||
```
|
||||
|
||||
*.names.txt*
|
||||
```
|
||||
# base_offset, interleave, chunk_header_size, chunk_data_size
|
||||
#1: 0x1F40, 0x800, 0x00, 0x1000
|
||||
#2: 0x1F50, 0x10000, 0x1000, 0x20000
|
||||
```
|
||||
|
@ -4,12 +4,13 @@
|
||||
|
||||
typedef enum { PSX, DSP, XBOX, WMA, IMA, XMA2 } strwav_codec;
|
||||
typedef struct {
|
||||
int32_t channels;
|
||||
int32_t sample_rate;
|
||||
int tracks;
|
||||
int channels;
|
||||
int sample_rate;
|
||||
int32_t num_samples;
|
||||
int32_t loop_start;
|
||||
int32_t loop_end;
|
||||
int32_t loop_flag;
|
||||
int loop_flag;
|
||||
size_t interleave;
|
||||
|
||||
off_t coefs_offset;
|
||||
@ -20,7 +21,7 @@ typedef struct {
|
||||
strwav_codec codec;
|
||||
} strwav_header;
|
||||
|
||||
static int parse_header(STREAMFILE* sf_h, strwav_header* strwav);
|
||||
static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwav);
|
||||
|
||||
|
||||
/* STR+WAV - Blitz Games streams+header */
|
||||
@ -61,20 +62,23 @@ VGMSTREAM* init_vgmstream_str_wav(STREAMFILE* sf) {
|
||||
}
|
||||
|
||||
/* detect version */
|
||||
if (!parse_header(sf_h, &strwav))
|
||||
if (!parse_header(sf_h, sf, &strwav))
|
||||
goto fail;
|
||||
|
||||
if (strwav.flags == 0)
|
||||
goto fail;
|
||||
if (strwav.channels > 8)
|
||||
goto fail;
|
||||
|
||||
/* &0x01: loop?, &0x02: non-mono?, &0x04: stream???, &0x08: unused? */
|
||||
if (strwav.flags > 0x07) {
|
||||
VGM_LOG("STR+WAV: unknown flags\n");
|
||||
/* &0x01: loop, &0x02: stereo tracks, &0x04: stream?, &0x200: has named cues? */
|
||||
if (strwav.flags & 0xFFFFFDF8) {
|
||||
VGM_LOG("STR+WAV: unknown flags %x\n", strwav.flags);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
strwav.loop_flag = strwav.flags & 0x01;
|
||||
|
||||
if (!strwav.channels)
|
||||
strwav.channels = strwav.tracks * (strwav.flags & 0x02 ? 2 : 1);
|
||||
if (strwav.channels > 8)
|
||||
goto fail;
|
||||
|
||||
start_offset = 0x00;
|
||||
|
||||
@ -195,170 +199,318 @@ fail:
|
||||
|
||||
/* Parse header versions. Almost every game uses its own variation (struct serialization?),
|
||||
* so detection could be improved once enough versions are known. */
|
||||
static int parse_header(STREAMFILE* sf_h, strwav_header* strwav) {
|
||||
static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwav) {
|
||||
size_t header_size;
|
||||
|
||||
if (read_32bitBE(0x00,sf_h) != 0x00000000)
|
||||
if (read_u32be(0x00,sf_h) != 0x00000000)
|
||||
goto fail;
|
||||
|
||||
header_size = get_streamfile_size(sf_h);
|
||||
|
||||
/* most variations have extra tables (at least 1 entry):
|
||||
* - table1: samples
|
||||
* - table2: f32 ms + cue hash (ex. 2D7FC4C5 = __EndMarker0, not unique) + optional 0x38 cue name
|
||||
* table entries don't need to match (table2 may be slightly bigger)
|
||||
*/
|
||||
|
||||
//breaking ta rules full test again, fuse with Pac-Man World 3
|
||||
//same on xbox and pc
|
||||
//same with zapper + pw3 gc
|
||||
|
||||
|
||||
//todo loop start/end values may be off for some headers
|
||||
|
||||
/* Fuzion Frenzy (Xbox)[2001] wma */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000900 &&
|
||||
read_32bitLE(0x20,sf_h) == 0 && /* no num_samples */
|
||||
read_32bitLE(0x24,sf_h) == read_32bitLE(0x80,sf_h) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,sf_h) == 0x10 &&
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
|
||||
read_u32le(0x0c,sf_h) != header_size &&
|
||||
read_u32le(0x24,sf_h) != 0 &&
|
||||
read_u32le(0x24,sf_h) == read_u32le(0x80,sf_h) && /* sample rate repeat */
|
||||
header_size == 0x110 /* no value in header */
|
||||
) {
|
||||
strwav->num_samples = read_32bitLE(0x20,sf_h); /* 0 but will be rectified later */
|
||||
strwav->sample_rate = read_32bitLE(0x24,sf_h);
|
||||
strwav->flags = read_32bitLE(0x2c,sf_h);
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->num_samples = read_s32le(0x20,sf_h); /* 0 but will be rectified later */
|
||||
strwav->sample_rate = read_s32le(0x24,sf_h);
|
||||
/* 0x28: 16 bps */
|
||||
strwav->flags = read_u32le(0x2c,sf_h);
|
||||
/* 0x38: related to samples? */
|
||||
/* 0x48: number of chunks */
|
||||
strwav->tracks = read_s32le(0x60,sf_h);
|
||||
/* 0x80: sample rate 2 */
|
||||
|
||||
strwav->loop_start = 0;
|
||||
strwav->loop_end = 0;
|
||||
|
||||
strwav->channels = read_32bitLE(0x60,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = 0;
|
||||
|
||||
strwav->codec = WMA;
|
||||
//;VGM_LOG("STR+WAV: header Fuzion Frenzy (Xbox)\n");
|
||||
strwav->interleave = 0;
|
||||
;VGM_LOG("STR+WAV: header FF (Xbox)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Taz: Wanted (GC)[2002] */
|
||||
/* Cubix Robots for Everyone: Showdown (GC)[2003] */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000900 &&
|
||||
read_32bitBE(0x24,sf_h) == read_32bitBE(0x90,sf_h) && /* sample rate repeat */
|
||||
read_32bitBE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitBE(0xa0,sf_h) == header_size /* ~0x3C0 */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
|
||||
read_u32be(0x0c,sf_h) != header_size &&
|
||||
read_u32le(0x24,sf_h) != 0 &&
|
||||
read_u32be(0x24,sf_h) == read_u32be(0x90,sf_h) && /* sample rate repeat */
|
||||
read_u32be(0xa0,sf_h) == header_size /* ~0x3C0 */
|
||||
) {
|
||||
strwav->num_samples = read_32bitBE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x24,sf_h);
|
||||
strwav->flags = read_32bitBE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitBE(0xb8,sf_h);
|
||||
strwav->loop_end = read_32bitBE(0xbc,sf_h);
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->num_samples = read_s32be(0x20,sf_h);
|
||||
strwav->sample_rate = read_s32be(0x24,sf_h);
|
||||
/* 0x28: 16 bps */
|
||||
strwav->flags = read_u32be(0x2c,sf_h);
|
||||
/* 0x38: related to samples? */
|
||||
strwav->tracks = read_s32be(0x50,sf_h);
|
||||
/* 0x58: number of chunks */
|
||||
/* 0x90: sample rate 2 */
|
||||
/* 0xa0: header size */
|
||||
strwav->loop_start = read_s32be(0xb8,sf_h);
|
||||
strwav->loop_end = read_s32be(0xbc,sf_h);
|
||||
/* 0xc0: standard DSP header */
|
||||
|
||||
strwav->channels = read_32bitBE(0x50,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
|
||||
|
||||
strwav->coefs_offset = 0xdc;
|
||||
strwav->codec = DSP;
|
||||
//;VGM_LOG("STR+WAV: header Taz: Wanted (GC)\n");
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000;
|
||||
strwav->coefs_offset = 0xdc;
|
||||
;VGM_LOG("STR+WAV: header TZW/CBX (GC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The Fairly OddParents - Breakin' da Rules (Xbox)[2003] */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000900 &&
|
||||
read_32bitLE(0x24,sf_h) == read_32bitLE(0xb0,sf_h) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitLE(0xc0,sf_h)*0x04 + read_32bitLE(0xc4,sf_h) == header_size /* ~0xe0 + variable */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
|
||||
read_u32le(0x24,sf_h) == read_u32le(0xb0,sf_h) && /* sample rate repeat */
|
||||
read_u32le(0xc0,sf_h)*0x04 + read_u32le(0xc4,sf_h) == header_size /* ~0xe0 + variable */
|
||||
) {
|
||||
strwav->num_samples = read_32bitLE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitLE(0x24,sf_h);
|
||||
strwav->flags = read_32bitLE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitLE(0x38,sf_h);
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->num_samples = read_s32le(0x20,sf_h);
|
||||
strwav->sample_rate = read_s32le(0x24,sf_h);
|
||||
/* 0x28: 16 bps */
|
||||
strwav->flags = read_u32le(0x2c,sf_h);
|
||||
strwav->loop_start = read_s32le(0x38,sf_h);
|
||||
strwav->tracks = read_s32le(0x70,sf_h);
|
||||
/* 0x78: number of chunks */
|
||||
/* 0xb0: sample rate 2 */
|
||||
/* 0xc0: table1 entries */
|
||||
/* 0xc4: table1 offset */
|
||||
/* 0xdc: total frames */
|
||||
|
||||
strwav->loop_end = strwav->num_samples;
|
||||
|
||||
strwav->channels = read_32bitLE(0x70,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = 0xD800/2;
|
||||
|
||||
strwav->codec = XBOX;
|
||||
//;VGM_LOG("STR+WAV: header The Fairly OddParents (Xbox)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Bad Boys II (GC)[2004] */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000800 &&
|
||||
read_32bitBE(0x24,sf_h) == read_32bitBE(0xb0,sf_h) && /* sample rate repeat */
|
||||
read_32bitBE(0x24,sf_h) == read_32bitBE(read_32bitBE(0xe0,sf_h)+0x08,sf_h) && /* sample rate vs 1st DSP header */
|
||||
read_32bitBE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitBE(0xc0,sf_h)*0x04 + read_32bitBE(0xc4,sf_h) == header_size /* variable + variable */
|
||||
) {
|
||||
strwav->num_samples = read_32bitBE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x24,sf_h);
|
||||
strwav->flags = read_32bitBE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitBE(0xd8,sf_h);
|
||||
strwav->loop_end = read_32bitBE(0xdc,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x70,sf_h) * read_32bitBE(0x88,sf_h); /* tracks of Nch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
|
||||
|
||||
strwav->dsps_table = 0xe0;
|
||||
strwav->codec = DSP;
|
||||
//;VGM_LOG("STR+WAV: header Bad Boys II (GC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Bad Boys II (PS2)[2004] */
|
||||
/* Pac-Man World 3 (PS2)[2005] */
|
||||
if ((read_32bitBE(0x04,sf_h) == 0x00000800 ||
|
||||
read_32bitBE(0x04,sf_h) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */
|
||||
read_32bitLE(0x24,sf_h) == read_32bitLE(0x70,sf_h) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitLE(0x78,sf_h)*0x04 + read_32bitLE(0x7c,sf_h) == header_size /* ~0xe0 + variable */
|
||||
) {
|
||||
strwav->num_samples = read_32bitLE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitLE(0x24,sf_h);
|
||||
strwav->flags = read_32bitLE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitLE(0x38,sf_h);
|
||||
strwav->interleave = read_32bitLE(0x40,sf_h) == 1 ? 0x8000 : 0x4000;
|
||||
strwav->loop_end = read_32bitLE(0x54,sf_h);
|
||||
|
||||
strwav->channels = read_32bitLE(0x40,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000;
|
||||
|
||||
strwav->codec = PSX;
|
||||
//;VGM_LOG("STR+WAV: header Bad Boys II (PS2)\n");
|
||||
strwav->interleave = strwav->tracks > 1 ? 0xD800/2 : 0xD800;
|
||||
;VGM_LOG("STR+WAV: header FOP (Xbox)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Pac-Man World 3 (Xbox)[2005] */
|
||||
if ((read_u32be(0x04,sf_h) == 0x00000800 ||
|
||||
read_u32be(0x04,sf_h) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */
|
||||
read_u32be(0x04,sf_h) == 0x01000800) && /* rare, mu_spectral1_explore_2 */
|
||||
read_u32le(0x24,sf_h) == read_u32le(0xB0,sf_h) && /* sample rate repeat */
|
||||
read_u32le(0x28,sf_h) == 0x10 &&
|
||||
read_u32le(0xE0,sf_h) + read_u32le(0xE4,sf_h) * 0x40 == header_size /* ~0x100 + cues */
|
||||
) {
|
||||
strwav->num_samples = read_u32le(0x20,sf_h);
|
||||
strwav->sample_rate = read_u32le(0x24,sf_h);
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->num_samples = read_s32le(0x20,sf_h);
|
||||
strwav->sample_rate = read_s32le(0x24,sf_h);
|
||||
/* 0x28: 16 bps */
|
||||
strwav->flags = read_u32le(0x2c,sf_h);
|
||||
strwav->loop_start = read_u32le(0x38,sf_h);
|
||||
strwav->loop_start = read_s32le(0x38,sf_h);
|
||||
strwav->tracks = read_s32le(0x70,sf_h);
|
||||
/* 0x78: number of chunks */
|
||||
/* 0xb0: sample rate 2 */
|
||||
/* 0xdc: total frames */
|
||||
/* 0xe0: table2 offset */
|
||||
/* 0xe4: table2 entries */
|
||||
/* 0xf0: default hashname? */
|
||||
|
||||
strwav->loop_end = strwav->num_samples;
|
||||
|
||||
strwav->channels = read_u32le(0x70,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0xD800/2 : 0xD800;
|
||||
|
||||
strwav->codec = XBOX;
|
||||
//;VGM_LOG("STR+WAV: Pac-Man World 3 (Xbox)\n");
|
||||
strwav->interleave = strwav->tracks > 1 ? 0xD800/2 : 0xD800;
|
||||
;VGM_LOG("STR+WAV: PW3 (Xbox)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The Fairly OddParents! - Shadow Showdown (GC)[2004] */
|
||||
/* Bad Boys II (GC)[2004] */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000800 &&
|
||||
read_u32be(0x24,sf_h) == read_u32be(0xb0,sf_h) && /* sample rate repeat */
|
||||
read_u32be(0x24,sf_h) == read_u32be(read_u32be(0xe0,sf_h)+0x08,sf_h) && /* sample rate vs 1st DSP header */
|
||||
read_u32be(0xc0,sf_h)*0x04 + read_u32be(0xc4,sf_h) == header_size /* variable + variable */
|
||||
) {
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->num_samples = read_s32be(0x20,sf_h);
|
||||
strwav->sample_rate = read_s32be(0x24,sf_h);
|
||||
/* 0x28: 16 bps */
|
||||
strwav->flags = read_u32be(0x2c,sf_h);
|
||||
/* 0x38: related to samples? */
|
||||
strwav->tracks = read_s32be(0x70,sf_h);
|
||||
/* 0x78: number of chunks */
|
||||
/* 0x88: track channels */
|
||||
/* 0xb0: sample rate 2 */
|
||||
/* 0xc0: table1 entries */
|
||||
/* 0xc4: table1 offset */
|
||||
|
||||
strwav->loop_start = read_s32be(0xd8,sf_h);
|
||||
strwav->loop_end = read_s32be(0xdc,sf_h);
|
||||
/* 0xe0: DSP offset per channel */
|
||||
/* 0x100: standard DSP header */
|
||||
|
||||
strwav->codec = DSP;
|
||||
strwav->dsps_table = 0xe0;
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000;
|
||||
;VGM_LOG("STR+WAV: header FOP/BB2 (GC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Zapper: One Wicked Cricket! (GC)[2005] */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
|
||||
read_u32be(0x24,sf_h) == read_u32be(0xB0,sf_h) && /* sample rate repeat */
|
||||
read_u32le(0xc0,sf_h) == header_size /* LE! */
|
||||
) {
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->num_samples = read_s32be(0x20,sf_h);
|
||||
strwav->sample_rate = read_s32be(0x24,sf_h);
|
||||
/* 0x28: 16 bps */
|
||||
strwav->flags = read_u32be(0x2c,sf_h);
|
||||
/* 0x38: related to samples? */
|
||||
strwav->tracks = read_s32be(0x70,sf_h);
|
||||
/* 0x78: number of chunks */
|
||||
/* 0x88: track channels */
|
||||
/* 0xC0: size */
|
||||
/* 0xb0: sample rate 2 */
|
||||
/* 0xc0: table1 offset LE */
|
||||
|
||||
strwav->loop_start = read_s32be(0xd8,sf_h);
|
||||
strwav->loop_end = read_s32be(0xdc,sf_h);
|
||||
/* 0xe0: DSP offset per channel */
|
||||
/* 0x100: standard DSP header */
|
||||
|
||||
strwav->codec = DSP;
|
||||
strwav->dsps_table = 0xe0;
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000;
|
||||
|
||||
;VGM_LOG("STR+WAV: header ZP (GC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The Fairly OddParents - Breakin' da Rules (PS2)[2003] */
|
||||
/* The Fairly OddParents! - Shadow Showdown (PS2)[2004] */
|
||||
/* Bad Boys II (PS2)[2004] */
|
||||
/* Zapper: One Wicked Cricket! (PS2)[2005] */
|
||||
if ((read_u32be(0x04,sf_h) == 0x00000800 || /* BB2 */
|
||||
read_u32be(0x04,sf_h) == 0x00000900) && /* FOP, ZP */
|
||||
read_u32le(0x24,sf_h) == read_u32le(0x70,sf_h) && /* sample rate repeat */
|
||||
read_u32le(0x78,sf_h)*0x04 + read_u32le(0x7c,sf_h) == header_size /* ~0xe0 + variable */
|
||||
) {
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->num_samples = read_s32le(0x20,sf_h);
|
||||
strwav->sample_rate = read_s32le(0x24,sf_h);
|
||||
/* 0x28: 16 bps */
|
||||
strwav->flags = read_u32le(0x2c,sf_h);
|
||||
strwav->loop_start = read_s32le(0x38,sf_h);
|
||||
strwav->tracks = read_s32le(0x40,sf_h);
|
||||
strwav->loop_end = read_s32le(0x54,sf_h);
|
||||
/* 0x70: sample rate 2 */
|
||||
/* 0x78: table1 entries */
|
||||
/* 0x7c: table1 offset */
|
||||
/* 0xb4: number of 0x800 sectors */
|
||||
|
||||
strwav->codec = PSX;
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x4000 : 0x8000;
|
||||
;VGM_LOG("STR+WAV: header FOP/BB2/ZP/PW3 (PS2)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Pac-Man World 3 (PS2)[2005] */
|
||||
if ((read_u32be(0x04,sf_h) == 0x00000800 ||
|
||||
read_u32be(0x04,sf_h) == 0x01000800) && /* rare, mu_spectral1_explore_2 */
|
||||
read_u32le(0x24,sf_h) == read_u32le(0x70,sf_h) && /* sample rate repeat */
|
||||
read_u32le(0x78,sf_h)*0x04 + read_u32le(0x7c,sf_h) == header_size /* ~0xe0 + variable */
|
||||
) {
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->num_samples = read_s32le(0x20,sf_h);
|
||||
strwav->sample_rate = read_s32le(0x24,sf_h);
|
||||
/* 0x28: 16 bps */
|
||||
strwav->flags = read_u32le(0x2c,sf_h);
|
||||
strwav->loop_start = read_s32le(0x38,sf_h);
|
||||
strwav->tracks = read_s32le(0x40,sf_h);
|
||||
strwav->loop_end = read_s32le(0x54,sf_h);
|
||||
/* 0x70: sample rate 2 */
|
||||
/* 0x78: table1 entries */
|
||||
/* 0x7c: table1 offset */
|
||||
/* 0xb4: number of 0x800 sectors */
|
||||
/* 0xe0: table2 offset */
|
||||
/* 0xe4: table2 entries */
|
||||
|
||||
strwav->codec = PSX;
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x4000 : 0x8000;
|
||||
;VGM_LOG("STR+WAV: header FOP/BB2/ZP/PW3 (PS2)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Zapper: One Wicked Cricket! (PC)[2005] */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000900 &&
|
||||
read_u32le(0x24,sf_h) == read_u32le(0x114,sf_h) && /* sample rate repeat */
|
||||
read_u32le(0x12c,sf_h) == header_size /* ~0x130 */
|
||||
) {
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->num_samples = read_s32le(0x20,sf_h);
|
||||
strwav->sample_rate = read_s32le(0x24,sf_h);
|
||||
/* 0x28: 16 bps */
|
||||
strwav->flags = read_u32le(0x2c,sf_h);
|
||||
strwav->loop_end = read_s32le(0x30,sf_h);
|
||||
/* 0x38: related to samples? */
|
||||
/* 0x54: number of chunks */
|
||||
strwav->loop_start = read_s32le(0x54,sf_h);
|
||||
strwav->tracks = read_s32le(0xF8,sf_h);
|
||||
/* 0x114: sample rate 2 */
|
||||
/* 0x120: number of blocks */
|
||||
/* 0x12c: table1 offset */
|
||||
|
||||
strwav->loop_start = 0; /* ??? */
|
||||
|
||||
strwav->codec = IMA;
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000;
|
||||
;VGM_LOG("STR+WAV: header ZP (PC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Pac-Man World 3 (PC)[2005] */
|
||||
if ((read_u32be(0x04,sf_h) == 0x00000800 ||
|
||||
read_u32be(0x04,sf_h) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */
|
||||
read_u32be(0x04,sf_h) == 0x01000800) && /* rare, mu_spectral1_explore_2 */
|
||||
read_u32le(0x24,sf_h) == read_u32le(0x114,sf_h) && /* sample rate repeat */
|
||||
read_u32le(0x28,sf_h) == 0x10 &&
|
||||
read_u32le(0x130,sf_h) + read_u32le(0x134,sf_h) * 0x40 == header_size /* ~0x140 + cues */
|
||||
) {
|
||||
strwav->num_samples = read_u32le(0x20,sf_h);
|
||||
strwav->sample_rate = read_u32le(0x24,sf_h);
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->num_samples = read_s32le(0x20,sf_h);
|
||||
strwav->sample_rate = read_s32le(0x24,sf_h);
|
||||
/* 0x28: 16 bps */
|
||||
strwav->flags = read_u32le(0x2c,sf_h);
|
||||
strwav->loop_start = read_u32le(0x38,sf_h);
|
||||
strwav->loop_end = read_u32le(0x30,sf_h);
|
||||
|
||||
strwav->channels = read_u32le(0xF8,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
|
||||
strwav->loop_end = read_s32le(0x30,sf_h);
|
||||
strwav->loop_start = read_s32le(0x38,sf_h);
|
||||
/* 0x54: number of chunks */
|
||||
strwav->tracks = read_s32le(0xF8,sf_h);
|
||||
/* 0x114: sample rate 2 */
|
||||
/* 0x120: default hashname? */
|
||||
/* 0x124: number of blocks? */
|
||||
/* 0x128: table1 entries */
|
||||
/* 0x12c: table1 offset */
|
||||
/* 0x130: table2 offset */
|
||||
/* 0x134: table2 entries */
|
||||
|
||||
strwav->codec = IMA;
|
||||
//;VGM_LOG("STR+WAV: Pac-Man World 3 (PC)\n");
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000;
|
||||
;VGM_LOG("STR+WAV: PW3 (PC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -368,224 +520,179 @@ static int parse_header(STREAMFILE* sf_h, strwav_header* strwav) {
|
||||
read_u32le(0x70,sf_h) == 0 && /* sample rate repeat? */
|
||||
header_size == 0x78
|
||||
) {
|
||||
strwav->num_samples = read_u32le(0x5c,sf_h);
|
||||
strwav->sample_rate = read_u32le(0x2c,sf_h);
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
/* 0x28: loop start? */
|
||||
strwav->sample_rate = read_s32le(0x2c,sf_h);
|
||||
/* 0x30: number of 0x800 sectors */
|
||||
strwav->flags = read_u32le(0x34,sf_h);
|
||||
strwav->num_samples = read_s32le(0x5c,sf_h);
|
||||
strwav->tracks = read_s32le(0x60,sf_h);
|
||||
|
||||
strwav->loop_start = 0;
|
||||
strwav->loop_end = 0;
|
||||
|
||||
strwav->channels = read_u32le(0x60,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x8000;
|
||||
strwav->codec = PSX;
|
||||
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x8000;
|
||||
//todo: tracks are stereo blocks of size 0x20000*tracks, containing 4 interleaves of 0x8000:
|
||||
// | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | ...
|
||||
|
||||
strwav->codec = PSX;
|
||||
;VGM_LOG("STR+WAV: header Zapper Beta (PS2)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Zapper: One Wicked Cricket! (PS2)[2005] */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000900 &&
|
||||
read_32bitLE(0x24,sf_h) == read_32bitLE(0x70,sf_h) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitLE(0x7c,sf_h) == header_size /* ~0xD0 */
|
||||
) {
|
||||
strwav->num_samples = read_32bitLE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitLE(0x24,sf_h);
|
||||
strwav->flags = read_32bitLE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitLE(0x38,sf_h);
|
||||
strwav->loop_end = read_32bitLE(0x54,sf_h);
|
||||
|
||||
strwav->channels = read_32bitLE(0x40,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000;
|
||||
|
||||
strwav->codec = PSX;
|
||||
//;VGM_LOG("STR+WAV: header Zapper (PS2)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Zapper: One Wicked Cricket! (GC)[2005] */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000900 &&
|
||||
read_32bitBE(0x24,sf_h) == read_32bitBE(0xB0,sf_h) && /* sample rate repeat */
|
||||
read_32bitBE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitLE(0xc0,sf_h) == header_size /* variable LE size */
|
||||
) {
|
||||
strwav->num_samples = read_32bitBE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x24,sf_h);
|
||||
strwav->flags = read_32bitBE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitBE(0xd8,sf_h);
|
||||
strwav->loop_end = read_32bitBE(0xdc,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x70,sf_h) * read_32bitBE(0x88,sf_h); /* tracks of Nch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
|
||||
|
||||
strwav->dsps_table = 0xe0;
|
||||
strwav->codec = DSP;
|
||||
//;VGM_LOG("STR+WAV: header Zapper (GC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Zapper: One Wicked Cricket! (PC)[2005] */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000900 &&
|
||||
read_32bitLE(0x24,sf_h) == read_32bitLE(0x114,sf_h) && /* sample rate repeat */
|
||||
read_32bitLE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitLE(0x12c,sf_h) == header_size /* ~0x130 */
|
||||
) {
|
||||
strwav->num_samples = read_32bitLE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitLE(0x24,sf_h);
|
||||
strwav->flags = read_32bitLE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitLE(0x54,sf_h);
|
||||
strwav->loop_end = read_32bitLE(0x30,sf_h);
|
||||
|
||||
strwav->channels = read_32bitLE(0xF8,sf_h) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
|
||||
|
||||
strwav->codec = IMA;
|
||||
//;VGM_LOG("STR+WAV: header Zapper (PC)\n");
|
||||
;VGM_LOG("STR+WAV: header ZPb (PS2)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Pac-Man World 3 (GC)[2005] */
|
||||
/* SpongeBob SquarePants: Creature from the Krusty Krab (GC)[2006] */
|
||||
/* SpongeBob SquarePants: Creature from the Krusty Krab (Wii)[2006] */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000800 &&
|
||||
read_32bitBE(0x24,sf_h) == read_32bitBE(0xb0,sf_h) && /* sample rate repeat */
|
||||
read_32bitBE(0x24,sf_h) == read_32bitBE(read_32bitBE(0xf0,sf_h)+0x08,sf_h) && /* sample rate vs 1st DSP header */
|
||||
read_32bitBE(0x28,sf_h) == 0x10 &&
|
||||
read_32bitBE(0xc0,sf_h)*0x04 + read_32bitBE(0xc4,sf_h) == read_32bitBE(0xe0,sf_h) && /* main size */
|
||||
(read_32bitBE(0xe0,sf_h) + read_32bitBE(0xe4,sf_h)*0x40 == header_size || /* main size + extradata 1 (config? PMW3 cs2.wav) */
|
||||
read_32bitBE(0xe0,sf_h) + read_32bitBE(0xe4,sf_h)*0x08 == header_size) /* main size + extradata 2 (ids? SBSP 0_0_mu_hr.wav) */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000800 &&
|
||||
read_u32be(0x24,sf_h) == read_u32be(0xb0,sf_h) && /* sample rate repeat */
|
||||
read_u32be(0x24,sf_h) == read_u32be(read_u32be(0xf0,sf_h)+0x08,sf_h) && /* sample rate vs 1st DSP header */
|
||||
read_u32be(0xc0,sf_h)*0x04 + read_u32be(0xc4,sf_h) == read_u32be(0xe0,sf_h) && /* main size */
|
||||
(read_u32be(0xe0,sf_h) + read_u32be(0xe4,sf_h)*0x40 == header_size || /* main size + extradata 1 (config? PMW3 cs2.wav) */
|
||||
read_u32be(0xe0,sf_h) + read_u32be(0xe4,sf_h)*0x08 == header_size) /* main size + extradata 2 (ids? SBSP 0_0_mu_hr.wav) */
|
||||
) {
|
||||
strwav->num_samples = read_32bitBE(0x20,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x24,sf_h);
|
||||
strwav->flags = read_32bitBE(0x2c,sf_h);
|
||||
strwav->loop_start = read_32bitBE(0xd8,sf_h);
|
||||
strwav->loop_end = read_32bitBE(0xdc,sf_h);
|
||||
strwav->num_samples = read_s32be(0x20,sf_h);
|
||||
strwav->sample_rate = read_s32be(0x24,sf_h);
|
||||
strwav->flags = read_u32be(0x2c,sf_h);
|
||||
strwav->loop_start = read_s32be(0xd8,sf_h);
|
||||
strwav->loop_end = read_s32be(0xdc,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x70,sf_h) * read_32bitBE(0x88,sf_h); /* tracks of Nch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
|
||||
strwav->tracks = read_s32be(0x70,sf_h);
|
||||
|
||||
strwav->dsps_table = 0xf0;
|
||||
strwav->codec = DSP;
|
||||
//;VGM_LOG("STR+WAV: header SpongeBob SquarePants (GC)\n");
|
||||
strwav->dsps_table = 0xf0;
|
||||
strwav->interleave = strwav->tracks > 2 ? 0x8000 : 0x10000;
|
||||
;VGM_LOG("STR+WAV: header SBCKK (GC)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* SpongeBob SquarePants: Creature from the Krusty Krab (PS2)[2006] */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000800 &&
|
||||
read_32bitLE(0x08,sf_h) == 0x00000000 &&
|
||||
read_32bitLE(0x0c,sf_h) != header_size && /* some ID */
|
||||
(header_size == 0x74 + read_32bitLE(0x64,sf_h)*0x04 ||
|
||||
header_size == 0x78 + read_32bitLE(0x64,sf_h)*0x04)
|
||||
/* Sneak King (Xbox)[2006] */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000800 &&
|
||||
read_u32le(0x08,sf_h) == 0x00000000 &&
|
||||
read_u32le(0x0c,sf_h) != header_size &&
|
||||
header_size ==
|
||||
read_u32le(0x40,sf_h) + read_u16le(0x48,sf_h) * 0x04 +
|
||||
read_u16le(0x4a,sf_h) * ((read_u32le(0x3c,sf_h) & 0x200) ? 0x08+0x38 : 0x08)
|
||||
) {
|
||||
strwav->loop_start = read_32bitLE(0x24,sf_h); //not ok?
|
||||
strwav->num_samples = read_32bitLE(0x30,sf_h);
|
||||
strwav->loop_end = read_32bitLE(0x34,sf_h);
|
||||
strwav->sample_rate = read_32bitLE(0x38,sf_h);
|
||||
strwav->flags = read_32bitLE(0x3c,sf_h);
|
||||
/* 0x08: null */
|
||||
/* 0x0c: hashname */
|
||||
strwav->loop_start = read_s32le(0x24,sf_h); //ok?
|
||||
/* 0x28: f32 time in ms */
|
||||
strwav->num_samples = read_s32le(0x30,sf_h);
|
||||
strwav->loop_end = read_s32le(0x34,sf_h);
|
||||
strwav->sample_rate = read_s32le(0x38,sf_h);
|
||||
strwav->flags = read_u32le(0x3c,sf_h);
|
||||
/* 0x40: table1 offset */
|
||||
/* 0x44: table2 offset */
|
||||
/* 0x48: table1 entries */
|
||||
/* 0x4a: table2 entries */
|
||||
/* 0x4c: ? (some low number) */
|
||||
strwav->tracks = read_u8 (0x4e,sf_h);
|
||||
/* 0x4f: 16 bps */
|
||||
/* 0x54: channels per each track? (ex. 2 stereo track: 0x02,0x02) */
|
||||
/* 0x64: channels */
|
||||
/* 0x70+: tables */
|
||||
|
||||
strwav->channels = read_32bitLE(0x64,sf_h); /* tracks of 1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
|
||||
|
||||
strwav->codec = PSX;
|
||||
//;VGM_LOG("STR+WAV: header SpongeBob SquarePants (PS2)\n");
|
||||
/* no codec flags */
|
||||
if (ps_check_format(sf_b, 0x00, 0x100)) {
|
||||
strwav->codec = PSX;
|
||||
strwav->interleave = strwav->tracks > 2 ? 0x4000 : 0x8000;
|
||||
}
|
||||
else {
|
||||
strwav->codec = XBOX;
|
||||
strwav->interleave = strwav->tracks > 1 ? 0xD800/2 : 0xD800; /* assumed for multitrack */
|
||||
}
|
||||
;VGM_LOG("STR+WAV: header SBCKK/SK (PS2)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Tak and the Guardians of Gross (PS2)[2008] */
|
||||
if ( read_32bitBE(0x04,sf_h) == 0x00000800 &&
|
||||
read_32bitLE(0x08,sf_h) != 0x00000000 &&
|
||||
read_32bitLE(0x0c,sf_h) == header_size && /* ~0x80+0x04*ch */
|
||||
read_32bitLE(0x0c,sf_h) == 0x80 + read_32bitLE(0x70,sf_h)*0x04
|
||||
/* SpongeBob's Atlantis SquarePantis (PS2)[2007] */
|
||||
if ( read_u32be(0x04,sf_h) == 0x00000800 &&
|
||||
read_u32le(0x08,sf_h) != 0x00000000 && /* some ID */
|
||||
read_u32le(0x0c,sf_h) == header_size && /* ~0x7c+variable */
|
||||
header_size ==
|
||||
read_u32le(0x40,sf_h) + read_u16le(0x48,sf_h) * 0x04 +
|
||||
read_u16le(0x4a,sf_h) * ((read_u32le(0x3c,sf_h) & 0x200) ? 0x08+0x38 : 0x08)
|
||||
) {
|
||||
strwav->loop_start = read_32bitLE(0x24,sf_h); //not ok?
|
||||
strwav->num_samples = read_32bitLE(0x30,sf_h);
|
||||
strwav->loop_end = read_32bitLE(0x34,sf_h);
|
||||
strwav->sample_rate = read_32bitLE(0x38,sf_h);
|
||||
strwav->flags = read_32bitLE(0x3c,sf_h);
|
||||
strwav->loop_start = read_s32le(0x24,sf_h); //not ok?
|
||||
strwav->num_samples = read_s32le(0x30,sf_h);
|
||||
strwav->loop_end = read_s32le(0x34,sf_h);
|
||||
strwav->sample_rate = read_s32le(0x38,sf_h);
|
||||
strwav->flags = read_u32le(0x3c,sf_h);
|
||||
|
||||
strwav->channels = read_32bitLE(0x70,sf_h); /* tracks of 1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
|
||||
strwav->channels = read_s32le(0x70,sf_h); /* tracks of 1ch */
|
||||
|
||||
strwav->codec = PSX;
|
||||
//;VGM_LOG("STR+WAV: header Tak (PS2)\n");
|
||||
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
|
||||
;VGM_LOG("STR+WAV: header TKGG/SBASP (PS2)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Tak and the Guardians of Gross (Wii)[2008] */
|
||||
/* The House of the Dead: Overkill (Wii)[2009] (not Blitz but still the same format) */
|
||||
/* All Star Karate (Wii)[2010] */
|
||||
if ((read_32bitBE(0x04,sf_h) == 0x00000800 ||
|
||||
read_32bitBE(0x04,sf_h) == 0x00000700) && /* rare? */
|
||||
read_32bitLE(0x08,sf_h) != 0x00000000 &&
|
||||
read_32bitBE(0x0c,sf_h) == header_size && /* variable per header */
|
||||
read_32bitBE(0x7c,sf_h) != 0 && /* has DSP header */
|
||||
read_32bitBE(0x38,sf_h) == read_32bitBE(read_32bitBE(0x7c,sf_h)+0x38,sf_h) /* sample rate vs 1st DSP header */
|
||||
if ((read_u32be(0x04,sf_h) == 0x00000800 ||
|
||||
read_u32be(0x04,sf_h) == 0x00000700) && /* rare? */
|
||||
read_u32be(0x08,sf_h) != 0x00000000 &&
|
||||
read_u32be(0x0c,sf_h) == header_size && /* variable per header */
|
||||
read_u32be(0x7c,sf_h) != 0 && /* has DSP header */
|
||||
read_u32be(0x38,sf_h) == read_u32be(read_u32be(0x7c,sf_h)+0x38,sf_h) /* sample rate vs 1st DSP header */
|
||||
) {
|
||||
strwav->loop_start = 0; //read_32bitLE(0x24,sf_h); //not ok?
|
||||
strwav->num_samples = read_32bitBE(0x30,sf_h);
|
||||
strwav->loop_end = read_32bitBE(0x34,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x38,sf_h);
|
||||
strwav->flags = read_32bitBE(0x3c,sf_h);
|
||||
strwav->loop_start = 0; //read_s32be(0x24,sf_h); //not ok?
|
||||
strwav->num_samples = read_s32be(0x30,sf_h);
|
||||
strwav->loop_end = read_s32be(0x34,sf_h);
|
||||
strwav->sample_rate = read_s32be(0x38,sf_h);
|
||||
strwav->flags = read_u32be(0x3c,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x70,sf_h); /* tracks of 1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
|
||||
strwav->channels = read_s32be(0x70,sf_h); /* tracks of 1ch */
|
||||
|
||||
strwav->coefs_table = 0x7c;
|
||||
strwav->codec = DSP;
|
||||
//;VGM_LOG("STR+WAV: header Tak/HOTD:O (Wii)\n");
|
||||
strwav->coefs_table = 0x7c;
|
||||
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
|
||||
;VGM_LOG("STR+WAV: header TKGG/HOTDO/ASK (Wii)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The House of the Dead: Overkill (PS3)[2009] (not Blitz but still the same format) */
|
||||
if ((read_32bitBE(0x04,sf_h) == 0x00000800 ||
|
||||
read_32bitBE(0x04,sf_h) == 0x00000700) && /* rare? */
|
||||
read_32bitLE(0x08,sf_h) != 0x00000000 &&
|
||||
read_32bitBE(0x0c,sf_h) == header_size && /* variable per header */
|
||||
read_32bitBE(0x7c,sf_h) == 0 /* not DSP header */
|
||||
if ((read_u32be(0x04,sf_h) == 0x00000800 ||
|
||||
read_u32be(0x04,sf_h) == 0x00000700) && /* rare? */
|
||||
read_u32be(0x08,sf_h) != 0x00000000 &&
|
||||
read_u32be(0x0c,sf_h) == header_size && /* variable per header */
|
||||
read_u32be(0x7c,sf_h) == 0 /* not DSP header */
|
||||
) {
|
||||
strwav->loop_start = 0; //read_32bitLE(0x24,sf_h); //not ok?
|
||||
strwav->num_samples = read_32bitBE(0x30,sf_h);
|
||||
strwav->loop_end = read_32bitBE(0x34,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x38,sf_h);
|
||||
strwav->flags = read_32bitBE(0x3c,sf_h);
|
||||
strwav->num_samples = read_s32be(0x30,sf_h);
|
||||
strwav->loop_end = read_s32be(0x34,sf_h);
|
||||
strwav->sample_rate = read_s32be(0x38,sf_h);
|
||||
strwav->flags = read_u32be(0x3c,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x70,sf_h); /* tracks of 1ch */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->channels = read_s32be(0x70,sf_h); /* tracks of 1ch */
|
||||
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
|
||||
|
||||
strwav->codec = PSX;
|
||||
//;VGM_LOG("STR+WAV: header HOTD:O (PS3)\n");
|
||||
;VGM_LOG("STR+WAV: header HOTDO (PS3)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* SpongeBob's Surf & Skate Roadtrip (X360)[2011] */
|
||||
if ((read_32bitBE(0x04,sf_h) == 0x00000800 || /* used? */
|
||||
read_32bitBE(0x04,sf_h) == 0x00000700) &&
|
||||
read_32bitLE(0x08,sf_h) != 0x00000000 &&
|
||||
read_32bitBE(0x0c,sf_h) == 0x124 && /* variable, not sure about final calc */
|
||||
read_32bitBE(0x8c,sf_h) == 0x180 /* encoder delay actually */
|
||||
if ((read_u32be(0x04,sf_h) == 0x00000800 || /* used? */
|
||||
read_u32be(0x04,sf_h) == 0x00000700) &&
|
||||
read_u32be(0x08,sf_h) != 0x00000000 &&
|
||||
read_u32be(0x0c,sf_h) == 0x124 && /* variable, not sure about final calc */
|
||||
read_u32be(0x8c,sf_h) == 0x180 /* encoder delay actually */
|
||||
//0x4c is data_size + 0x210
|
||||
) {
|
||||
strwav->loop_start = 0; //read_32bitLE(0x24,sf_h); //not ok?
|
||||
strwav->num_samples = read_32bitBE(0x30,sf_h);//todo sometimes wrong?
|
||||
strwav->loop_end = read_32bitBE(0x34,sf_h);
|
||||
strwav->sample_rate = read_32bitBE(0x38,sf_h);
|
||||
strwav->flags = read_32bitBE(0x3c,sf_h);
|
||||
strwav->num_samples = read_s32be(0x30,sf_h);//todo sometimes wrong?
|
||||
strwav->loop_end = read_s32be(0x34,sf_h);
|
||||
strwav->sample_rate = read_s32be(0x38,sf_h);
|
||||
strwav->flags = read_u32be(0x3c,sf_h);
|
||||
|
||||
strwav->channels = read_32bitBE(0x70,sf_h); /* multichannel XMA */
|
||||
strwav->loop_flag = strwav->flags & 0x01;
|
||||
strwav->channels = read_s32be(0x70,sf_h); /* multichannel XMA */
|
||||
|
||||
strwav->codec = XMA2;
|
||||
//;VGM_LOG("STR+WAV: header SBSSR (X360)\n");
|
||||
;VGM_LOG("STR+WAV: header SBSSR (X360)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
149
src/meta/txth.c
149
src/meta/txth.c
@ -54,7 +54,7 @@ typedef struct {
|
||||
uint32_t value_sub;
|
||||
|
||||
uint32_t id_value;
|
||||
uint32_t id_offset;
|
||||
uint32_t id_check;
|
||||
|
||||
uint32_t interleave;
|
||||
uint32_t interleave_last;
|
||||
@ -122,6 +122,8 @@ typedef struct {
|
||||
uint32_t name_values[16];
|
||||
int name_values_count;
|
||||
|
||||
int is_multi_txth;
|
||||
|
||||
/* original STREAMFILE and its type (may be an unsupported "base" file or a .txth) */
|
||||
STREAMFILE* sf;
|
||||
int streamfile_is_txth;
|
||||
@ -571,7 +573,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
|
||||
vgmstream->allow_dual_stereo = 1;
|
||||
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream,txth.sf_body,txth.start_offset) )
|
||||
if (!vgmstream_open_stream(vgmstream, txth.sf_body, txth.start_offset))
|
||||
goto fail;
|
||||
|
||||
clean_txth(&txth);
|
||||
@ -750,7 +752,7 @@ static void set_body_chunk(txth_header* txth) {
|
||||
return;
|
||||
|
||||
/* treat chunks as subsongs */
|
||||
if (txth->subsong_count > 1)
|
||||
if (txth->subsong_count > 1 && txth->subsong_count == txth->chunk_count)
|
||||
txth->chunk_number = txth->target_subsong;
|
||||
if (txth->chunk_number == 0)
|
||||
txth->chunk_number = 1;
|
||||
@ -798,6 +800,7 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
|
||||
static int parse_string(STREAMFILE* sf, txth_header* txth, const char* val, char* str);
|
||||
static int parse_coef_table(STREAMFILE* sf, txth_header* txth, const char* val, uint8_t* out_value, size_t out_size);
|
||||
static int parse_name_table(txth_header* txth, char* val);
|
||||
static int parse_multi_txth(txth_header* txth, char* val);
|
||||
static int is_string(const char* val, const char* cmp);
|
||||
static int get_bytes_to_samples(txth_header* txth, uint32_t bytes);
|
||||
static int get_padding_size(txth_header* txth, int discard_empty);
|
||||
@ -944,9 +947,9 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
|
||||
else if (is_string(key,"id_value")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->id_value)) goto fail;
|
||||
}
|
||||
else if (is_string(key,"id_offset")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->id_offset)) goto fail;
|
||||
if (txth->id_value != txth->id_offset) /* evaluate current ID */
|
||||
else if (is_string(key,"id_check") || is_string(key,"id_offset")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->id_check)) goto fail;
|
||||
if (txth->id_value != txth->id_check) /* evaluate current ID */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -1327,6 +1330,11 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
|
||||
if (!parse_name_table(txth,val)) goto fail;
|
||||
}
|
||||
|
||||
/* MULTI TXTH */
|
||||
else if (is_string(key,"multi_txth")) {
|
||||
if (!parse_multi_txth(txth,val)) goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* DEFAULT */
|
||||
else {
|
||||
@ -1511,8 +1519,52 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_name_table_keyval(txth_header* txth, const char* line, char* key, char* val) {
|
||||
int ok;
|
||||
int subsong;
|
||||
|
||||
/* get key/val (ignores lead spaces, stops at space/comment/separator) */
|
||||
//todo names with # and subsongs don't work
|
||||
|
||||
/* ignore comments (that aren't subsongs) */
|
||||
if (line[0] == '#' && strchr(line,':') < 0)
|
||||
return 0;
|
||||
|
||||
/* try "(name): (val))" */
|
||||
ok = sscanf(line, " %[^\t#:] : %[^\t#\r\n] ", key, val);
|
||||
if (ok == 2) {
|
||||
;VGM_LOG("TXTH: name %s get\n", key);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* try "(empty): (val))" */
|
||||
key[0] = '\0';
|
||||
ok = sscanf(line, " : %[^\t#\r\n] ", val);
|
||||
if (ok == 1) {
|
||||
;VGM_LOG("TXTH: default get\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* try "(name)#subsong: (val))" */
|
||||
ok = sscanf(line, " %[^\t#:]#%i : %[^\t#\r\n] ", key, &subsong, val);
|
||||
if (ok == 3 && subsong == txth->target_subsong) {
|
||||
VGM_LOG("TXTH: name %s + subsong %i get\n", key, subsong);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* try "(empty)#subsong: (val))" */
|
||||
key[0] = '\0';
|
||||
ok = sscanf(line, " #%i: %[^\t#\r\n] ", &subsong, val);
|
||||
if (ok == 2 && subsong == txth->target_subsong) {
|
||||
VGM_LOG("TXTH: default + subsong %i get\n", subsong);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_name_table(txth_header* txth, char* name_list) {
|
||||
STREAMFILE* nameFile = NULL;
|
||||
STREAMFILE* sf_names = NULL;
|
||||
off_t txt_offset, file_size;
|
||||
char fullname[PATH_LIMIT];
|
||||
char filename[PATH_LIMIT];
|
||||
@ -1536,8 +1588,8 @@ static int parse_name_table(txth_header* txth, char* name_list) {
|
||||
//;VGM_LOG("TXTH: name_list='%s'\n", name_list);
|
||||
|
||||
/* open companion file near .txth */
|
||||
nameFile = open_streamfile_by_filename(txth->sf_text, name_list);
|
||||
if (!nameFile) goto fail;
|
||||
sf_names = open_streamfile_by_filename(txth->sf_text, name_list);
|
||||
if (!sf_names) goto fail;
|
||||
|
||||
get_streamfile_name(txth->sf_body, fullname, sizeof(filename));
|
||||
get_streamfile_filename(txth->sf_body, filename, sizeof(filename));
|
||||
@ -1545,14 +1597,14 @@ static int parse_name_table(txth_header* txth, char* name_list) {
|
||||
//;VGM_LOG("TXTH: names full=%s, file=%s, base=%s\n", fullname, filename, basename);
|
||||
|
||||
txt_offset = 0x00;
|
||||
file_size = get_streamfile_size(nameFile);
|
||||
file_size = get_streamfile_size(sf_names);
|
||||
|
||||
/* skip BOM if needed */
|
||||
if ((uint16_t)read_16bitLE(0x00, nameFile) == 0xFFFE ||
|
||||
(uint16_t)read_16bitLE(0x00, nameFile) == 0xFEFF) {
|
||||
if ((uint16_t)read_16bitLE(0x00, sf_names) == 0xFFFE ||
|
||||
(uint16_t)read_16bitLE(0x00, sf_names) == 0xFEFF) {
|
||||
txt_offset = 0x02;
|
||||
}
|
||||
else if (((uint32_t)read_32bitBE(0x00, nameFile) & 0xFFFFFF00) == 0xEFBBBF00) {
|
||||
else if (((uint32_t)read_32bitBE(0x00, sf_names) & 0xFFFFFF00) == 0xEFBBBF00) {
|
||||
txt_offset = 0x03;
|
||||
}
|
||||
|
||||
@ -1569,22 +1621,14 @@ static int parse_name_table(txth_header* txth, char* name_list) {
|
||||
while (txt_offset < file_size) {
|
||||
int ok, bytes_read, line_ok;
|
||||
|
||||
bytes_read = read_line(line, sizeof(line), txt_offset, nameFile, &line_ok);
|
||||
bytes_read = read_line(line, sizeof(line), txt_offset, sf_names, &line_ok);
|
||||
if (!line_ok) goto fail;
|
||||
//;VGM_LOG("TXTH: line=%s\n",line);
|
||||
|
||||
txt_offset += bytes_read;
|
||||
|
||||
/* get key/val (ignores lead spaces, stops at space/comment/separator) */
|
||||
ok = sscanf(line, " %[^\t#:] : %[^\t#\r\n] ", key,val);
|
||||
if (ok != 2) { /* ignore line if no key=val (comment or garbage) */
|
||||
/* try again with " (empty): (val)) */
|
||||
key[0] = '\0';
|
||||
ok = sscanf(line, " : %[^\t#\r\n] ", val);
|
||||
if (ok != 1)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!read_name_table_keyval(txth, line, key, val))
|
||||
continue;
|
||||
|
||||
//;VGM_LOG("TXTH: compare name '%s'\n", key);
|
||||
/* parse values if key (name) matches default ("") or filename with/without extension */
|
||||
@ -1619,14 +1663,66 @@ static int parse_name_table(txth_header* txth, char* name_list) {
|
||||
|
||||
/* ignore if name is not actually found (values will return 0) */
|
||||
|
||||
close_streamfile(nameFile);
|
||||
close_streamfile(sf_names);
|
||||
return 1;
|
||||
fail:
|
||||
close_streamfile(nameFile);
|
||||
close_streamfile(sf_names);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int parse_multi_txth(txth_header* txth, char* names) {
|
||||
STREAMFILE* sf_text = NULL;
|
||||
char name[PATH_LIMIT];
|
||||
int n, ok;
|
||||
|
||||
/* temp save */
|
||||
sf_text = txth->sf_text;
|
||||
txth->sf_text = NULL;
|
||||
|
||||
/* to avoid potential infinite recursion plus stack overflows */
|
||||
if (txth->is_multi_txth > 3)
|
||||
goto fail;
|
||||
txth->is_multi_txth++;
|
||||
|
||||
while (names[0] != '\0') {
|
||||
STREAMFILE* sf_test = NULL;
|
||||
int found;
|
||||
|
||||
ok = sscanf(names, " %[^\t#\r\n,]%n ", name, &n);
|
||||
if (ok != 1)
|
||||
goto fail;
|
||||
|
||||
//;VGM_LOG("TXTH: multi name %s\n", name);
|
||||
sf_test = open_streamfile_by_filename(txth->sf, name);
|
||||
if (!sf_test)
|
||||
goto fail;
|
||||
|
||||
/* re-parse with current txth and hope */
|
||||
txth->sf_text = sf_test;
|
||||
found = parse_txth(txth);
|
||||
close_streamfile(sf_test);
|
||||
//todo may need to close header/body streamfiles?
|
||||
|
||||
if (found) {
|
||||
//;VGM_LOG("TXTH: found valid multi txth %s\n", name);
|
||||
break; /* found, otherwise keep trying */
|
||||
}
|
||||
|
||||
names += n;
|
||||
if (names[0] == ',')
|
||||
names++;
|
||||
}
|
||||
|
||||
txth->is_multi_txth--;
|
||||
txth->sf_text = sf_text;
|
||||
return 1;
|
||||
fail:
|
||||
txth->is_multi_txth--;
|
||||
txth->sf_text = sf_text;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_t* out_value) {
|
||||
/* out_value can be these, save before modifying */
|
||||
uint32_t value_mul = txth->value_mul;
|
||||
@ -1734,6 +1830,7 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
|
||||
else if ((n = is_string_field(val,"loop_start"))) value = txth->loop_start_sample;
|
||||
else if ((n = is_string_field(val,"loop_end_sample"))) value = txth->loop_end_sample;
|
||||
else if ((n = is_string_field(val,"loop_end"))) value = txth->loop_end_sample;
|
||||
else if ((n = is_string_field(val,"subsong"))) value = txth->target_subsong;
|
||||
else if ((n = is_string_field(val,"subsong_count"))) value = txth->subsong_count;
|
||||
else if ((n = is_string_field(val,"subsong_spacing"))) value = txth->subsong_spacing;
|
||||
else if ((n = is_string_field(val,"subsong_offset"))) value = txth->subsong_spacing;
|
||||
|
@ -260,17 +260,24 @@ static inline float read_f32be_m(off_t offset, STREAMFILE* sf) {
|
||||
return sample_float;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
|
||||
// on GCC, this reader will be correctly optimized out (as long as it's static/inline), would be same as declaring:
|
||||
// uintXX_t (*read_uXX)(off_t,uint8_t*) = be ? get_uXXbe : get_uXXle;
|
||||
// only for the functions actually used in code, and inlined if possible (like big_endian param being a constant).
|
||||
// on MSVC seems all read_X in sf_reader are compiled and included in the translation unit, plus ignores constants
|
||||
// so may result on bloatness?
|
||||
// (from godbolt tests, test more real cases)
|
||||
|
||||
/* collection of callbacks for quick access */
|
||||
typedef struct sf_reader {
|
||||
int32_t (*read_s32)(off_t,STREAMFILE*); //maybe s32
|
||||
int32_t (*read_s32)(off_t,STREAMFILE*); //maybe r.s32
|
||||
float (*read_f32)(off_t,STREAMFILE*);
|
||||
/* ... */
|
||||
} sf_reader;
|
||||
|
||||
void init_reader(sf_reader *r, int big_endian);
|
||||
/* ... */
|
||||
void sf_reader_init(sf_reader *r, int big_endian) {
|
||||
static inline void sf_reader_init(sf_reader* r, int big_endian) {
|
||||
memset(r, 0, sizeof(sf_reader));
|
||||
if (big_endian) {
|
||||
r->read_s32 = read_s32be;
|
||||
@ -281,6 +288,7 @@ void sf_reader_init(sf_reader *r, int big_endian) {
|
||||
r->read_f32 = read_f32le;
|
||||
}
|
||||
}
|
||||
|
||||
/* sf_reader r;
|
||||
* ...
|
||||
* sf_reader_init(&r, big_endian);
|
||||
@ -326,12 +334,12 @@ static inline /*const*/ int is_id64be(off_t offset, STREAMFILE* sf, const char*
|
||||
static inline int guess_endianness16bit(off_t offset, STREAMFILE* sf) {
|
||||
uint8_t buf[0x02];
|
||||
if (read_streamfile(buf, offset, 0x02, sf) != 0x02) return -1; /* ? */
|
||||
return (uint16_t)get_16bitLE(buf) > (uint16_t)get_16bitBE(buf) ? 1 : 0;
|
||||
return get_u16le(buf) > get_u16be(buf) ? 1 : 0;
|
||||
}
|
||||
static inline int guess_endianness32bit(off_t offset, STREAMFILE* sf) {
|
||||
uint8_t buf[0x04];
|
||||
if (read_streamfile(buf, offset, 0x04, sf) != 0x04) return -1; /* ? */
|
||||
return (uint32_t)get_32bitLE(buf) > (uint32_t)get_32bitBE(buf) ? 1 : 0;
|
||||
return get_u32le(buf) > get_u32be(buf) ? 1 : 0;
|
||||
}
|
||||
|
||||
static inline size_t align_size_to_block(size_t value, size_t block_align) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user