Merge pull request #368 from bnnm/txth-mta-sfh

txth mta sfh
This commit is contained in:
Christopher Snowhill 2019-03-12 00:49:36 -07:00 committed by GitHub
commit 0d7af1b9e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 2209 additions and 776 deletions

View File

@ -403,6 +403,8 @@ int main(int argc, char ** argv) {
strcpy(outfilename_temp, cfg.infilename); strcpy(outfilename_temp, cfg.infilename);
strcat(outfilename_temp, ".wav"); strcat(outfilename_temp, ".wav");
cfg.outfilename = outfilename_temp; cfg.outfilename = outfilename_temp;
/* maybe should avoid overwriting with this auto-name, for the unlikely
* case of file header-body pairs (file.ext+file.ext.wav) */
} }
outfile = fopen(cfg.outfilename,"wb"); outfile = fopen(cfg.outfilename,"wb");
@ -465,7 +467,7 @@ int main(int argc, char ** argv) {
#ifdef VGMSTREAM_MIXING #ifdef VGMSTREAM_MIXING
/* enable after all config but before outbuf */ /* enable after all config but before outbuf */
vgmstream_enable_mixing(vgmstream, BUFFER_SAMPLES, &input_channels, &channels); vgmstream_mixing_enable(vgmstream, BUFFER_SAMPLES, &input_channels, &channels);
#endif #endif
buf = malloc(BUFFER_SAMPLES * sizeof(sample_t) * input_channels); buf = malloc(BUFFER_SAMPLES * sizeof(sample_t) * input_channels);
@ -656,6 +658,8 @@ static size_t make_wav_header(uint8_t * buf, size_t buf_size, int32_t sample_cou
put_32bitLE(buf+0x28, (int32_t)data_size); /* size of WAVE data chunk */ put_32bitLE(buf+0x28, (int32_t)data_size); /* size of WAVE data chunk */
} }
/* could try to add channel_layout, but would need to write WAVEFORMATEXTENSIBLE (maybe only if arg flag?) */
return header_size; return header_size;
fail: fail:
return 0; return 0;

View File

@ -7,7 +7,7 @@ When an unsupported file is loaded (for instance "bgm01.snd"), vgmstream tries t
- .(ext).txth - .(ext).txth
- .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. 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: 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 - 001.1ch.str, 001.1ch.str may use .1ch.txth
@ -19,30 +19,27 @@ For an unsupported bgm01.vag this would be a simple TXTH for it:
``` ```
id_value = 0x534E4420 #test that file starts with "SND " id_value = 0x534E4420 #test that file starts with "SND "
id_offset = @0x00:BE #test is done at offset 0, big endian value id_offset = @0x00:BE #test is done at offset 0, big endian value
codec = PSX codec = PSX #data uses PS-ADPCM
sample_rate = @0x10$2 #get sample rate at offset 0x10, 16 bit value sample_rate = @0x10$2 #get sample rate at offset 0x10, 16 bit value
channels = @0x14 #get number of channels at offset 14 channels = @0x14 #get number of channels at offset 14
interleave = 0x1000 #fixed value interleave = 0x1000 #fixed value
start_offset = 0x100 start_offset = 0x100 #data starts after exactly this value
num_samples = data_size #find automatically number of samples in the file num_samples = data_size #find automatically number of samples in the file
loop_flag = auto loop_flag = auto #find loop points in PS-ADPCM
``` ```
A text file with the above commands must be saved as ".vag.txth" or ".txth", notice it starts with a "." (dot). On Windows files starting with a dot can be created by appending a dot at the end: ".txth." A text file with the above commands must be saved as ".vag.txth" or ".txth", notice it starts with a "." (dot). On Windows files starting with a dot can be created by appending a dot at the end: ".txth."
## Available commands ## 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 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).
``` ```
###################################################### # COMMON VALUES
# The file is made of lines like "key = value" commands describing a header.
# Comments start with #, can be inlined. keys and commands are all case sensitive.
# Spaces are optional: key=value, key = value, and so on are all ok.
# 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).
# Common values:
# - (number): constant number in dec/hex, unsigned (no +10 or -10). # - (number): constant number in dec/hex, unsigned (no +10 or -10).
# Examples: 44100, 40, 0x40 (decimal=64) # Examples: 44100, 40, 0x40 (decimal=64)
# - (offset): format is @(number)[:LE|BE][$1|2|3|4] # - (offset): format is @(number)[:LE|BE][$1|2|3|4]
@ -55,9 +52,12 @@ A text file with the above commands must be saved as ".vag.txth" or ".txth", not
# - start_offset, data_size # - start_offset, data_size
# - num_samples, loop_start_sample, loop_end_sample # - num_samples, loop_start_sample, loop_end_sample
# - subsong_count, subsong_offset # - subsong_count, subsong_offset
# - {string}: other special values for certain keys, described below # - (string): other special values for certain keys, described below
# Those may be combined with math operations (+-*/):
# "field = (number) (op) (offset) (op) (field) (...)"
# Codec used to encode the data [REQUIRED] # CODEC [REQUIRED]
# Sets codec used to encode the data.
# Accepted codec strings: # Accepted codec strings:
# - PSX PlayStation ADPCM # - PSX PlayStation ADPCM
# * For many PS1/PS2/PS3 games # * For many PS1/PS2/PS3 games
@ -101,26 +101,31 @@ A text file with the above commands must be saved as ".vag.txth" or ".txth", not
# - MSADPCM Microsoft ADPCM (mono/stereo) # - MSADPCM Microsoft ADPCM (mono/stereo)
# * For some PC games # * For some PC games
# * Interleave (frame size) varies, often multiple of 0x100 [required] # * Interleave (frame size) varies, often multiple of 0x100 [required]
# - SDX2 Squareroot-delta-exact 8-bit DPCM (3DO games) # - SDX2 Squareroot-delta-exact 8-bit DPCM
# * For many 3DO games # * For many 3DO games
# - MPEG MPEG Audio Layer file (MP1/2/3) # - MPEG MPEG Audio Layer file (MP1/2/3)
# * For some games (usually PC/PS3) # * For some games (usually PC/PS3)
# * May set skip_samples
# - ATRAC3 Sony ATRAC3 # - ATRAC3 Sony ATRAC3
# * For some PS2 and PS3 games # * For some PS2 and PS3 games
# * Interleave (frame size) can be 0x60/0x98/0xC0 * channels [required] # * Interleave (frame size) can be 0x60/0x98/0xC0 * channels [required]
# * Should set skip_samples (more than 1024 but varies)
# - ATRAC3PLUS Sony ATRAC3plus # - ATRAC3PLUS Sony ATRAC3plus
# * For many PSP games and rare PS3 games # * For many PSP games and rare PS3 games
# * Interleave (frame size) can be: [required] # * Interleave (frame size) can be: [required]
# Mono: 0x0118|0178|0230|02E8 # Mono: 0x0118|0178|0230|02E8
# Stereo: 0x0118|0178|0230|02E8|03A8|0460|05D0|0748|0800 # Stereo: 0x0118|0178|0230|02E8|03A8|0460|05D0|0748|0800
# * Should set skip_samples (more than 2048 but varies)
# - XMA1 Microsoft XMA1 # - XMA1 Microsoft XMA1
# * For early X360 games # * For early X360 games
# - XMA2 Microsoft XMA2 # - XMA2 Microsoft XMA2
# * For later X360 games # * For later X360 games
# - FFMPEG Any headered FFmpeg format # - FFMPEG Any headered FFmpeg format
# * For uncommon games # * For uncommon games
# * May set skip_samples
# - AC3 AC3/SPDIF # - AC3 AC3/SPDIF
# * For few PS2 games # * For few PS2 games
# * Should set skip_samples (around 256 but varies)
# - PCFX PC-FX ADPCM # - PCFX PC-FX ADPCM
# * For many PC-FX games # * For many PC-FX games
# * Interleave is multiple of 0x1, often +0x8000 # * Interleave is multiple of 0x1, often +0x8000
@ -131,9 +136,13 @@ A text file with the above commands must be saved as ".vag.txth" or ".txth", not
# * Variation with modified encoding # * Variation with modified encoding
# - OKI16 OKI ADPCM with 16-bit output (not VOX/Dialogic 12-bit) # - OKI16 OKI ADPCM with 16-bit output (not VOX/Dialogic 12-bit)
# * For few PS2 games (Sweet Legacy, Hooligan) # * For few PS2 games (Sweet Legacy, Hooligan)
# - AAC Advanced Audio Coding (raw without .mp4)
# * For some 3DS games and many iOS games
# * Should set skip_samples (around 1024 but varies)
codec = (codec string) codec = (codec string)
# Codec variations [OPTIONAL, depends on codec] # CODEC VARIATIONS [OPTIONAL, depends on codec]
# Accepted values:
# - NGC_DSP: 0=normal interleave, 1=byte interleave, 2=no interleave # - NGC_DSP: 0=normal interleave, 1=byte interleave, 2=no interleave
# - ATRAC3: 0=autodetect joint stereo, 1=force joint stereo, 2=force normal stereo # - ATRAC3: 0=autodetect joint stereo, 1=force joint stereo, 2=force normal stereo
# - XMA1|XMA2: 0=dual multichannel (2ch xN), 1=single multichannel (1ch xN) # - XMA1|XMA2: 0=dual multichannel (2ch xN), 1=single multichannel (1ch xN)
@ -141,55 +150,61 @@ codec = (codec string)
# - PCFX: 0=standard, 1='buggy encoder' mode, 2/3=same as 0/1 but with double volume # - PCFX: 0=standard, 1='buggy encoder' mode, 2/3=same as 0/1 but with double volume
# - PCM4|PCM4_U: 0=low nibble first, 1=high nibble first # - PCM4|PCM4_U: 0=low nibble first, 1=high nibble first
# - others: ignored # - others: ignored
codec_mode = (number) codec_mode = (value)
# Modifies next values [OPTIONAL] # (deprecated) VALUE MODIFIERS [OPTIONAL]
# Values will be "(key) = (number)|(offset)|(field) */+- value_(op)" # Changes next read to: "(key) = (number)|(offset)|(field) */+- value_(op)"
# Useful when a size or such needs adjustments (like given in 0x800 sectors). # Deprecated, should use inline math instead.
# Set to 0 when done using, as it affects ANY value. Priority is as listed. # Set to 0 when done using, as it affects ANY value. Priority is as listed.
value_mul|value_* = (number)|(offset)|(field) value_mul|value_* = (number)|(offset)|(field)
value_div|value_/ = (number)|(offset)|(field) value_div|value_/ = (number)|(offset)|(field)
value_add|value_+ = (number)|(offset)|(field) value_add|value_+ = (number)|(offset)|(field)
value_sub|value_- = (number)|(offset)|(field) value_sub|value_- = (number)|(offset)|(field)
# Interleave or block size [REQUIRED/OPTIONAL, depends on codec] # INTERLEAVE / FRAME SIZE [REQUIRED/OPTIONAL, depends on codec]
# - half_size: sets interleave as data_size / channels
# For mono/interleaved codecs it's the amount of data between channels, # For mono/interleaved codecs it's the amount of data between channels,
# and while optional you'll often need to set it to get proper sound. # and while optional you'll often need to set it to get proper sound.
# For codecs with custom frame sizes (MSADPCM, MS-IMA, ATRAC3/plus) # For codecs with custom frame sizes (MSADPCM, MS-IMA, ATRAC3/plus)
# means frame size and it's required. # means frame size and it's required.
# Interleave 0 means "stereo mode" for codecs marked as "mono/stereo", # Interleave 0 means "stereo mode" for codecs marked as "mono/stereo",
# and setting it will usually force mono-interleaved mode. # and setting it will usually force mono-interleaved mode.
# Special values:
# - half_size: sets interleave as data_size / channels
interleave = (number)|(offset)|(field)|half_size interleave = (number)|(offset)|(field)|half_size
# Interleave in the last block [OPTIONAL] # INTERLEAVE IN THE LAST BLOCK [OPTIONAL]
# - auto: calculate based on channels, interleave and data_size/start_offset
# In some files with interleaved data the last block is smaller than interleave, # In some files with interleaved data the last block is smaller than interleave,
# so interleave must be smaller in the last block. This fixes decoding glitches # so interleave must be smaller in the last block. This fixes decoding glitches
# for those files. Note that this doesn't affect files with padding data in the # for those files. Note that this doesn't affect files with padding data in the
# last block (as the interleave itself is constant). # last block (as the interleave itself is constant).
interleave_last = (number)|(auto) # Special values:
# - auto: calculate based on channels, interleave and data_size/start_offset
interleave_last = (number)|auto
# Validate that id_value matches value at id_offset [OPTIONAL] # ID VALUES [OPTIONAL]
# Validates that id_value (normally set as constant) matches value at id_offset.
# 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_value = (number)|(offset)|(field) id_value = (number)|(offset)|(field)
id_offset = (number)|(offset)|(field) id_offset = (number)|(offset)|(field)
# Number of channels [REQUIRED] # NUMBER OF CHANNELS [REQUIRED]
channels = (number)|(offset)|(field) channels = (number)|(offset)|(field)
# Music frequency in hz [REQUIRED] # MUSIC FREQUENCY [REQUIRED]
sample_rate = (number)|(offset)|(field) sample_rate = (number)|(offset)|(field)
# Data start [OPTIONAL, default to 0] # DATA START [OPTIONAL, default to 0]
start_offset = (number)|(offset)|(field) start_offset = (number)|(offset)|(field)
# Variable that can be used in sample values [OPTIONAL] # DATA SIZE [OPTIONAL]
# Special variable that can be used in sample values.
# Defaults to (file_size - start_offset), re-calculated when start_offset # Defaults to (file_size - start_offset), re-calculated when start_offset
# is set (won't recalculate if data_size is set then start_offset changes). # is set (won't recalculate if data_size is set then start_offset changes).
data_size = (number)|(offset)|(field) data_size = (number)|(offset)|(field)
# Modifies the meaning of sample fields when set *before* them [OPTIONAL, defaults to samples] # SAMPLE MEANINGS [OPTIONAL, defaults to samples]
# Modifies the meaning of sample fields when set *before* them.
# Accepted values:
# - samples: exact sample # - samples: exact sample
# - bytes: automatically converts bytes/offset to samples (applies after */+- modifiers) # - bytes: automatically converts bytes/offset to samples (applies after */+- modifiers)
# - blocks: same as bytes, but value is given in blocks/frames # - blocks: same as bytes, but value is given in blocks/frames
@ -198,30 +213,34 @@ data_size = (number)|(offset)|(field)
# For XMA1/2 bytes does special parsing, with loop values being bit offsets within data. # For XMA1/2 bytes does special parsing, with loop values being bit offsets within data.
sample_type = samples|bytes|blocks sample_type = samples|bytes|blocks
# Various sample values [REQUIRED (num_samples) / OPTIONAL (rest)] # SAMPLE VALUES [REQUIRED (num_samples) / OPTIONAL (rest)]
# Special values:
# - data_size: automatically converts bytes-to-samples # - data_size: automatically converts bytes-to-samples
num_samples = (number)|(offset)|(field)|data_size num_samples = (number)|(offset)|(field)|data_size
loop_start_sample = (number)|(offset)|(field) loop_start_sample = (number)|(offset)|(field)
loop_end_sample = (number)|(offset)|(field)|data_size loop_end_sample = (number)|(offset)|(field)|data_size
# Force loop, on (>0) or off (0), as loop start/end may be defined but not used [OPTIONAL] # LOOP SETTING [OPTIONAL]
# - auto: tries to autodetect loop points for PS-ADPCM data, which may include loop flags. # Force loop, on (>0) or off (0), as loop start/end may be defined but not used.
# Ignores values 0xFFFF/0xFFFFFFFF (-1) as they are often used to disable loops. # Ignores values 0xFFFF/0xFFFFFFFF (-1) as they are often used to disable loops.
# By default it loops when loop_end_sample is defined and less than num_samples. # By default it loops when loop_end_sample is defined and less than num_samples.
# Special values:
# - auto: tries to autodetect loop points for PS-ADPCM data using data loop flags.
loop_flag = (number)|(offset)|(field)|auto loop_flag = (number)|(offset)|(field)|auto
# Loop start/end modifier [OPTIONAL] # LOOP START/END MODIFIER [OPTIONAL]
# For XMA1/2 + sample_type=bytes it means loop subregion, if read after loop values. # For XMA1/2 + sample_type=bytes it means loop subregion, if read after loop values.
# For other codecs its added to loop start/end, if read before loop values # For other codecs its added to loop start/end, if read before loop values
# (a format may rarely have rough loop offset/bytes, then a loop adjust in samples). # (a format may rarely have rough loop offset/bytes, then a loop adjust in samples).
loop_adjust = (number)|(offset)|(field) loop_adjust = (number)|(offset)|(field)
# Beginning samples to skip (encoder delay) [OPTIONAL] # ENCODER DELAY [OPTIONAL]
# Only some codecs use them (ATRAC3/ATRAC3PLUS/XMA/FFMPEG/AC3) # Beginning samples to skip, a.k.a. priming samples or encoder delay.
# Only a few codecs use them (ATRAC3/ATRAC3PLUS/XMA/FFMPEG/AC3/AAC), since
# they need to "warm up" with a number of skip_samples.
skip_samples = (number)|(offset)|(field) skip_samples = (number)|(offset)|(field)
# DSP DECODING COEFFICIENTS [REQUIRED for DSP]
# DSP decoding coefficients [REQUIRED for NGC_DSP]
# These coefs are a list of 8*2 16-bit values per channel, starting from offset. # These coefs are a list of 8*2 16-bit values per channel, starting from offset.
coef_offset = (number)|(offset)|(field) coef_offset = (number)|(offset)|(field)
# Offset separation per channel, usually 0x20 (16 values * 2 bytes) # Offset separation per channel, usually 0x20 (16 values * 2 bytes)
@ -231,9 +250,15 @@ coef_spacing = (number)|(offset)|(field)
coef_endianness = BE|LE|(offset)|(field) coef_endianness = BE|LE|(offset)|(field)
# Split/normal coefs [NOT IMPLEMENTED YET] # Split/normal coefs [NOT IMPLEMENTED YET]
#coef_mode = (number)|(offset) #coef_mode = (number)|(offset)
# Inline coefs, used over those found in coef_offset. Format is a long string
# of bytes (optionally space-separated). It's interpreted like normal coefs
# (byte array), meaning you still need to set coef_spacing and coef_endianness.
# coef_table = 0x1E02DE01 3C0C0EFA ...
coef_table = (string)
# HEADER/BODY SETTINGS [OPTIONAL]
# Change header/body to external files [OPTIONAL] # Changes internal header/body representation to external files.
#
# TXTH commands are done on a "header", and decoding on "body". # TXTH commands are done on a "header", and decoding on "body".
# When loading an unsupported file it becomes the "base" file # When loading an unsupported file it becomes the "base" file
# that loads the .txth, and is both header and body. # that loads the .txth, and is both header and body.
@ -250,7 +275,7 @@ coef_endianness = BE|LE|(offset)|(field)
header_file = (filename)|*.(extension)|null header_file = (filename)|*.(extension)|null
body_file = (filename)|*.(extension)|null body_file = (filename)|*.(extension)|null
# Subsongs [OPTIONAL] # SUBSONGS [OPTIONAL]
# Sets the number of subsongs in the file, adjusting reads per subsong N: # Sets the number of subsongs in the file, adjusting reads per subsong N:
# "value = @(offset) + subsong_offset*N". (number) values aren't adjusted # "value = @(offset) + subsong_offset*N". (number) values aren't adjusted
# as they are seen as constants. # as they are seen as constants.
@ -260,7 +285,7 @@ body_file = (filename)|*.(extension)|null
subsong_count = (number)|(offset)|(field) subsong_count = (number)|(offset)|(field)
subsong_offset = (number)|(offset)|(field) subsong_offset = (number)|(offset)|(field)
# Names [OPTIONAL] # NAMES [OPTIONAL]
# Sets the name of the stream, most useful when used with subsongs. # Sets the name of the stream, most useful when used with subsongs.
# TXTH will read a string at name_offset, with name_size characters. # TXTH will read a string at name_offset, with name_size characters.
# name_size defaults to 0, which reads until null-terminator or a # name_size defaults to 0, which reads until null-terminator or a
@ -269,6 +294,18 @@ subsong_offset = (number)|(offset)|(field)
# adjusted by subsong_offset. # adjusted by subsong_offset.
name_offset = (number)|(offset)|(field) name_offset = (number)|(offset)|(field)
name_size = (number)|(offset)|(field) name_size = (number)|(offset)|(field)
# SUBFILES [OPTIONAL]
# Tells TXTH to parse a full file (ex. .ogg) at subfile_offset, with size
# of subfile_size (defaults to file size - subfile_offset if not set).
# Internal subfile extension can be changed to subfile_extension if needed,
# as vgmstream won't accept unknown extensions (for example if your file
# uses .pogg you may need to set subfile_extension = ogg).
# Setting any of those three will trigger this mode (it's ok to set offset 0).
# Once triggered most fields are ignored, but not all, explained below.
subfile_offset = (number)|(offset)|(field)
subfile_size = (number)|(offset)|(field)
subfile_extension = (string)
``` ```
## Usages ## Usages
@ -290,7 +327,6 @@ sample_type = bytes
num_samples = @0x10 #calculated from channel_size num_samples = @0x10 #calculated from channel_size
channels = 2 #change once calculations are done channels = 2 #change once calculations are done
``` ```
This can be done with value modifiers too (see below).
### Redefining values ### Redefining values
Some commands alter the function of all next commands and can be redefined as needed: Some commands alter the function of all next commands and can be redefined as needed:
@ -331,44 +367,82 @@ sample_rate = 0x04 # sample rate is the same for all subsongs
# Nth subsong ch: 0x04+0x00*N: 0x08 # Nth subsong ch: 0x04+0x00*N: 0x08
``` ```
### Modifiers ### Math
Sometimes header values are in "sectors" or similar concepts (typical in DVD games), and need to be adjusted to a real value. Sometimes header values are in "sectors" or similar concepts (typical in DVD games), and need to be adjusted to a real value.
``` ```
value_multiply = 0x800 # offsets are in DVD sector size sample_type = bytes
start_offset = @0x10 # 0x15*0x800, for example start_offset = @0x10 * 0x800 # 0x15 * DVD sector size, for example
value_multiply = 0 # next values don't need to be multiplied
start_offset = @0x14
``` ```
You can also use certain fields' values: You can also use certain fields' values:
``` ```
value_add = 1 num_samples = @0x10 * channels # byte-to-samples of channel_size
channels = @0x08 # may be 1 + 1 = 2 ```
value_add = 0 `data_size` is a special value for `num_samples` and `loop_end_sample` and will always convert as bytes-to-samples, though.
value_multiply = channels # now set to 2
sample_type = bytes Priority is left-to-right. Do add brackets though, they are accounted for and if they are implemented in the future your .txth *will* break with impunity.
num_samples = @0x10 # channel_size * channels ```
# normal priority
data_size = @0x10 * 0x800 + 0x800
# also works
data_size = (@0x10 + 1) * 0x800
# same as above but don't do this
# (may become @0x10 + (1 * 0x800) in the future
data_size = @0x10 + 1 * 0x800
# doesn't work at the moment, so reorder as (1 * 0x800) + @0x10
data_size = @0x10 + (1 * 0x800)
# fails, wrong bracket count
data_size = (@0x10 + 1 * 0x800
# fails, wrong bracket count
data_size = )@0x10 + 1 * 0x800
```
If a TXTH needs too many calculations it may be better to implement directly in vgmstream though, consider reporting.
### Modifiers
Remnant of simpler math (priority is fixed to */+-), shouldn't be needed anymore.
```
value_multiply = 0x800
start_offset = @0x10
value_multiply = 0
```
```
value_add = 1
channels = @0x08
value_add = 0
value_multiply = channels
sample_type = bytes
num_samples = @0x10
value_multiply = 0 value_multiply = 0
``` ```
num_samples and loop_end_sample will always convert "data_size" field as bytes-to-samples though.
Priority is fixed to */+-:
``` ```
value_add = 0x10 value_add = 0x10
value_mul = 0x800 value_mul = 0x800
start_offset = @0x10 # (0x15*0x800) + 0x10 = 0xA810 start_offset = @0x10
``` ```
But with some creativity you can do fairly involved stuff: ### Subfiles
Sometimes a file is just a wrapper for another common format. In those cases you can tell TXTH to just play the internal format:
``` ```
value_add = 0x10 subfile_offset = 0x20 # tell TXTH to parse a full file (ex. .ogg) at this offset
start_offset = @0x10 # (0x15+0x10) = 0x25 subfile_size = @0x10 # defaults to (file size - subfile_offset) if not set
value_add = 0 subfile_extension = ogg # may be ommited if subfile extension is the same
value_mul = 0x800 # many fields are ignored
start_offset = start_offset # (0x25*0x800) = 0x12800 codec = PCM16LE
value_mul = 0 interleave = 0x1000
channels = 2
# a few fields are applied
sample_rate = @0x08
num_samples = @0x10
loop_start_sample = @0x14
loop_end_sample = @0x18
``` ```
Most fields can't be changed after parsing since doesn't make much sense technically, as the parsed subfile should supply them.
If a TXTH needs too many complex calculations it may be better to implement directly in vgmstream though.

View File

@ -182,7 +182,7 @@ ptp_btl_bgm_voice.sgd#s1#h11050
### Install loops ### Install loops
**`#I(loop start time) (loop end time)`**: force/override looping values, same as .pos but nicer. Loop end is optional and defaults to total samples. **`#I(loop start time) (loop end time)`**: force/override looping values, same as .pos but nicer. Loop end is optional and defaults to total samples.
Time values can be `M:S(.n)` (minutes and seconds), `S.n` (seconds with dot) or `N` (samples). Beware of the subtle difference between 10.0 (10 seconds) and 10 (10 samples). Wrong loop values (for example loop end being much larger than file's samples) will be ignored, but there is some leeway when using seconds for the loop end. Time values can be `M:S(.n)` (minutes and seconds), `S.n` (seconds with dot), `0xN` (samples in hex format) or `N` (samples). Beware of the subtle difference between 10.0 (ten seconds) and 10 (ten samples). Wrong loop values (for example loop end being much larger than file's samples) will be ignored, but there is some leeway when using seconds for the loop end.
**Jewels Ocean (PC)** **Jewels Ocean (PC)**
``` ```

View File

@ -45,8 +45,8 @@ size_t xbox_ima_bytes_to_samples(size_t bytes, int channels);
size_t apple_ima4_bytes_to_samples(size_t bytes, int channels); size_t apple_ima4_bytes_to_samples(size_t bytes, int channels);
/* ngc_dsp_decoder */ /* ngc_dsp_decoder */
void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ngc_dsp_subint(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int interleave); void decode_ngc_dsp_subint(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int interleave);
size_t dsp_bytes_to_samples(size_t bytes, int channels); size_t dsp_bytes_to_samples(size_t bytes, int channels);
int32_t dsp_nibbles_to_samples(int32_t nibbles); int32_t dsp_nibbles_to_samples(int32_t nibbles);
void dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing); void dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing);
@ -80,8 +80,8 @@ 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); size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample);
/* psx_decoder */ /* psx_decoder */
void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags); void decode_psx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags);
void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size);
int ps_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end); int ps_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end);
int ps_find_loop_offsets_full(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end); int ps_find_loop_offsets_full(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end);
size_t ps_bytes_to_samples(size_t bytes, int channels); size_t ps_bytes_to_samples(size_t bytes, int channels);
@ -123,9 +123,9 @@ void free_acm(acm_codec_data *data);
void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do); void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do);
/* msadpcm_decoder */ /* msadpcm_decoder */
void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do); 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 * 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);
void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); 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); long msadpcm_bytes_to_samples(long bytes, int block_size, int channels);
/* yamaha_decoder */ /* yamaha_decoder */
@ -148,10 +148,10 @@ void decode_sassc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing
void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* mtaf_decoder */ /* mtaf_decoder */
void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_mtaf(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
/* mta2_decoder */ /* mta2_decoder */
void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
/* mc3_decoder */ /* mc3_decoder */
void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
@ -175,8 +175,8 @@ void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* oki_decoder */ /* oki_decoder */
void decode_pcfx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode); void decode_pcfx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode);
void decode_oki16(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_oki16(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
size_t oki_bytes_to_samples(size_t bytes, int channels); size_t oki_bytes_to_samples(size_t bytes, int channels);
/* ea_mt_decoder*/ /* ea_mt_decoder*/
@ -353,7 +353,9 @@ int riff_get_fact_skip_samples(STREAMFILE * streamFile, off_t start_offset);
size_t atrac3_bytes_to_samples(size_t bytes, int full_block_align); size_t atrac3_bytes_to_samples(size_t bytes, int full_block_align);
size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align); size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align);
size_t ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels);
size_t aac_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes);
size_t mpeg_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes);
/* An internal struct to pass around and simulate a bitstream. */ /* An internal struct to pass around and simulate a bitstream. */

View File

@ -1006,17 +1006,61 @@ fail:
/* ******************************************** */ /* ******************************************** */
size_t atrac3_bytes_to_samples(size_t bytes, int full_block_align) { size_t atrac3_bytes_to_samples(size_t bytes, int full_block_align) {
if (full_block_align <= 0) return 0;
/* ATRAC3 expects full block align since as is can mix joint stereo with mono blocks; /* ATRAC3 expects full block align since as is can mix joint stereo with mono blocks;
* so (full_block_align / channels) DOESN'T give the size of a single channel (uncommon in ATRAC3 though) */ * so (full_block_align / channels) DOESN'T give the size of a single channel (uncommon in ATRAC3 though) */
return (bytes / full_block_align) * 1024; return (bytes / full_block_align) * 1024;
} }
size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align) { size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align) {
if (full_block_align <= 0) return 0;
/* ATRAC3plus expects full block align since as is can mix joint stereo with mono blocks; /* ATRAC3plus expects full block align since as is can mix joint stereo with mono blocks;
* so (full_block_align / channels) DOESN'T give the size of a single channel (common in ATRAC3plus) */ * so (full_block_align / channels) DOESN'T give the size of a single channel (common in ATRAC3plus) */
return (bytes / full_block_align) * 2048; return (bytes / full_block_align) * 2048;
} }
size_t ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels) {
if (full_block_align <= 0) return 0;
return (bytes / full_block_align) * 256 * channels;
}
size_t aac_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes) {
const int samples_per_frame = 1024; /* theoretically 960 exists in .MP4 so may need a flag */
int frames = 0;
off_t offset = start_offset;
off_t max_offset = start_offset + bytes;
if (!streamFile)
return 0;
if (max_offset > get_streamfile_size(streamFile))
max_offset = get_streamfile_size(streamFile);
/* AAC sometimes comes with an "ADIF" header right before data but probably not in games,
* while standard raw frame headers are called "ADTS" and are similar to MPEG's:
* (see https://wiki.multimedia.cx/index.php/ADTS) */
/* AAC uses VBR so must read all frames */
while (offset < max_offset) {
uint16_t frame_sync = read_u16be(offset+0x00, streamFile);
uint32_t frame_size = read_u32be(offset+0x02, streamFile);
frame_sync = (frame_sync >> 4) & 0x0FFF; /* 12b */
frame_size = (frame_size >> 5) & 0x1FFF; /* 13b */
if (frame_sync != 0xFFF)
break;
if (frame_size <= 0x08)
break;
frames++;
offset += frame_size;
}
return frames * samples_per_frame;
}
/* ******************************************** */ /* ******************************************** */
/* BITSTREAM */ /* BITSTREAM */

View File

@ -1111,11 +1111,13 @@ void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
/* ************************************************************* */ /* ************************************************************* */
size_t ima_bytes_to_samples(size_t bytes, int channels) { size_t ima_bytes_to_samples(size_t bytes, int channels) {
if (channels <= 0) return 0;
/* 2 samples per byte (2 nibbles) in stereo or mono config */ /* 2 samples per byte (2 nibbles) in stereo or mono config */
return bytes * 2 / channels; return bytes * 2 / channels;
} }
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) { size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) {
if (block_align <= 0 || channels <= 0) return 0;
/* MS-IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ /* MS-IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */
return (bytes / block_align) * ((block_align - 0x04*channels) * 2 / channels + 1) return (bytes / block_align) * ((block_align - 0x04*channels) * 2 / channels + 1)
+ ((bytes % block_align) ? (((bytes % block_align) - 0x04*channels) * 2 / channels + 1) : 0); + ((bytes % block_align) ? (((bytes % block_align) - 0x04*channels) * 2 / channels + 1) : 0);
@ -1123,6 +1125,7 @@ size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) {
size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) { size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) {
int block_align = 0x24 * channels; int block_align = 0x24 * channels;
if (channels <= 0) return 0;
/* XBOX IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */ /* XBOX IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */
return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels
+ ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */ + ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */
@ -1130,6 +1133,7 @@ size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) {
size_t apple_ima4_bytes_to_samples(size_t bytes, int channels) { size_t apple_ima4_bytes_to_samples(size_t bytes, int channels) {
int block_align = 0x22 * channels; int block_align = 0x22 * channels;
if (channels <= 0) return 0;
return (bytes / block_align) * (block_align - 0x02*channels) * 2 / channels return (bytes / block_align) * (block_align - 0x02*channels) * 2 / channels
+ ((bytes % block_align) ? ((bytes % block_align) - 0x02*channels) * 2 / channels : 0); + ((bytes % block_align) ? ((bytes % block_align) - 0x02*channels) * 2 / channels : 0);
} }

View File

@ -302,5 +302,64 @@ fail:
return 0; return 0;
} }
size_t mpeg_get_samples(STREAMFILE *streamFile, off_t start_offset, size_t bytes) {
off_t offset = start_offset;
off_t max_offset = start_offset + bytes;
int samples = 0;
mpeg_frame_info info;
size_t prev_size = 0;
int cbr_count = 0;
int is_vbr = 0;
if (!streamFile)
return 0;
if (max_offset > get_streamfile_size(streamFile))
max_offset = get_streamfile_size(streamFile);
/* MPEG may use VBR so must read all frames */
while (offset < max_offset) {
/* skip ID3v2 */
if ((read_32bitBE(offset+0x00, streamFile) & 0xFFFFFF00) == 0x49443300) { /* "ID3\0" */
size_t frame_size = 0;
uint8_t flags = read_8bit(offset+0x05, streamFile);
/* this is how it's officially read :/ */
frame_size += read_8bit(offset+0x06, streamFile) << 21;
frame_size += read_8bit(offset+0x07, streamFile) << 14;
frame_size += read_8bit(offset+0x08, streamFile) << 7;
frame_size += read_8bit(offset+0x09, streamFile) << 0;
frame_size += 0x0a;
if (flags & 0x10) /* footer? */
frame_size += 0x0a;
offset += frame_size;
continue;
}
/* this may fail with unknown ID3 tags */
if (!mpeg_get_frame_info(streamFile, offset, &info))
break;
if (prev_size && prev_size != info.frame_size) {
is_vbr = 1;
}
else if (!is_vbr) {
cbr_count++;
}
if (cbr_count >= 10) {
/* must be CBR, don't bother counting */
samples = (bytes / info.frame_size) * info.frame_samples;
break;
}
offset += info.frame_size;
prev_size = info.frame_size;
samples += info.frame_samples; /* header frames may be 0? */
}
return samples;
}
#endif #endif

View File

@ -19,7 +19,7 @@ static const int msadpcm_coefs[7][2] = {
{ 392, -232 } { 392, -232 }
}; };
void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do) { void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t first_sample, int32_t samples_to_do) {
VGMSTREAMCHANNEL *ch1,*ch2; VGMSTREAMCHANNEL *ch1,*ch2;
STREAMFILE *streamfile; STREAMFILE *streamfile;
int i, frames_in; int i, frames_in;
@ -97,7 +97,7 @@ void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first
} }
} }
void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * 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) {
VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel]; VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel];
int i, frames_in; int i, frames_in;
size_t bytes_per_frame, samples_per_frame; size_t bytes_per_frame, samples_per_frame;
@ -160,7 +160,7 @@ void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int channelspac
/* Cricket Audio's MSADPCM, same thing with reversed hist and nibble order /* Cricket Audio's MSADPCM, same thing with reversed hist and nibble order
* (their tools may convert to float/others but internally it's all PCM16, from debugging). */ * (their tools may convert to float/others but internally it's all PCM16, from debugging). */
void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel]; VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel];
int i, frames_in; int i, frames_in;
size_t bytes_per_frame, samples_per_frame; size_t bytes_per_frame, samples_per_frame;
@ -222,6 +222,7 @@ void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample * outbuf, int channelspacin
} }
long msadpcm_bytes_to_samples(long bytes, int block_size, int channels) { long msadpcm_bytes_to_samples(long bytes, int block_size, int channels) {
if (block_size <= 0 || channels <= 0) return 0;
return (bytes / block_size) * (block_size - (7-1)*channels) * 2 / channels return (bytes / block_size) * (block_size - (7-1)*channels) * 2 / channels
+ ((bytes % block_size) ? ((bytes % block_size) - (7-1)*channels) * 2 / channels : 0); + ((bytes % block_size) ? ((bytes % block_size) - (7-1)*channels) * 2 / channels : 0);
} }

View File

@ -1,7 +1,7 @@
#include "coding.h" #include "coding.h"
#include "../util.h" #include "../util.h"
/* MTA2 (EA XAS variant?) decoder based on: /* MTA2 decoder based on:
* - MGS Developer Wiki: https://www.mgsdevwiki.com/wiki/index.php/MTA2_(Codec) [codec by daemon1] * - MGS Developer Wiki: https://www.mgsdevwiki.com/wiki/index.php/MTA2_(Codec) [codec by daemon1]
* - Solid4 tools: https://github.com/GHzGangster/Drebin * - Solid4 tools: https://github.com/GHzGangster/Drebin
* *
@ -11,23 +11,21 @@
* * up to 16 possible tracks, but max seen is 3 (ex. track0=sneaking, track1=action, track2=ambience) * * up to 16 possible tracks, but max seen is 3 (ex. track0=sneaking, track1=action, track2=ambience)
* - each ch frame is divided into 4 headers + 4 vertical groups with nibbles (0x4*4 + 0x20*4) * - each ch frame is divided into 4 headers + 4 vertical groups with nibbles (0x4*4 + 0x20*4)
* ex. group1 is 0x04(4) + 0x14(4) + 0x24(4) + 0x34(4) ... (vertically maybe for paralelism?) * ex. group1 is 0x04(4) + 0x14(4) + 0x24(4) + 0x34(4) ... (vertically maybe for paralelism?)
* - in case of "macroblock" layout, there are also headers before N tracks (like other MGS games)
* *
* Due to this vertical layout and multiple hist/indexes, it decodes everything in a block between calls * Due to this vertical layout and multiple hist/indexes, it decodes everything in a block between calls
* but discards unwanted data, instead of trying to skip to the target nibble. Meaning no need to save hist, and * but discards unwanted data, instead of trying to skip to the target nibble. Meaning no need to save hist, and
* expects samples_to_do to be block_samples at most (could be simplified, I guess). * expects samples_to_do to be block_samples at most (could be simplified, I guess).
*
* Because of how the macroblock/track and stream's offset per channel work, they are supported by
* autodetecting and skipping when needed (ideally should keep a special layout/count, but this is simpler).
*/ */
static const int c1[8] = { /* mod table 1 */ /* coefs table (extended XA filters) */
static const int mta2_coefs1[8] = {
0, 240, 460, 392, 488, 460, 460, 240 0, 240, 460, 392, 488, 460, 460, 240
}; };
static const int c2[8] = { /* mod table 2 */ static const int mta2_coefs2[8] = {
0, 0, -208, -220, -240, -240, -220, -104 0, 0, -208, -220, -240, -240, -220, -104
}; };
static const int c3[32] = { /* shift table */ /* shift table */
static const int mta2_shifts[32] = {
256, 335, 438, 573, 749, 979, 1281, 1675, 256, 335, 438, 573, 749, 979, 1281, 1675,
2190, 2864, 3746, 4898, 6406, 8377, 10955, 14327, 2190, 2864, 3746, 4898, 6406, 8377, 10955, 14327,
18736, 24503, 32043, 41905, 54802, 71668, 93724, 122568, 18736, 24503, 32043, 41905, 54802, 71668, 93724, 122568,
@ -35,80 +33,31 @@ static const int c3[32] = { /* shift table */
}; };
/* expands nibble */ /* expands nibble */
static short calculate_output(int nibble, short smp1, short smp2, int mod, int sh) { static short mta2_expand_nibble(int nibble, short hist1, short hist2, int coef_index, int shift_index) {
int output; int output;
if (nibble > 7) /* sign extend */ if (nibble > 7) /* sign extend */
nibble = nibble - 16; nibble = nibble - 16;
output = (smp1 * c1[mod] + smp2 * c2[mod] + (nibble * c3[sh]) + 128) >> 8; output = (hist1 * mta2_coefs1[coef_index] + hist2 * mta2_coefs2[coef_index] + (nibble * mta2_shifts[shift_index]) + 128) >> 8;
output = clamp16(output); output = clamp16(output);
return (short)output; return (short)output;
} }
/* decodes a block for a channel */
/* autodetect and skip "macroblocks" */ void decode_mta2(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
static void mta2_block_update(VGMSTREAMCHANNEL * stream) {
int block_type, block_size, block_tracks, repeat = 1;
/* may need to skip N empty blocks */
do {
block_type = read_32bitBE(stream->offset + 0x00, stream->streamfile);
block_size = read_32bitBE(stream->offset + 0x04, stream->streamfile); /* including this header */
/* 0x08: always null */
block_tracks = read_32bitBE(stream->offset + 0x0c, stream->streamfile); /* total tracks of variable size (can be 0) */
/* 0x10001: music, 0x20001: sfx?, 0xf0: loop control (goes at the end) */
if (block_type != 0x00010001 && block_type != 0x00020001 && block_type != 0x000000F0)
return; /* not a block */
/* frame=010001+00/etc can be mistaken as block_type, do extra checks */
{
int i, track_channels = 0;
uint16_t channel_layout = (block_size >> 16);
uint16_t track_size = (block_size & 0xFFFF);
/* has chanel layout == may be a track */
if (channel_layout > 0 && channel_layout <= 0xFF) {
for (i = 0; i < 8; i++) {
if ((channel_layout >> i) & 0x01)
track_channels++;
}
if (track_channels*0x90 == track_size)
return;
}
}
if (block_size <= 0 || block_tracks < 0) { /* nonsense block (maybe at EOF) */
VGM_LOG("MTA2: bad block @ 0x%x\n", (uint32_t)stream->offset);
stream->offset += 0x10;
repeat = 0;
}
else if (block_tracks == 0) { /* empty block (common), keep repeating */
stream->offset += block_size;
}
else { /* normal block, position into next track header */
stream->offset += 0x10;
repeat = 0;
}
} while (repeat);
}
/* decodes a block for a channel, skipping macroblocks/tracks if needed */
void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0; int samples_done = 0, sample_count = 0, channel_block_samples, channel_first_sample, frame_size = 0;
int i, group, row, col; int i, group, row, col;
int track_channels = 0, track_channel; int track_channels = 0, track_channel;
/* block/track skip */ /* track skip */
do { do {
int num_track = 0, channel_layout; int num_track = 0, channel_layout;
/* autodetect and skip macroblock header */
mta2_block_update(stream);
/* parse track header (0x10) and skip tracks that our current channel doesn't belong to */ /* parse track header (0x10) and skip tracks that our current channel doesn't belong to */
num_track = read_8bit(stream->offset+0x00,stream->streamfile); /* 0=first */ num_track = read_8bit(stream->offset+0x00,stream->streamfile); /* 0=first */
/* 0x01(3): num_frame (0=first), 0x04(1): 0? */ /* 0x01(3): num_frame (0=first) */
/* 0x04(1): 0? */
channel_layout = read_8bit(stream->offset+0x05,stream->streamfile); /* bitmask, see mta2.c */ channel_layout = read_8bit(stream->offset+0x05,stream->streamfile); /* bitmask, see mta2.c */
frame_size = read_16bitBE(stream->offset+0x06,stream->streamfile); /* not including this header */ frame_size = read_16bitBE(stream->offset+0x06,stream->streamfile); /* not including this header */
/* 0x08(8): null */ /* 0x08(8): null */
@ -151,22 +100,22 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
/* parse channel frame (header 0x04*4 + data 0x20*4) */ /* parse channel frame (header 0x04*4 + data 0x20*4) */
for (group = 0; group < 4; group++) { for (group = 0; group < 4; group++) {
short smp2, smp1, mod, sh, output; short hist2, hist1, coefs, shift, output;
int group_header = read_32bitBE(stream->offset + 0x10 + track_channel*0x90 + group*0x4, stream->streamfile); int group_header = read_32bitBE(stream->offset + 0x10 + track_channel*0x90 + group*0x4, stream->streamfile);
smp2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */ hist2 = (short) ((group_header >> 16) & 0xfff0); /* upper 16b discarding 4b */
smp1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */ hist1 = (short) ((group_header >> 4) & 0xfff0); /* lower 16b discarding 4b */
mod = (group_header >> 5) & 0x7; /* mid 3b */ coefs = (group_header >> 5) & 0x7; /* mid 3b */
sh = group_header & 0x1f; /* lower 5b */ shift = group_header & 0x1f; /* lower 5b */
/* write header samples (skips the last 2 group nibbles), like Drebin's decoder /* write header samples (skips the last 2 group nibbles), like Drebin's decoder
* last 2 nibbles and next 2 header hist should match though */ * last 2 nibbles and next 2 header hist should match though */
if (sample_count >= channel_first_sample && samples_done < samples_to_do) { if (sample_count >= channel_first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = smp2; outbuf[samples_done * channelspacing] = hist2;
samples_done++; samples_done++;
} }
sample_count++; sample_count++;
if (sample_count >= channel_first_sample && samples_done < samples_to_do) { if (sample_count >= channel_first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = smp1; outbuf[samples_done * channelspacing] = hist1;
samples_done++; samples_done++;
} }
sample_count++; sample_count++;
@ -175,7 +124,7 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
for (col = 0; col < 4*2; col++) { for (col = 0; col < 4*2; col++) {
uint8_t nibbles = read_8bit(stream->offset + 0x10 + 0x10 + track_channel*0x90 + group*0x4 + row*0x10 + col/2, stream->streamfile); uint8_t nibbles = read_8bit(stream->offset + 0x10 + 0x10 + track_channel*0x90 + group*0x4 + row*0x10 + col/2, stream->streamfile);
int nibble_shift = (!(col&1) ? 4 : 0); /* upper first */ int nibble_shift = (!(col&1) ? 4 : 0); /* upper first */
output = calculate_output((nibbles >> nibble_shift) & 0xf, smp1, smp2, mod, sh); output = mta2_expand_nibble((nibbles >> nibble_shift) & 0xf, hist1, hist2, coefs, shift);
/* ignore last 2 nibbles (uses first 2 header samples) */ /* ignore last 2 nibbles (uses first 2 header samples) */
if (row < 7 || col < 3*2) { if (row < 7 || col < 3*2) {
@ -186,8 +135,8 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
sample_count++; sample_count++;
} }
smp2 = smp1; hist2 = hist1;
smp1 = output; hist1 = output;
} }
} }
} }

View File

@ -1,15 +1,11 @@
#include "coding.h" #include "coding.h"
#include "../util.h" #include "../util.h"
#define MTAF_BLOCK_SUPPORT
/* A hybrid of IMA and Yamaha ADPCM found in Metal Gear Solid 3 /* A hybrid of IMA and Yamaha ADPCM found in Metal Gear Solid 3
* Thanks to X_Tra (http://metalgear.in/) for pointing me to the step size table. * Thanks to X_Tra (http://metalgear.in/) for pointing me to the step size table.
* *
* Layout: N tracks of 0x10 header + 0x80*2 (always 2ch; multichannels uses 4ch = 2ch track0 + 2ch track1 xN) * Layout: N tracks of 0x10 header + 0x80*2 (always 2ch; multichannels uses 4ch = 2ch track0 + 2ch track1 xN).
* "macroblocks" support is not really needed as the extractors should remove them but they are
* autodetected and skipped if found (ideally should keep a special layout/count, but this is simpler).
*/ */
static const int index_table[16] = { static const int index_table[16] = {
@ -84,48 +80,8 @@ static const int16_t step_size[32][16] = {
-424, -1273, -2121, -2970, -3819, -4668, -5516, -6365, }, -424, -1273, -2121, -2970, -3819, -4668, -5516, -6365, },
}; };
#ifdef MTAF_BLOCK_SUPPORT
/* autodetect and skip "macroblocks" */
static void mtaf_block_update(VGMSTREAMCHANNEL * stream) {
int block_type, block_size, block_empty, block_tracks, repeat = 1;
do { void decode_mtaf(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
block_type = read_32bitLE(stream->offset+0x00, stream->streamfile);
block_size = read_32bitLE(stream->offset+0x04, stream->streamfile); /* including this header */
block_empty = read_32bitLE(stream->offset+0x08, stream->streamfile); /* always 0 */
block_tracks = read_32bitLE(stream->offset+0x0c, stream->streamfile); /* total tracks of 0x110 (can be 0)*/
/* 0x110001: music (type 0x11=adpcm), 0xf0: loop control (goes at the end) */
if ((block_type != 0x00110001 && block_type != 0x000000F0) || block_empty != 0)
return; /* not a block */
/* track=001100+01 could be mistaken as block_type, do extra checks */
{
int track = read_8bit(stream->offset+0x10, stream->streamfile);
if (track != 0 && track != 1)
return; /* if this is a block, next header should be from track 0/1 */
if (block_tracks > 0 && (block_size-0x10) != block_tracks*0x110)
return; /* wrong expected size */
}
if (block_size <= 0 || block_tracks < 0) { /* nonsense block (maybe at EOF) */
VGM_LOG("MTAF: bad block @ %x\n", (uint32_t)stream->offset);
stream->offset += 0x10;
repeat = 0;
}
else if (block_tracks == 0) { /* empty block (common), keep repeating */
stream->offset += block_size;
}
else { /* normal block, position into next track header */
stream->offset += 0x10;
repeat = 0;
}
} while(repeat);
}
#endif
void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int32_t sample_count; int32_t sample_count;
int i; int i;
int c = channel%2; /* global channel to track channel */ int c = channel%2; /* global channel to track channel */
@ -133,11 +89,6 @@ void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
int32_t step_idx = stream->adpcm_step_index; int32_t step_idx = stream->adpcm_step_index;
#ifdef MTAF_BLOCK_SUPPORT
/* autodetect and skip macroblock header */
mtaf_block_update(stream);
#endif
/* read header when we hit a new track every 0x100 samples */ /* read header when we hit a new track every 0x100 samples */
first_sample = first_sample % 0x100; first_sample = first_sample % 0x100;

View File

@ -1,7 +1,7 @@
#include "coding.h" #include "coding.h"
#include "../util.h" #include "../util.h"
void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i=first_sample; int i=first_sample;
int32_t sample_count; int32_t sample_count;
@ -37,7 +37,7 @@ void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci
} }
/* read from memory rather than a file */ /* read from memory rather than a file */
static void decode_ngc_dsp_subint_internal(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, uint8_t * mem) { static void decode_ngc_dsp_subint_internal(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, uint8_t * mem) {
int i=first_sample; int i=first_sample;
int32_t sample_count; int32_t sample_count;
@ -71,7 +71,7 @@ static void decode_ngc_dsp_subint_internal(VGMSTREAMCHANNEL * stream, sample * o
} }
/* decode DSP with byte-interleaved frames (ex. 0x08: 1122112211221122) */ /* decode DSP with byte-interleaved frames (ex. 0x08: 1122112211221122) */
void decode_ngc_dsp_subint(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int interleave) { void decode_ngc_dsp_subint(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int interleave) {
uint8_t sample_data[0x08]; uint8_t sample_data[0x08];
int i; int i;
@ -100,21 +100,12 @@ int32_t dsp_nibbles_to_samples(int32_t nibbles) {
int32_t whole_frames = nibbles/16; int32_t whole_frames = nibbles/16;
int32_t remainder = nibbles%16; int32_t remainder = nibbles%16;
/*
fprintf(stderr,"%d (%#x) nibbles => %x bytes and %d samples\n",nibbles,nibbles,whole_frames*8,remainder);
*/
#if 0
if (remainder > 0 && remainder < 14)
return whole_frames*14 + remainder;
else if (remainder >= 14)
fprintf(stderr,"***** last frame %d leftover nibbles makes no sense\n",remainder);
#endif
if (remainder>0) return whole_frames*14+remainder-2; if (remainder>0) return whole_frames*14+remainder-2;
else return whole_frames*14; else return whole_frames*14;
} }
size_t dsp_bytes_to_samples(size_t bytes, int channels) { size_t dsp_bytes_to_samples(size_t bytes, int channels) {
if (channels <= 0) return 0;
return bytes / channels / 8 * 14; return bytes / channels / 8 * 14;
} }

View File

@ -87,7 +87,7 @@ static void oki16_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, in
* so it's needs GENH/TXTH. Sample rate can only be base_value divided by 1/2/3/4, where * so it's needs GENH/TXTH. Sample rate can only be base_value divided by 1/2/3/4, where
* base_value is approximately ~31468.5 (follows hardware clocks), mono or interleaved for stereo. * base_value is approximately ~31468.5 (follows hardware clocks), mono or interleaved for stereo.
*/ */
void decode_pcfx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode) { void decode_pcfx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode) {
int i, sample_count = 0; int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
@ -108,7 +108,7 @@ void decode_pcfx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
/* OKI variation with 16-bit output (vs standard's 12-bit), found in FrontWing's PS2 games (Sweet Legacy, Hooligan). /* OKI variation with 16-bit output (vs standard's 12-bit), found in FrontWing's PS2 games (Sweet Legacy, Hooligan).
* Reverse engineered from the ELF with help from the folks at hcs. */ * Reverse engineered from the ELF with help from the folks at hcs. */
void decode_oki16(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { void decode_oki16(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0; int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
@ -140,6 +140,7 @@ void decode_oki16(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing
size_t oki_bytes_to_samples(size_t bytes, int channels) { size_t oki_bytes_to_samples(size_t bytes, int channels) {
if (channels <= 0) return 0;
/* 2 samples per byte (2 nibbles) in stereo or mono config */ /* 2 samples per byte (2 nibbles) in stereo or mono config */
return bytes * 2 / channels; return bytes * 2 / channels;
} }

View File

@ -221,5 +221,6 @@ 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) { size_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; return ((int64_t)bytes * 8) / channels / bits_per_sample;
} }

View File

@ -43,7 +43,7 @@ static const int ps_adpcm_coefs_i[5][2] = {
*/ */
/* standard PS-ADPCM (float math version) */ /* standard PS-ADPCM (float math version) */
void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags) { void decode_psx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags) {
off_t frame_offset; off_t frame_offset;
int i, frames_in, sample_count = 0; int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame; size_t bytes_per_frame, samples_per_frame;
@ -75,24 +75,24 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
/* decode nibbles */ /* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) { for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t new_sample = 0; int32_t sample = 0;
if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */ if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x02+i/2,stream->streamfile); uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x02+i/2,stream->streamfile);
new_sample = i&1 ? /* low nibble first */ sample = i&1 ? /* low nibble first */
(nibbles >> 4) & 0x0f : (nibbles >> 4) & 0x0f :
(nibbles >> 0) & 0x0f; (nibbles >> 0) & 0x0f;
new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ sample = (int16_t)((sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
new_sample = (int)(new_sample + ps_adpcm_coefs_f[coef_index][0]*hist1 + ps_adpcm_coefs_f[coef_index][1]*hist2); sample = (int)(sample + ps_adpcm_coefs_f[coef_index][0]*hist1 + ps_adpcm_coefs_f[coef_index][1]*hist2);
new_sample = clamp16(new_sample); sample = clamp16(sample);
} }
outbuf[sample_count] = new_sample; outbuf[sample_count] = sample;
sample_count += channelspacing; sample_count += channelspacing;
hist2 = hist1; hist2 = hist1;
hist1 = new_sample; hist1 = sample;
} }
stream->adpcm_history1_32 = hist1; stream->adpcm_history1_32 = hist1;
@ -104,7 +104,7 @@ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
* Found in some PC/PS3 games (FF XI in sizes 3/5/9/41, Afrika in size 4, Blur/James Bond in size 33, etc). * Found in some PC/PS3 games (FF XI in sizes 3/5/9/41, Afrika in size 4, Blur/James Bond in size 33, etc).
* *
* Uses int math to decode, which seems more likely (based on FF XI PC's code in Moogle Toolbox). */ * Uses int math to decode, which seems more likely (based on FF XI PC's code in Moogle Toolbox). */
void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size) { void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size) {
off_t frame_offset; off_t frame_offset;
int i, frames_in, sample_count = 0; int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame; size_t bytes_per_frame, samples_per_frame;
@ -131,21 +131,21 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int cha
/* decode nibbles */ /* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) { for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t new_sample = 0; int32_t sample = 0;
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01+i/2,stream->streamfile); uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x01+i/2,stream->streamfile);
new_sample = i&1 ? /* low nibble first */ sample = i&1 ? /* low nibble first */
(nibbles >> 4) & 0x0f : (nibbles >> 4) & 0x0f :
(nibbles >> 0) & 0x0f; (nibbles >> 0) & 0x0f;
new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ sample = (int16_t)((sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
new_sample = new_sample + ((ps_adpcm_coefs_i[coef_index][0]*hist1 + ps_adpcm_coefs_i[coef_index][1]*hist2) >> 6); sample = sample + ((ps_adpcm_coefs_i[coef_index][0]*hist1 + ps_adpcm_coefs_i[coef_index][1]*hist2) >> 6);
new_sample = clamp16(new_sample); sample = clamp16(sample);
outbuf[sample_count] = new_sample; outbuf[sample_count] = sample;
sample_count += channelspacing; sample_count += channelspacing;
hist2 = hist1; hist2 = hist1;
hist1 = new_sample; hist1 = sample;
} }
stream->adpcm_history1_32 = hist1; stream->adpcm_history1_32 = hist1;
@ -269,6 +269,7 @@ int ps_find_loop_offsets_full(STREAMFILE *streamFile, off_t start_offset, size_t
} }
size_t ps_bytes_to_samples(size_t bytes, int channels) { size_t ps_bytes_to_samples(size_t bytes, int channels) {
if (channels <= 0) return 0;
return bytes / channels / 0x10 * 28; return bytes / channels / 0x10 * 28;
} }

View File

@ -151,13 +151,14 @@ void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin
} }
size_t yamaha_bytes_to_samples(size_t bytes, int channels) { size_t yamaha_bytes_to_samples(size_t bytes, int channels) {
if (channels <= 0) return 0;
/* 2 samples per byte (2 nibbles) in stereo or mono config */ /* 2 samples per byte (2 nibbles) in stereo or mono config */
return bytes * 2 / channels; return bytes * 2 / channels;
} }
size_t aska_bytes_to_samples(size_t bytes, int channels) { size_t aska_bytes_to_samples(size_t bytes, int channels) {
int block_align = 0x40; int block_align = 0x40;
if (channels <= 0) return 0;
return (bytes / block_align) * (block_align - 0x04*channels) * 2 / channels return (bytes / block_align) * (block_align - 0x04*channels) * 2 / channels
+ ((bytes % block_align) ? ((bytes % block_align) - 0x04*channels) * 2 / channels : 0); + ((bytes % block_align) ? ((bytes % block_align) - 0x04*channels) * 2 / channels : 0);
} }

View File

@ -184,6 +184,7 @@ static const char* extension_list[] = {
"idx", "idx",
"ikm", "ikm",
"ild", "ild",
"ilv", //txth/reserved [Star Wars Episode III (PS2)]
"imc", "imc",
"int", "int",
"isd", "isd",
@ -457,6 +458,7 @@ static const char* extension_list[] = {
"vis", "vis",
"vms", "vms",
"voi", "voi",
"vp6",
"vpk", "vpk",
"vs", "vs",
"vsf", "vsf",
@ -762,7 +764,7 @@ static const meta_info meta_info_list[] = {
{meta_DSP_CSTR, "Namco Cstr header"}, {meta_DSP_CSTR, "Namco Cstr header"},
{meta_GCSW, "GCSW header"}, {meta_GCSW, "GCSW header"},
{meta_PS2_SShd, "Sony ADS header"}, {meta_PS2_SShd, "Sony ADS header"},
{meta_PS2_NPSF, "Namco Production Sound File (NPSF) header"}, {meta_NPS, "Namco NPSF header"},
{meta_RWSD, "Nintendo RWSD header (single stream)"}, {meta_RWSD, "Nintendo RWSD header (single stream)"},
{meta_RWAR, "Nintendo RWAR header (single RWAV stream)"}, {meta_RWAR, "Nintendo RWAR header (single RWAV stream)"},
{meta_RWAV, "Nintendo RWAV header"}, {meta_RWAV, "Nintendo RWAV header"},
@ -1009,7 +1011,7 @@ static const meta_info meta_info_list[] = {
{meta_SQEX_SCD, "Square-Enix SCD header"}, {meta_SQEX_SCD, "Square-Enix SCD header"},
{meta_NGC_NST_DSP, "Animaniacs NST header"}, {meta_NGC_NST_DSP, "Animaniacs NST header"},
{meta_BAF, "Bizarre Creations .baf header"}, {meta_BAF, "Bizarre Creations .baf header"},
{meta_PS3_MSF, "Sony MSF header"}, {meta_MSF, "Sony MSF header"},
{meta_NUB_VAG, "Namco NUB VAG header"}, {meta_NUB_VAG, "Namco NUB VAG header"},
{meta_PS3_PAST, "SNDP header"}, {meta_PS3_PAST, "SNDP header"},
{meta_SGXD, "Sony SGXD header"}, {meta_SGXD, "Sony SGXD header"},
@ -1030,7 +1032,7 @@ static const meta_info meta_info_list[] = {
{meta_OTNS_ADP, "Omikron: The Nomad Soul ADP header"}, {meta_OTNS_ADP, "Omikron: The Nomad Soul ADP header"},
{meta_EB_SFX, "Excitebots .sfx header"}, {meta_EB_SFX, "Excitebots .sfx header"},
{meta_EB_SF0, "assumed Excitebots .sf0 by extension"}, {meta_EB_SF0, "assumed Excitebots .sf0 by extension"},
{meta_PS2_MTAF, "Konami MTAF header"}, {meta_MTAF, "Konami MTAF header"},
{meta_PS2_VAG1, "Konami VAG1 header"}, {meta_PS2_VAG1, "Konami VAG1 header"},
{meta_PS2_VAG2, "Konami VAG2 header"}, {meta_PS2_VAG2, "Konami VAG2 header"},
{meta_TUN, "Lego Racers ALP header"}, {meta_TUN, "Lego Racers ALP header"},
@ -1071,7 +1073,7 @@ static const meta_info meta_info_list[] = {
{meta_TA_AAC_X360, "tri-Ace AAC (X360) header"}, {meta_TA_AAC_X360, "tri-Ace AAC (X360) header"},
{meta_TA_AAC_PS3, "tri-Ace AAC (PS3) header"}, {meta_TA_AAC_PS3, "tri-Ace AAC (PS3) header"},
{meta_TA_AAC_MOBILE, "tri-Ace AAC (Mobile) header"}, {meta_TA_AAC_MOBILE, "tri-Ace AAC (Mobile) header"},
{meta_PS3_MTA2, "Konami MTA2 header"}, {meta_MTA2, "Konami MTA2 header"},
{meta_NGC_ULW, "Criterion ULW raw header"}, {meta_NGC_ULW, "Criterion ULW raw header"},
{meta_PC_XA30, "Reflections XA30 PC header"}, {meta_PC_XA30, "Reflections XA30 PC header"},
{meta_WII_04SW, "Reflections 04SW header"}, {meta_WII_04SW, "Reflections 04SW header"},
@ -1172,6 +1174,9 @@ static const meta_info meta_info_list[] = {
{meta_208, "Ocean .208 header"}, {meta_208, "Ocean .208 header"},
{meta_DSP_DS2, "LucasArts .DS2 header"}, {meta_DSP_DS2, "LucasArts .DS2 header"},
{meta_MUS_VC, "Vicious Cycle .MUS header"}, {meta_MUS_VC, "Vicious Cycle .MUS header"},
{meta_STRM_ABYLIGHT, "Abylight STRM header"},
{meta_MSF_KONAMI, "Konami MSF header"},
{meta_XWMA_KONAMI, "Konami XWMA header"},
}; };

View File

@ -10,6 +10,10 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
size_t block_size, block_samples; size_t block_size, block_samples;
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
uint32_t flag_lang = (vgmstream->codec_config >> 16) & 0xFFFF;
int flag_be = (vgmstream->codec_config & 0x02);
int flag_adpcm = (vgmstream->codec_config & 0x01);
/* EOF reads: signal we have nothing and let the layout fail */ /* EOF reads: signal we have nothing and let the layout fail */
if (block_offset >= get_streamfile_size(streamFile)) { if (block_offset >= get_streamfile_size(streamFile)) {
@ -23,35 +27,21 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
{ {
uint32_t block_id = read_32bitBE(block_offset+0x00,streamFile); uint32_t block_id = read_32bitBE(block_offset+0x00,streamFile);
if (vgmstream->codec_config & 0x02) /* size is always LE, except in early SS/MAC */ if (flag_be) /* size is always LE, except in early SS/MAC */
block_size = read_32bitBE(block_offset + 0x04,streamFile); block_size = read_32bitBE(block_offset + 0x04,streamFile);
else else
block_size = read_32bitLE(block_offset + 0x04,streamFile); block_size = read_32bitLE(block_offset + 0x04,streamFile);
switch(block_id) { if (block_id == 0x5343446C || block_id == (0x53440000 | flag_lang)) {
case 0x5343446C: /* "SCDl" */ /* "SCDl" or "SDxx" audio chunk */
case 0x5344454E: /* "SDEN" */ if (vgmstream->coding_type == coding_PSX)
case 0x53444652: /* "SDFR" */ block_samples = ps_bytes_to_samples(block_size-0x10, vgmstream->channels);
case 0x53444745: /* "SDGE" */ else
case 0x53444445: /* "SDDE" */ block_samples = read_32bit(block_offset+0x08,streamFile);
case 0x53444954: /* "SDIT" */ }
case 0x53445350: /* "SDSP" */ else {
case 0x53444553: /* "SDES" */ /* ignore other chunks (audio "SCHl/SCCl/...", non-target lang, video "pIQT/MADk/...", etc) */
case 0x53444D58: /* "SDMX" */ block_samples = 0; /* layout ignores this */
case 0x53445255: /* "SDRU" */
case 0x53444A41: /* "SDJA" */
case 0x53444A50: /* "SDJP" */
case 0x5344504C: /* "SDPL" */
/* audio chunk */
if (vgmstream->coding_type == coding_PSX)
block_samples = ps_bytes_to_samples(block_size-0x10, vgmstream->channels);
else
block_samples = read_32bit(block_offset+0x08,streamFile);
break;
default:
/* ignore other chunks (audio "SCHl/SCCl/...", video "pIQT/MADk/...", etc) */
block_samples = 0; /* layout ignores this */
break;
} }
/* "SCHl" start block (movie "SHxx" shouldn't use multi files) */ /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */
@ -185,7 +175,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
} }
/* read ADPCM history before each channel if needed (not actually read in sx.exe) */ /* read ADPCM history before each channel if needed (not actually read in sx.exe) */
if (vgmstream->codec_config & 0x01) { if (flag_adpcm) {
for (i = 0; i < vgmstream->channels; i++) { for (i = 0; i < vgmstream->channels; i++) {
//vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile);
//vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile);

View File

@ -256,6 +256,10 @@
RelativePath=".\meta\kma9_streamfile.h" RelativePath=".\meta\kma9_streamfile.h"
> >
</File> </File>
<File
RelativePath=".\meta\mta2_streamfile.h"
>
</File>
<File <File
RelativePath=".\meta\opus_interleave_streamfile.h" RelativePath=".\meta\opus_interleave_streamfile.h"
> >
@ -268,6 +272,10 @@
RelativePath=".\meta\vsv_streamfile.h" RelativePath=".\meta\vsv_streamfile.h"
> >
</File> </File>
<File
RelativePath=".\meta\sfh_streamfile.h"
>
</File>
<File <File
RelativePath=".\meta\sqex_scd_streamfile.h" RelativePath=".\meta\sqex_scd_streamfile.h"
> >
@ -288,6 +296,10 @@
RelativePath=".\meta\xvag_streamfile.h" RelativePath=".\meta\xvag_streamfile.h"
> >
</File> </File>
<File
RelativePath=".\meta\xwma_konami_streamfile.h"
>
</File>
<File <File
RelativePath=".\meta\zsnd_streamfile.h" RelativePath=".\meta\zsnd_streamfile.h"
> >
@ -1122,6 +1134,10 @@
RelativePath=".\meta\msf_banpresto.c" RelativePath=".\meta\msf_banpresto.c"
> >
</File> </File>
<File
RelativePath=".\meta\msf_konami.c"
>
</File>
<File <File
RelativePath=".\meta\msf_tamasoft.c" RelativePath=".\meta\msf_tamasoft.c"
> >
@ -1131,11 +1147,11 @@
> >
</File> </File>
<File <File
RelativePath=".\meta\ps2_mtaf.c" RelativePath=".\meta\mtaf.c"
> >
</File> </File>
<File <File
RelativePath=".\meta\ps2_npsf.c" RelativePath=".\meta\nps.c"
> >
</File> </File>
<File <File
@ -1283,11 +1299,11 @@
> >
</File> </File>
<File <File
RelativePath=".\meta\ps3_msf.c" RelativePath=".\meta\msf.c"
> >
</File> </File>
<File <File
RelativePath=".\meta\ps3_mta2.c" RelativePath=".\meta\mta2.c"
> >
</File> </File>
<File <File
@ -1390,10 +1406,14 @@
RelativePath=".\meta\sdt.c" RelativePath=".\meta\sdt.c"
> >
</File> </File>
<File <File
RelativePath=".\meta\seg.c" RelativePath=".\meta\seg.c"
> >
</File> </File>
<File
RelativePath=".\meta\sfh.c"
>
</File>
<File <File
RelativePath=".\meta\sfl.c" RelativePath=".\meta\sfl.c"
> >
@ -1457,6 +1477,10 @@
<File <File
RelativePath=".\meta\str_wav.c" RelativePath=".\meta\str_wav.c"
> >
</File>
<File
RelativePath=".\meta\strm_abylight.c"
>
</File> </File>
<File <File
RelativePath=".\meta\stx.c" RelativePath=".\meta\stx.c"
@ -1689,6 +1713,10 @@
<File <File
RelativePath=".\meta\xwma.c" RelativePath=".\meta\xwma.c"
> >
</File>
<File
RelativePath=".\meta\xwma_konami.c"
>
</File> </File>
<File <File
RelativePath=".\meta\ydsp.c" RelativePath=".\meta\ydsp.c"

View File

@ -90,6 +90,7 @@
<ClInclude Include="coding\vorbis_custom_data_wwise.h" /> <ClInclude Include="coding\vorbis_custom_data_wwise.h" />
<ClInclude Include="coding\vorbis_custom_decoder.h" /> <ClInclude Include="coding\vorbis_custom_decoder.h" />
<ClInclude Include="meta\xvag_streamfile.h" /> <ClInclude Include="meta\xvag_streamfile.h" />
<ClInclude Include="meta\xwma_konami_streamfile.h" />
<ClInclude Include="meta\zsnd_streamfile.h" /> <ClInclude Include="meta\zsnd_streamfile.h" />
<ClInclude Include="plugins.h" /> <ClInclude Include="plugins.h" />
<ClInclude Include="streamfile.h" /> <ClInclude Include="streamfile.h" />
@ -108,7 +109,9 @@
<ClInclude Include="meta\kma9_streamfile.h" /> <ClInclude Include="meta\kma9_streamfile.h" />
<ClInclude Include="meta\ppst_streamfile.h" /> <ClInclude Include="meta\ppst_streamfile.h" />
<ClInclude Include="meta\vsv_streamfile.h" /> <ClInclude Include="meta\vsv_streamfile.h" />
<ClInclude Include="meta\mta2_streamfile.h" />
<ClInclude Include="meta\opus_interleave_streamfile.h" /> <ClInclude Include="meta\opus_interleave_streamfile.h" />
<ClInclude Include="meta\sfh_streamfile.h" />
<ClInclude Include="meta\sqex_scd_streamfile.h" /> <ClInclude Include="meta\sqex_scd_streamfile.h" />
<ClInclude Include="meta\ubi_bao_streamfile.h" /> <ClInclude Include="meta\ubi_bao_streamfile.h" />
<ClInclude Include="meta\ubi_sb_streamfile.h" /> <ClInclude Include="meta\ubi_sb_streamfile.h" />
@ -161,6 +164,7 @@
<ClCompile Include="meta\mp4.c" /> <ClCompile Include="meta\mp4.c" />
<ClCompile Include="meta\msb_msh.c" /> <ClCompile Include="meta\msb_msh.c" />
<ClCompile Include="meta\msf_banpresto.c" /> <ClCompile Include="meta\msf_banpresto.c" />
<ClCompile Include="meta\msf_konami.c" />
<ClCompile Include="meta\msf_tamasoft.c" /> <ClCompile Include="meta\msf_tamasoft.c" />
<ClCompile Include="meta\ngca.c" /> <ClCompile Include="meta\ngca.c" />
<ClCompile Include="meta\opus.c" /> <ClCompile Include="meta\opus.c" />
@ -173,7 +177,7 @@
<ClCompile Include="meta\ps2_hsf.c" /> <ClCompile Include="meta\ps2_hsf.c" />
<ClCompile Include="meta\ps2_iab.c" /> <ClCompile Include="meta\ps2_iab.c" />
<ClCompile Include="meta\mss.c" /> <ClCompile Include="meta\mss.c" />
<ClCompile Include="meta\ps2_mtaf.c" /> <ClCompile Include="meta\mtaf.c" />
<ClCompile Include="meta\ps2_spm.c" /> <ClCompile Include="meta\ps2_spm.c" />
<ClCompile Include="meta\vs_str.c" /> <ClCompile Include="meta\vs_str.c" />
<ClCompile Include="meta\ps2_wmus.c" /> <ClCompile Include="meta\ps2_wmus.c" />
@ -183,6 +187,7 @@
<ClCompile Include="meta\sk_aud.c" /> <ClCompile Include="meta\sk_aud.c" />
<ClCompile Include="meta\vawx.c" /> <ClCompile Include="meta\vawx.c" />
<ClCompile Include="meta\seg.c" /> <ClCompile Include="meta\seg.c" />
<ClCompile Include="meta\sfh.c" />
<ClCompile Include="meta\sqex_scd.c" /> <ClCompile Include="meta\sqex_scd.c" />
<ClCompile Include="meta\sqex_scd_sscf.c" /> <ClCompile Include="meta\sqex_scd_sscf.c" />
<ClCompile Include="meta\sqex_sead.c" /> <ClCompile Include="meta\sqex_sead.c" />
@ -379,7 +384,7 @@
<ClCompile Include="meta\ps2_mic.c" /> <ClCompile Include="meta\ps2_mic.c" />
<ClCompile Include="meta\ps2_mihb.c" /> <ClCompile Include="meta\ps2_mihb.c" />
<ClCompile Include="meta\ps2_msa.c" /> <ClCompile Include="meta\ps2_msa.c" />
<ClCompile Include="meta\ps2_npsf.c" /> <ClCompile Include="meta\nps.c" />
<ClCompile Include="meta\ps2_p2bt.c" /> <ClCompile Include="meta\ps2_p2bt.c" />
<ClCompile Include="meta\ps2_pcm.c" /> <ClCompile Include="meta\ps2_pcm.c" />
<ClCompile Include="meta\ps2_pnb.c" /> <ClCompile Include="meta\ps2_pnb.c" />
@ -412,8 +417,8 @@
<ClCompile Include="meta\ps2_xa2_rrp.c" /> <ClCompile Include="meta\ps2_xa2_rrp.c" />
<ClCompile Include="meta\ps2_xa30.c" /> <ClCompile Include="meta\ps2_xa30.c" />
<ClCompile Include="meta\ps3_cps.c" /> <ClCompile Include="meta\ps3_cps.c" />
<ClCompile Include="meta\ps3_msf.c" /> <ClCompile Include="meta\msf.c" />
<ClCompile Include="meta\ps3_mta2.c" /> <ClCompile Include="meta\mta2.c" />
<ClCompile Include="meta\xa.c" /> <ClCompile Include="meta\xa.c" />
<ClCompile Include="meta\fag.c" /> <ClCompile Include="meta\fag.c" />
<ClCompile Include="meta\ffdl.c" /> <ClCompile Include="meta\ffdl.c" />
@ -447,6 +452,7 @@
<ClCompile Include="meta\str_asr.c" /> <ClCompile Include="meta\str_asr.c" />
<ClCompile Include="meta\str_snds.c" /> <ClCompile Include="meta\str_snds.c" />
<ClCompile Include="meta\str_wav.c" /> <ClCompile Include="meta\str_wav.c" />
<ClCompile Include="meta\strm_abylight.c" />
<ClCompile Include="meta\stx.c" /> <ClCompile Include="meta\stx.c" />
<ClCompile Include="meta\svs.c" /> <ClCompile Include="meta\svs.c" />
<ClCompile Include="meta\sxd.c" /> <ClCompile Include="meta\sxd.c" />
@ -497,6 +503,7 @@
<ClCompile Include="meta\xwb.c" /> <ClCompile Include="meta\xwb.c" />
<ClCompile Include="meta\xwc.c" /> <ClCompile Include="meta\xwc.c" />
<ClCompile Include="meta\xwma.c" /> <ClCompile Include="meta\xwma.c" />
<ClCompile Include="meta\xwma_konami.c" />
<ClCompile Include="meta\ydsp.c" /> <ClCompile Include="meta\ydsp.c" />
<ClCompile Include="meta\zsd.c" /> <ClCompile Include="meta\zsd.c" />
<ClCompile Include="meta\zsnd.c" /> <ClCompile Include="meta\zsnd.c" />

View File

@ -92,6 +92,9 @@
<ClInclude Include="meta\kma9_streamfile.h"> <ClInclude Include="meta\kma9_streamfile.h">
<Filter>meta\Header Files</Filter> <Filter>meta\Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="meta\mta2_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\opus_interleave_streamfile.h"> <ClInclude Include="meta\opus_interleave_streamfile.h">
<Filter>meta\Header Files</Filter> <Filter>meta\Header Files</Filter>
</ClInclude> </ClInclude>
@ -101,6 +104,9 @@
<ClInclude Include="meta\vsv_streamfile.h"> <ClInclude Include="meta\vsv_streamfile.h">
<Filter>meta\Header Files</Filter> <Filter>meta\Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="meta\sfh_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\sqex_scd_streamfile.h"> <ClInclude Include="meta\sqex_scd_streamfile.h">
<Filter>meta\Header Files</Filter> <Filter>meta\Header Files</Filter>
</ClInclude> </ClInclude>
@ -164,6 +170,9 @@
<ClInclude Include="meta\xvag_streamfile.h"> <ClInclude Include="meta\xvag_streamfile.h">
<Filter>meta\Header Files</Filter> <Filter>meta\Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="meta\xwma_konami_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\zsnd_streamfile.h"> <ClInclude Include="meta\zsnd_streamfile.h">
<Filter>meta\Header Files</Filter> <Filter>meta\Header Files</Filter>
</ClInclude> </ClInclude>
@ -694,7 +703,7 @@
<ClCompile Include="meta\ps2_msa.c"> <ClCompile Include="meta\ps2_msa.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\ps2_npsf.c"> <ClCompile Include="meta\nps.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\ps2_p2bt.c"> <ClCompile Include="meta\ps2_p2bt.c">
@ -793,10 +802,10 @@
<ClCompile Include="meta\ps3_cps.c"> <ClCompile Include="meta\ps3_cps.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\ps3_msf.c"> <ClCompile Include="meta\msf.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\ps3_mta2.c"> <ClCompile Include="meta\mta2.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\xa.c"> <ClCompile Include="meta\xa.c">
@ -898,6 +907,9 @@
<ClCompile Include="meta\str_wav.c"> <ClCompile Include="meta\str_wav.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\strm_abylight.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\stx.c"> <ClCompile Include="meta\stx.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
@ -1033,6 +1045,9 @@
<ClCompile Include="meta\xwma.c"> <ClCompile Include="meta\xwma.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\xwma_konami.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ydsp.c"> <ClCompile Include="meta\ydsp.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
@ -1315,7 +1330,7 @@
<ClCompile Include="meta\opus.c"> <ClCompile Include="meta\opus.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\ps2_mtaf.c"> <ClCompile Include="meta\mtaf.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\ps2_spm.c"> <ClCompile Include="meta\ps2_spm.c">
@ -1369,6 +1384,9 @@
<ClCompile Include="meta\seg.c"> <ClCompile Include="meta\seg.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\sfh.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_wmus.c"> <ClCompile Include="meta\ps2_wmus.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
@ -1438,6 +1456,9 @@
<ClCompile Include="meta\msf_banpresto.c"> <ClCompile Include="meta\msf_banpresto.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\msf_konami.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\msf_tamasoft.c"> <ClCompile Include="meta\msf_tamasoft.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>

View File

@ -188,6 +188,9 @@ static const adxkey_info adxkey8_list[] = {
/* Lucky Star - Ryouou Gakuen Outousai (PS2) */ /* Lucky Star - Ryouou Gakuen Outousai (PS2) */
{0x481D,0x44F9,0x4E35, "LSTARPS2",0}, {0x481D,0x44F9,0x4E35, "LSTARPS2",0},
/* Bakumatsu Renka: Shinsengumi (PS2) */
{0x5381,0x5701,0x665B, "SHINN",0},
}; };
static const adxkey_info adxkey9_list[] = { static const adxkey_info adxkey9_list[] = {

View File

@ -861,6 +861,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
case 0x03: eaac.channels = 4; break; case 0x03: eaac.channels = 4; break;
case 0x04: eaac.channels = 5; break; /* rare [Battlefield 4 (X360)-EAXMA] */ case 0x04: eaac.channels = 5; break; /* rare [Battlefield 4 (X360)-EAXMA] */
case 0x05: eaac.channels = 6; break; case 0x05: eaac.channels = 6; break;
case 0x06: eaac.channels = 7; break; /* rare [Battlefield 4 (X360)-EAXMA] */
case 0x07: eaac.channels = 8; break; case 0x07: eaac.channels = 8; break;
case 0x0a: eaac.channels = 11; break; /* rare [Army of Two: The Devil's Cartel (PS3)-EALayer3v2P] */ case 0x0a: eaac.channels = 11; break; /* rare [Army of Two: The Devil's Cartel (PS3)-EALayer3v2P] */
default: default:

View File

@ -74,6 +74,7 @@
#define EA_BLOCKID_LOC_JA 0x00004A41 /* Japanese, older */ #define EA_BLOCKID_LOC_JA 0x00004A41 /* Japanese, older */
#define EA_BLOCKID_LOC_JP 0x00004A50 /* Japanese, newer */ #define EA_BLOCKID_LOC_JP 0x00004A50 /* Japanese, newer */
#define EA_BLOCKID_LOC_PL 0x0000504C /* Polish */ #define EA_BLOCKID_LOC_PL 0x0000504C /* Polish */
#define EA_BLOCKID_LOC_BR 0x00004252 /* Brazilian Portuguese */
#define EA_BNK_HEADER_LE 0x424E4B6C /* "BNKl" */ #define EA_BNK_HEADER_LE 0x424E4B6C /* "BNKl" */
#define EA_BNK_HEADER_BE 0x424E4B62 /* "BNKb" */ #define EA_BNK_HEADER_BE 0x424E4B62 /* "BNKb" */
@ -135,18 +136,19 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
/* check header */ /* check header */
if (read_32bitBE(0x00,streamFile) != EA_BLOCKID_HEADER && /* "SCHl" */ if (read_32bitBE(0x00,streamFile) != EA_BLOCKID_HEADER && /* "SCHl" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_EN) && /* "SHEN" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_EN) && /* "SHEN" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_FR) && /* "SHFR" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_FR) && /* "SHFR" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_GE) && /* "SHGE" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_GE) && /* "SHGE" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_DE) && /* "SHDE" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_DE) && /* "SHDE" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_IT) && /* "SHIT" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_IT) && /* "SHIT" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_SP) && /* "SHSP" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_SP) && /* "SHSP" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_ES) && /* "SHES" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_ES) && /* "SHES" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_MX) && /* "SHMX" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_MX) && /* "SHMX" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_RU) && /* "SHRU" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_RU) && /* "SHRU" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JA) && /* "SHJA" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JA) && /* "SHJA" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JP) && /* "SHJP" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_JP) && /* "SHJP" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_PL)) /* "SHPL" */ read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_PL) && /* "SHPL" */
read_32bitBE(0x00,streamFile) != (EA_BLOCKID_LOC_HEADER | EA_BLOCKID_LOC_BR)) /* "SHBR" */
goto fail; goto fail;
/* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end. /* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end.
@ -158,6 +160,89 @@ fail:
return NULL; return NULL;
} }
/* EA SCHl inside non-demuxed videos, used in current gen games too */
VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t offset = 0, start_offset = 0;
int blocks_done = 0;
int total_subsongs, target_subsong = streamFile->stream_index;
int32_t(*read_32bit)(off_t, STREAMFILE*);
/* check extension */
/* .vp6: ~late */
if (!check_extensions(streamFile,"vp6"))
goto fail;
/* check initial movie block id */
if (read_32bitBE(0x00,streamFile) != 0x4D566864) /* "MVhd" */
goto fail;
/* use block size to check endianness */
if (guess_endianness32bit(0x04, streamFile)) {
read_32bit = read_32bitBE;
} else {
read_32bit = read_32bitLE;
}
/* find starting valid header for the parser */
while (offset < get_streamfile_size(streamFile)) {
uint32_t block_id = read_32bitBE(offset+0x00,streamFile);
uint32_t block_size = read_32bit (offset+0x04,streamFile);
/* find "SCHl" or "SHxx" blocks */
if ((block_id == EA_BLOCKID_HEADER) || ((block_id & 0xFFFF0000) == EA_BLOCKID_LOC_HEADER)) {
start_offset = offset;
break;
}
if (block_size == 0xFFFFFFFF)
goto fail;
if (blocks_done > 10)
goto fail; /* unlikely to contain music */
blocks_done++;
offset += block_size;
}
if (start_offset == 0)
goto fail;
/* find target subsong (one per each SHxx multilang block) */
total_subsongs = 1;
if (target_subsong == 0) target_subsong = 1;
offset = start_offset;
while (offset < get_streamfile_size(streamFile)) {
uint32_t block_id = read_32bitBE(offset+0x00,streamFile);
uint32_t block_size = read_32bit (offset+0x04,streamFile);
/* no more subsongs (assumes all SHxx headers go together) */
if (((block_id & 0xFFFF0000) != EA_BLOCKID_LOC_HEADER)) {
break;
}
if (target_subsong == total_subsongs) {
start_offset = offset;
/* keep counting subsongs */
}
total_subsongs++;
offset += block_size;
}
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
vgmstream = parse_schl_block(streamFile, start_offset, 1);
if (!vgmstream) goto fail;
vgmstream->num_streams = total_subsongs;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* EA BNK with variable header - from EA games SFXs; also created by sx.exe */ /* EA BNK with variable header - from EA games SFXs; also created by sx.exe */
VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
off_t offset; off_t offset;
@ -702,8 +787,16 @@ fail:
static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int standalone) { static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int standalone) {
off_t start_offset, header_offset; off_t start_offset, header_offset;
size_t header_size; size_t header_size;
uint32_t header_id;
ea_header ea = { 0 }; ea_header ea = { 0 };
/* use higher bits to store target localized block in case of multilang video,
* so only header sub-id will be read and other langs skipped */
header_id = read_32bitBE(offset + 0x00, streamFile);
if ((header_id & 0xFFFF0000) == EA_BLOCKID_LOC_HEADER) {
ea.codec_config |= (header_id & 0xFFFF) << 16;
}
if (guess_endianness32bit(offset + 0x04, streamFile)) { /* size is always LE, except in early SS/MAC */ if (guess_endianness32bit(offset + 0x04, streamFile)) { /* size is always LE, except in early SS/MAC */
header_size = read_32bitBE(offset + 0x04, streamFile); header_size = read_32bitBE(offset + 0x04, streamFile);
ea.codec_config |= 0x02; ea.codec_config |= 0x02;
@ -1497,6 +1590,7 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start
size_t file_size = get_streamfile_size(streamFile); size_t file_size = get_streamfile_size(streamFile);
off_t block_offset = start_offset; off_t block_offset = start_offset;
int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE; int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE;
uint32_t header_lang = (ea->codec_config >> 16) & 0xFFFF;
while (block_offset < file_size) { while (block_offset < file_size) {
uint32_t block_id, block_size; uint32_t block_id, block_size;
@ -1508,27 +1602,16 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start
if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */ if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */
block_size = read_32bitBE(block_offset+0x04,streamFile); block_size = read_32bitBE(block_offset+0x04,streamFile);
switch(block_id) { if (block_id == EA_BLOCKID_DATA || block_id == ((EA_BLOCKID_LOC_DATA | header_lang))) {
case EA_BLOCKID_DATA: /* "SCDl" */ /* "SCDl" or target "SDxx" multilang blocks */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_EN: /* "SDEN" */ offset = read_32bit(block_offset+0x0c,streamFile); /* first value seems ok, second is something else in EALayer3 */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_FR: /* "SDFR" */ return block_offset + 0x0c + ea->channels*0x04 + offset;
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_GE: /* "SDGE" */ }
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_DE: /* "SDDE" */ else if (block_id == 0x00000000) {
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_IT: /* "SDIT" */ goto fail; /* just in case */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_SP: /* "SDSP" */ }
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_ES: /* "SDES" */ else {
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_MX: /* "SDMX" */ block_offset += block_size; /* size includes header */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_RU: /* "SDRU" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_JA: /* "SDJA" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_JP: /* "SDJP" */
case EA_BLOCKID_LOC_DATA | EA_BLOCKID_LOC_PL: /* "SDPL" */
offset = read_32bit(block_offset+0x0c,streamFile); /* first value seems ok, second is something else in EALayer3 */
return block_offset + 0x0c + ea->channels*0x04 + offset;
case 0x00000000:
goto fail; /* just in case */
default:
block_offset += block_size; /* size includes header */
break;
} }
} }

View File

@ -19,6 +19,11 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start,
int32_t loop_start = 0, loop_end = 0, num_samples = 0; int32_t loop_start = 0, loop_end = 0, num_samples = 0;
int total_subsongs, target_subsong = streamFile->stream_index; int total_subsongs, target_subsong = streamFile->stream_index;
/* no checks */
//if (!check_extensions(streamFile, "..."))
// goto fail;
/* init ffmpeg */ /* init ffmpeg */
ffmpeg_codec_data *data = init_ffmpeg_offset(streamFile, start, size); ffmpeg_codec_data *data = init_ffmpeg_offset(streamFile, start, size);
if (!data) return NULL; if (!data) return NULL;
@ -41,6 +46,15 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start,
} }
} }
if (!num_samples) {
num_samples = data->totalSamples;
}
/* hack for AAC files (will return 0 samples if not an actual .aac) */
if (!num_samples && check_extensions(streamFile, "aac,laac")) {
num_samples = aac_get_samples(streamFile, 0x00, get_streamfile_size(streamFile));
}
/* build VGMSTREAM */ /* build VGMSTREAM */
vgmstream = allocate_vgmstream(data->channels, loop_flag); vgmstream = allocate_vgmstream(data->channels, loop_flag);
@ -52,11 +66,7 @@ VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start,
vgmstream->codec_data = data; vgmstream->codec_data = data;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
if (!num_samples) {
num_samples = data->totalSamples;
}
vgmstream->num_samples = num_samples; vgmstream->num_samples = num_samples;
if (loop_flag) { if (loop_flag) {
vgmstream->loop_start_sample = loop_start; vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end; vgmstream->loop_end_sample = loop_end;

View File

@ -33,7 +33,8 @@ typedef enum {
PCFX = 24, /* PC-FX ADPCM */ PCFX = 24, /* PC-FX ADPCM */
PCM4 = 25, /* 4-bit signed PCM (3rd and 4th gen games) */ PCM4 = 25, /* 4-bit signed PCM (3rd and 4th gen games) */
PCM4_U = 26, /* 4-bit unsigned PCM (3rd and 4th gen games) */ PCM4_U = 26, /* 4-bit unsigned PCM (3rd and 4th gen games) */
OKI16 = 27, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */ OKI16 = 27, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */
AAC = 28, /* Advanced Audio Coding (raw without .mp4) */
} genh_type; } genh_type;
typedef struct { typedef struct {
@ -115,6 +116,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
case XMA1: case XMA1:
case XMA2: case XMA2:
case AC3: case AC3:
case AAC:
case FFMPEG: coding = coding_FFmpeg; break; case FFMPEG: coding = coding_FFmpeg; break;
#endif #endif
case PCFX: coding = coding_PCFX; break; case PCFX: coding = coding_PCFX; break;
@ -293,7 +295,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
case coding_FFmpeg: { case coding_FFmpeg: {
ffmpeg_codec_data *ffmpeg_data = NULL; ffmpeg_codec_data *ffmpeg_data = NULL;
if (genh.codec == FFMPEG || genh.codec == AC3) { if (genh.codec == FFMPEG || genh.codec == AC3 || genh.codec == AAC) {
/* default FFmpeg */ /* default FFmpeg */
ffmpeg_data = init_ffmpeg_offset(streamFile, genh.start_offset,genh.data_size); ffmpeg_data = init_ffmpeg_offset(streamFile, genh.start_offset,genh.data_size);
if ( !ffmpeg_data ) goto fail; if ( !ffmpeg_data ) goto fail;

View File

@ -58,7 +58,7 @@ VGMSTREAM * init_vgmstream_rfrm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_npsf(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_nps(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_rs03(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_rs03(STREAMFILE *streamFile);
@ -512,7 +512,7 @@ VGMSTREAM * init_vgmstream_ngc_nst_dsp(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_baf(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_baf(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_baf_badrip(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_baf_badrip(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_msf(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE* streamFile);
@ -551,7 +551,7 @@ VGMSTREAM * init_vgmstream_pc_adp_otns(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_eb_sfx(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_eb_sfx(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_eb_sf0(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_eb_sf0(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_mtaf(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_tun(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_tun(STREAMFILE* streamFile);
@ -628,7 +628,8 @@ VGMSTREAM * init_vgmstream_ta_aac_vita(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_va3(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_va3(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_mta2(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_mta2_container(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_ulw(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ngc_ulw(STREAMFILE * streamFile);
@ -639,6 +640,7 @@ VGMSTREAM * init_vgmstream_wii_04sw(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_txth(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_txth(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ea_schl_video(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_abk(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ea_hdr_dat(STREAMFILE * streamFile);
@ -835,4 +837,12 @@ VGMSTREAM * init_vgmstream_ffdl(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_mus_vc(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_mus_vc(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_strm_abylight(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_sfh(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_msf_konami(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_xwma_konami(STREAMFILE* streamFile);
#endif /*_META_H*/ #endif /*_META_H*/

View File

@ -7,7 +7,7 @@ VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile) {
STREAMFILE * streamHeader = NULL; STREAMFILE * streamHeader = NULL;
off_t start_offset; off_t start_offset;
size_t data_size, frame_size, frame_last, frame_count; size_t data_size, frame_size, frame_last, frame_count;
int channel_count, loop_flag; int channel_count, loop_flag, sample_rate;
/* check extension */ /* check extension */
if (!check_extensions(streamFile, "mib")) if (!check_extensions(streamFile, "mib"))
@ -19,20 +19,22 @@ VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile) {
if (read_32bitBE(0x00,streamHeader) != 0x40000000) /* header size */ if (read_32bitBE(0x00,streamHeader) != 0x40000000) /* header size */
goto fail; goto fail;
loop_flag = 0; /* MIB+MIH don't PS-ADPCM loop flags */ loop_flag = 0; /* MIB+MIH don't loop (nor use PS-ADPCM flags) per spec */
channel_count = read_32bitLE(0x08,streamHeader);
start_offset = 0x00; start_offset = 0x00;
/* 0x04: padding (0x20, MIH header must be multiple of 0x40) */ /* 0x04: padding size (always 0x20, MIH header must be multiple of 0x40) */
frame_last = (uint16_t)read_16bitLE(0x05,streamHeader); frame_last = (uint32_t)read_32bitLE(0x05,streamHeader) & 0x00FFFFFF; /* 24b */
frame_size = read_32bitLE(0x10,streamHeader); channel_count = read_32bitLE(0x08,streamHeader);
frame_count = read_32bitLE(0x14,streamHeader); sample_rate = read_32bitLE(0x0c,streamHeader);
frame_size = read_32bitLE(0x10,streamHeader);
frame_count = read_32bitLE(0x14,streamHeader);
if (frame_count == 0) { /* rarely [Gladius (PS2)] */ if (frame_count == 0) { /* rarely [Gladius (PS2)] */
frame_count = get_streamfile_size(streamFile) / (frame_size * channel_count); frame_count = get_streamfile_size(streamFile) / (frame_size * channel_count);
} }
data_size = frame_count * frame_size; data_size = frame_count * frame_size;
data_size -= frame_last ? (frame_size-frame_last) : 0; /* last frame has less usable data */ if (frame_last)
data_size -= frame_size - frame_last; /* last frame has less usable data */
data_size *= channel_count; data_size *= channel_count;
@ -40,7 +42,7 @@ VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x0C,streamHeader); vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
vgmstream->meta_type = meta_PS2_MIB_MIH; vgmstream->meta_type = meta_PS2_MIB_MIH;

View File

@ -2,12 +2,12 @@
#include "../coding/coding.h" #include "../coding/coding.h"
/* MSF - Sony's PS3 SDK format (MultiStream File) */ /* MSF - Sony's PS3 SDK format (MultiStream File) */
VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_msf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
uint32_t data_size, loop_start = 0, loop_end = 0; uint32_t data_size, loop_start = 0, loop_end = 0;
uint32_t id, codec_id, flags; uint32_t codec, flags;
int loop_flag = 0, channel_count; int loop_flag, channel_count, sample_rate;
/* checks */ /* checks */
@ -17,19 +17,19 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
if (!check_extensions(streamFile,"msf,at3,mp3")) if (!check_extensions(streamFile,"msf,at3,mp3"))
goto fail; goto fail;
start_offset = 0x40;
/* check header "MSF" + version-char, usually: /* check header "MSF" + version-char, usually:
* 0x01, 0x02, 0x30 ("0"), 0x35 ("5"), 0x43 ("C") (last/most common version) */ * 0x01, 0x02, 0x30 ("0"), 0x35 ("5"), 0x43 ("C") (last/most common version) */
id = read_32bitBE(0x00,streamFile); if ((read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4D534600) /* "MSF\0" */
if ((id & 0xffffff00) != 0x4D534600) goto fail; goto fail;
start_offset = 0x40;
codec_id = read_32bitBE(0x04,streamFile); codec = read_32bitBE(0x04,streamFile);
channel_count = read_32bitBE(0x08,streamFile); channel_count = read_32bitBE(0x08,streamFile);
data_size = read_32bitBE(0x0C,streamFile); /* without header */ data_size = read_32bitBE(0x0C,streamFile); /* without header */
if (data_size == 0xFFFFFFFF) /* unneeded? */ if (data_size == 0xFFFFFFFF) /* unneeded? */
data_size = get_streamfile_size(streamFile) - start_offset; data_size = get_streamfile_size(streamFile) - start_offset;
sample_rate = read_32bitBE(0x10,streamFile);
/* byte flags, not in MSFv1 or v2 /* byte flags, not in MSFv1 or v2
* 0x01/02/04/08: loop marker 0/1/2/3 * 0x01/02/04/08: loop marker 0/1/2/3
@ -56,17 +56,15 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitBE(0x10,streamFile); vgmstream->meta_type = meta_MSF;
/* sample rate hack for strange MSFv1 files (PS ADPCM only?) */ vgmstream->sample_rate = sample_rate;
if (vgmstream->sample_rate == 0x00000000) if (vgmstream->sample_rate == 0) /* some MSFv1 (PS-ADPCM only?) [Megazone 23 - Aoi Garland (PS3)] */
vgmstream->sample_rate = 48000; vgmstream->sample_rate = 48000;
vgmstream->meta_type = meta_PS3_MSF; switch (codec) {
switch (codec_id) {
case 0x00: /* PCM (Big Endian) */ case 0x00: /* PCM (Big Endian) */
case 0x01: { /* PCM (Little Endian) [Smash Cars (PS3)] */ case 0x01: { /* PCM (Little Endian) [Smash Cars (PS3)] */
vgmstream->coding_type = codec_id==0 ? coding_PCM16BE : coding_PCM16LE; vgmstream->coding_type = codec==0 ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave;
vgmstream->interleave_block_size = 2; vgmstream->interleave_block_size = 2;
@ -85,7 +83,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
case 0x03: { /* PS ADPCM [Smash Cars (PS3)] */ case 0x03: { /* PS ADPCM [Smash Cars (PS3)] */
vgmstream->coding_type = coding_PSX; vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10; vgmstream->interleave_block_size = 0x10;
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
@ -105,8 +103,8 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
uint8_t buf[100]; uint8_t buf[100];
int32_t bytes, block_size, encoder_delay, joint_stereo; int32_t bytes, block_size, encoder_delay, joint_stereo;
block_size = (codec_id==4 ? 0x60 : (codec_id==5 ? 0x98 : 0xC0)) * vgmstream->channels; block_size = (codec==4 ? 0x60 : (codec==5 ? 0x98 : 0xC0)) * vgmstream->channels;
joint_stereo = (codec_id==4); /* interleaved joint stereo (ch must be even) */ joint_stereo = (codec==4); /* interleaved joint stereo (ch must be even) */
/* MSF skip samples: from tests with MSEnc and real files (ex. TTT2 eddy.msf v43, v01 demos) seems like 1162 is consistent. /* MSF skip samples: from tests with MSEnc and real files (ex. TTT2 eddy.msf v43, v01 demos) seems like 1162 is consistent.
* Atelier Rorona bt_normal01 needs it to properly skip the beginning garbage but usually doesn't matter. * Atelier Rorona bt_normal01 needs it to properly skip the beginning garbage but usually doesn't matter.

View File

@ -19,7 +19,7 @@ VGMSTREAM * init_vgmstream_msf_banpresto_wmsf(STREAMFILE *streamFile) {
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL); temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL);
if (!temp_streamFile) goto fail; if (!temp_streamFile) goto fail;
vgmstream = init_vgmstream_ps3_msf(temp_streamFile); vgmstream = init_vgmstream_msf(temp_streamFile);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
close_streamfile(temp_streamFile); close_streamfile(temp_streamFile);

58
src/meta/msf_konami.c Normal file
View File

@ -0,0 +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;
}

133
src/meta/mta2.c Normal file
View File

@ -0,0 +1,133 @@
#include "meta.h"
#include "mta2_streamfile.h"
/* MTA2 - found in Metal Gear Solid 4 (PS3) */
VGMSTREAM * init_vgmstream_mta2(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, sample_rate;
int32_t loop_start, loop_end;
uint32_t sample_rate_int;
/* checks */
if ( !check_extensions(streamFile,"mta2"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4d544132) /* "MTA2" */
goto fail;
/* allow truncated files for now? */
//if (read_32bitBE(0x04, streamFile) + 0x08 != get_streamfile_size(streamFile))
// goto fail;
/* base header (everything is very similar to MGS3's MTAF but BE) */
/* 0x08(4): version? (1), 0x0c(52): null */
/* HEAD chunk */
if (read_32bitBE(0x40, streamFile) != 0x48454144) /* "HEAD" */
goto fail;
if (read_32bitBE(0x44, streamFile) != 0xB0) /* HEAD size */
goto fail;
/* 0x48(4): null, 0x4c: ? (0x10), 0x50(4): 0x7F (vol?), 0x54(2): 0x40 (pan?) */
channel_count = read_16bitBE(0x56, streamFile); /* counting all tracks */
/* 0x60(4): full block size (0x110 * channels), indirectly channels_per_track = channels / (block_size / 0x110) */
/* 0x80 .. 0xf8: null */
loop_start = read_32bitBE(0x58, streamFile);
loop_end = read_32bitBE(0x5c, streamFile);
loop_flag = (loop_start != loop_end); /* also flag possibly @ 0x73 */
#if 0
/* those values look like some kind of loop offsets */
if (loop_start/0x100 != read_32bitBE(0x68, streamFile) ||
loop_end /0x100 != read_32bitBE(0x6C, streamFile) ) {
VGM_LOG("MTA2: wrong loop points\n");
goto fail;
}
#endif
sample_rate_int = read_32bitBE(0x7c, streamFile);
if (sample_rate_int) { /* sample rate in 32b float (WHY?) typically 48000.0 */
float* sample_float = (float*)&sample_rate_int;
sample_rate = (int)*sample_float;
} else { /* default when not specified (most of the time) */
sample_rate = 48000;
}
/* TRKP chunks (x16) */
/* just seem to contain pan/vol stuff (0x7f/0x40), TRKP per track (sometimes +1 main track?) */
/* there is channel layout bitmask at 0x0f (ex. 1ch = 0x04, 3ch = 0x07, 4ch = 0x33, 6ch = 0x3f), surely:
* FL 0x01, FR 0x02, FC = 0x04, BL = 0x08, BR = 0x10, BC = 0x20 */
start_offset = 0x800;
/* DATA chunk */
if (read_32bitBE(0x7f8, streamFile) != 0x44415441) // "DATA"
goto fail;
//if (read_32bitBE(0x7fc, streamFile) + start_offset != get_streamfile_size(streamFile))
// goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = loop_end;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->coding_type = coding_MTA2;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_MTA2;
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* ****************************************************************************** */
/* MTA2 in containers */
VGMSTREAM * init_vgmstream_mta2_container(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
off_t subfile_offset;
/* checks */
/* .dbm: iPod metadata + mta2 with KCEJ blocks, .bgm: mta2 with KCEJ blocks (fake?) */
if ( !check_extensions(streamFile,"dbm,bgm,mta2"))
goto fail;
if (read_32bitBE(0x00,streamFile) == 0x444C424D) { /* "DLBM" */
subfile_offset = 0x800;
}
else if (read_32bitBE(0x00,streamFile) == 0x00000010) {
subfile_offset = 0x00;
}
else {
goto fail;
}
/* subfile size is implicit in KCEJ blocks */
temp_streamFile = setup_mta2_streamfile(streamFile, subfile_offset, 1, "mta2");
if (!temp_streamFile) goto fail;
vgmstream = init_vgmstream_mta2(temp_streamFile);
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}

154
src/meta/mta2_streamfile.h Normal file
View File

@ -0,0 +1,154 @@
#ifndef _MTA2_STREAMFILE_H_
#define _MTA2_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
/* config */
int big_endian;
uint32_t target_type;
off_t stream_offset;
size_t stream_size;
/* state */
off_t logical_offset; /* fake offset */
off_t physical_offset; /* actual offset */
size_t block_size; /* current size */
size_t skip_size; /* size from block start to reach data */
size_t data_size; /* usable size in a block */
size_t logical_size;
} mta2_io_data;
static size_t mta2_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, mta2_io_data* data) {
size_t total_read = 0;
uint32_t (*read_u32)(off_t,STREAMFILE*) = data->big_endian ? read_u32be : read_u32le;
/* re-start when previous offset (can't map logical<>physical offsets) */
if (data->logical_offset < 0 || offset < data->logical_offset) {
data->physical_offset = data->stream_offset;
data->logical_offset = 0x00;
data->data_size = 0;
}
/* read blocks */
while (length > 0) {
/* ignore EOF */
if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
break;
}
/* process new block */
if (data->data_size == 0) {
uint32_t block_type, block_size, block_track;
block_type = read_u32(data->physical_offset+0x00, streamfile); /* subtype and type */
block_size = read_u32(data->physical_offset+0x04, streamfile);
//block_unk = read_u32(data->physical_offset+0x08, streamfile); /* usually 0 except for 0xF0 'end' block */
block_track = read_u32(data->physical_offset+0x0c, streamfile);
if (block_type != data->target_type || block_size == 0xFFFFFFFF)
break;
data->block_size = block_size;
data->skip_size = 0x10;
data->data_size = block_size - data->skip_size;
/* no audio data (padding block), but write first (header) */
if (block_track == 0 && data->logical_offset > 0)
data->data_size = 0;
}
/* move to next block */
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
data->physical_offset += data->block_size;
data->logical_offset += data->data_size;
data->data_size = 0;
continue;
}
/* read data */
{
size_t bytes_consumed, bytes_done, to_read;
bytes_consumed = offset - data->logical_offset;
to_read = data->data_size - bytes_consumed;
if (to_read > length)
to_read = length;
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
total_read += bytes_done;
dest += bytes_done;
offset += bytes_done;
length -= bytes_done;
if (bytes_done != to_read || bytes_done == 0) {
break; /* error/EOF */
}
}
}
return total_read;
}
static size_t mta2_io_size(STREAMFILE *streamfile, mta2_io_data* data) {
uint8_t buf[1];
if (data->logical_size)
return data->logical_size;
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
mta2_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
data->logical_size = data->logical_offset;
return data->logical_size;
}
/* Handles removing KCE Japan-style blocks in MTA2 streams
* (these blocks exist in most KCEJ games and aren't actually related to audio) */
static STREAMFILE* setup_mta2_streamfile(STREAMFILE *streamFile, off_t stream_offset, int big_endian, const char* extension) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
mta2_io_data io_data = {0};
size_t io_data_size = sizeof(mta2_io_data);
uint32_t (*read_u32)(off_t,STREAMFILE*) = big_endian ? read_u32be : read_u32le;
/* blocks must start with a 'new sub-stream' id */
if (read_u32(stream_offset+0x00, streamFile) != 0x00000010)
goto fail;
io_data.target_type = read_u32(stream_offset + 0x0c, streamFile);
io_data.stream_offset = stream_offset + 0x10;
io_data.stream_size = get_streamfile_size(streamFile) - io_data.stream_offset;
io_data.big_endian = big_endian;
io_data.logical_offset = -1; /* force phys offset reset */
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, mta2_io_read,mta2_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
if (extension) {
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
}
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _MTA2_STREAMFILE_H_ */

View File

@ -2,19 +2,17 @@
#include "../util.h" #include "../util.h"
/* MTAF - found in Metal Gear Solid 3: Snake Eater (Subsistence and HD too) */ /* MTAF - found in Metal Gear Solid 3: Snake Eater (PS2), Subsistence and HD too */
VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_mtaf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
int loop_flag, channel_count; int loop_flag, channel_count;
int32_t loop_start, loop_end; int32_t loop_start, loop_end;
/* check extension */ /* checks */
if ( !check_extensions(streamFile,"mtaf")) if ( !check_extensions(streamFile,"mtaf"))
goto fail; goto fail;
/* base header */
if (read_32bitBE(0x00, streamFile) != 0x4d544146) /* "MTAF" */ if (read_32bitBE(0x00, streamFile) != 0x4d544146) /* "MTAF" */
goto fail; goto fail;
/* 0x04(4): pseudo file size (close but smaller) */ /* 0x04(4): pseudo file size (close but smaller) */
@ -51,12 +49,9 @@ VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE *streamFile) {
goto fail; goto fail;
/* 0x7fc: data size (without blocks in case of blocked layout) */ /* 0x7fc: data size (without blocks in case of blocked layout) */
/* without blocks it should start with 0x00000100 ("frame 1 from track 0") */
//is_blocked = read_32bitLE(0x800,streamFile) != 0x00000100 && read_32bitLE(0x810,streamFile) == 0x00000100;
start_offset = 0x800; start_offset = 0x800;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag); vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
@ -68,23 +63,11 @@ VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_MTAF; vgmstream->coding_type = coding_MTAF;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x110/2; /* kinda hacky for MTAF track layout */ vgmstream->interleave_block_size = 0x110 / 2; /* kinda hacky for MTAF (stereo codec) track layout */
vgmstream->meta_type = meta_PS2_MTAF; vgmstream->meta_type = meta_MTAF;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
/* open the file for reading, in a specific way */ goto fail;
{
int i;
char filename[PATH_LIMIT];
streamFile->get_name(streamFile,filename,sizeof(filename));
for (i = 0; i < channel_count; i++) {
STREAMFILE * file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset = start_offset + vgmstream->interleave_block_size*2*(i/2);
}
}
return vgmstream; return vgmstream;

View File

@ -1,13 +1,15 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
/* NPFS - found in Namco PS2/PSP games (Tekken 5, Ace Combat 5, Yumeria, Venus & Braves, Ridge Racer PSP) */ /* NPFS - found in Namco PS2/PSP games [Tekken 5 (PS2), Venus & Braves (PS2), Ridge Racer (PSP)] */
VGMSTREAM * init_vgmstream_ps2_npsf(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_nps(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset; off_t start_offset;
int loop_flag, channel_count; int loop_flag, channel_count;
/* check extension, case insensitive (should be .nps as per Venus & Braves data files) */ /* checks */
/* .nps: referenced extension (ex. Venus & Braves data files)
* .npsf: header id (Namco Production Sound File?) */
if ( !check_extensions(streamFile,"nps,npsf")) if ( !check_extensions(streamFile,"nps,npsf"))
goto fail; goto fail;
@ -16,7 +18,8 @@ VGMSTREAM * init_vgmstream_ps2_npsf(STREAMFILE *streamFile) {
loop_flag = (read_32bitLE(0x14,streamFile) != 0xFFFFFFFF); loop_flag = (read_32bitLE(0x14,streamFile) != 0xFFFFFFFF);
channel_count = read_32bitLE(0x0C,streamFile); channel_count = read_32bitLE(0x0C,streamFile);
start_offset = (off_t)read_32bitLE(0x10,streamFile);
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
@ -32,13 +35,9 @@ VGMSTREAM * init_vgmstream_ps2_npsf(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_PSX; vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave; vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x04,streamFile) / 2; vgmstream->interleave_block_size = read_32bitLE(0x04,streamFile) / 2;
vgmstream->meta_type = meta_PS2_NPSF; vgmstream->meta_type = meta_NPS;
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, 0x34,streamFile); read_string(vgmstream->stream_name,STREAM_NAME_SIZE, 0x34,streamFile);
start_offset = (off_t)read_32bitLE(0x10,streamFile);
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail; goto fail;
return vgmstream; return vgmstream;

View File

@ -1,104 +0,0 @@
#include "meta.h"
#include "../util.h"
/* MTA2 - found in Metal Gear Solid 4 */
VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t header_offset, start_offset;
int loop_flag, channel_count, sample_rate; //block_offset;
int32_t loop_start, loop_end;
/* check extension */
/* .mta2: normal file, .bgm: mta2 with block layout, .dbm: iPod metadata + block layout mta2 */
if ( !check_extensions(streamFile,"mta2,bgm,dbm"))
goto fail;
/* base header (everything is very similar to MGS3's MTAF but BE) */
if (read_32bitBE(0x00,streamFile) == 0x4d544132) { /* "MTA2" (.mta) */
//block_offset = 0;
header_offset = 0x00;
} else if (read_32bitBE(0x20,streamFile) == 0x4d544132) { /* "MTA2" @ 0x20 (.bgm) */
//block_offset = 0x10;
header_offset = 0x20;
} else if (read_32bitBE(0x00, streamFile) == 0x444C424D
&& read_32bitBE(0x820,streamFile) == 0x4d544132) { /* "DLBM" + "MTA2" @ 0x820 (.dbm) */
//block_offset = 0x810;
header_offset = 0x820;
} else {
goto fail;
}
/* 0x04(4): file size -4-4 (not including block headers in case of block layout) */
/* 0x08(4): version? (1), 0x0c(52): null */
/* HEAD chunk */
if (read_32bitBE(header_offset+0x40, streamFile) != 0x48454144) /* "HEAD" */
goto fail;
if (read_32bitBE(header_offset+0x44, streamFile) != 0xB0) /* HEAD size */
goto fail;
/* 0x48(4): null, 0x4c: ? (0x10), 0x50(4): 0x7F (vol?), 0x54(2): 0x40 (pan?) */
channel_count = read_16bitBE(header_offset+0x56, streamFile); /* counting all tracks */
/* 0x60(4): full block size (0x110 * channels), indirectly channels_per_track = channels / (block_size / 0x110) */
/* 0x80 .. 0xf8: null */
loop_start = read_32bitBE(header_offset+0x58, streamFile);
loop_end = read_32bitBE(header_offset+0x5c, streamFile);
loop_flag = (loop_start != loop_end); /* also flag possibly @ 0x73 */
#if 0
/* those values look like some kind of loop offsets */
if (loop_start/0x100 != read_32bitBE(header_offset+0x68, streamFile) ||
loop_end /0x100 != read_32bitBE(header_offset+0x6C, streamFile) ) {
VGM_LOG("MTA2: wrong loop points\n");
goto fail;
}
#endif
sample_rate = read_32bitBE(header_offset+0x7c, streamFile);
if (sample_rate) { /* sample rate in 32b float (WHY?) typically 48000.0 */
float sample_float;
memcpy(&sample_float, &sample_rate, 4);
sample_rate = (int)sample_float;
} else { /* default when not specified (most of the time) */
sample_rate = 48000;
}
/* TRKP chunks (x16) */
/* just seem to contain pan/vol stuff (0x7f/0x40), TRKP per track (sometimes +1 main track?) */
/* there is channel layout bitmask @ 0x0f (ex. 1ch = 0x04, 3ch = 0x07, 4ch = 0x33, 6ch = 0x3f), surely:
* FRONT_L = 0x01, FRONT_R = 0x02, FRONT_M = 0x04, BACK_L = 0x08, BACK_R = 0x10, BACK_M = 0x20 */
/* DATA chunk */
if (read_32bitBE(header_offset+0x7f8, streamFile) != 0x44415441) // "DATA"
goto fail;
/* 0x7fc: data size (without blocks in case of blocked layout) */
start_offset = header_offset + 0x800;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = loop_end;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->coding_type = coding_MTA2;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_PS3_MTA2;
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -233,6 +233,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk
bztmp = (bztmp >> 8) | (bztmp << 8); bztmp = (bztmp >> 8) | (bztmp << 8);
fmt->coding_type = coding_AT3plus; fmt->coding_type = coding_AT3plus;
fmt->block_size = (bztmp & 0x3FF) * 8 + 8; /* should match fmt->block_size */ fmt->block_size = (bztmp & 0x3FF) * 8 + 8; /* should match fmt->block_size */
break;
#elif defined(VGM_USE_FFMPEG) #elif defined(VGM_USE_FFMPEG)
fmt->coding_type = coding_FFmpeg; fmt->coding_type = coding_FFmpeg;
break; break;

46
src/meta/sfh.c Normal file
View File

@ -0,0 +1,46 @@
#include "meta.h"
#include "../coding/coding.h"
#include "sfh_streamfile.h"
/* .SFH - Capcom wrapper [Devil May Cry 4 Demo (PS3), Jojo's Bizarre Adventure HD (PS3)] */
VGMSTREAM * init_vgmstream_sfh(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
uint32_t version;
size_t clean_size, block_size;
/* check extensions */
if ( !check_extensions(streamFile,"at3"))
goto fail;
if (read_32bitBE(0x00, streamFile) != 0x00534648) /* "\0SFH" */
goto fail;
if (read_32bitBE(0x10, streamFile) != 0x52494646) /* "RIFF" */
goto fail;
/* mini header */
version = read_32bitBE(0x04,streamFile);
clean_size = read_32bitBE(0x08,streamFile); /* there is padding data at the end */
/* 0x0c: always 0 */
switch(version) {
case 0x00010000: block_size = 0x10010; break; /* DMC4 Demo (not retail) */
case 0x00010001: block_size = 0x20000; break; /* Jojo */
default: goto fail;
}
temp_streamFile = setup_sfh_streamfile(streamFile, 0x00, block_size, clean_size, "at3");
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;
}

133
src/meta/sfh_streamfile.h Normal file
View File

@ -0,0 +1,133 @@
#ifndef _SFH_STREAMFILE_H_
#define _SFH_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
/* config */
off_t stream_offset;
size_t stream_size;
/* state */
off_t logical_offset; /* fake offset */
off_t physical_offset; /* actual offset */
size_t block_size; /* current size */
size_t skip_size; /* size from block start to reach data */
size_t data_size; /* usable size in a block */
size_t logical_size;
} sfh_io_data;
static size_t sfh_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, sfh_io_data* data) {
size_t total_read = 0;
/* re-start when previous offset (can't map logical<>physical offsets) */
if (data->logical_offset < 0 || offset < data->logical_offset) {
data->physical_offset = data->stream_offset;
data->logical_offset = 0x00;
data->data_size = 0;
}
/* read blocks */
while (length > 0) {
/* ignore EOF */
if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
break;
}
/* process new block */
if (data->data_size == 0) {
data->skip_size = 0x10; /* skip 0x10 garbage on every block */
data->data_size = data->block_size - 0x10;
}
/* move to next block */
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
data->physical_offset += data->block_size;
data->logical_offset += data->data_size;
data->data_size = 0;
continue;
}
/* read data */
{
size_t bytes_consumed, bytes_done, to_read;
bytes_consumed = offset - data->logical_offset;
to_read = data->data_size - bytes_consumed;
if (to_read > length)
to_read = length;
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
total_read += bytes_done;
dest += bytes_done;
offset += bytes_done;
length -= bytes_done;
if (bytes_done != to_read || bytes_done == 0) {
break; /* error/EOF */
}
}
}
return total_read;
}
static size_t sfh_io_size(STREAMFILE *streamfile, sfh_io_data* data) {
uint8_t buf[1];
if (data->logical_size)
return data->logical_size;
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
sfh_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
data->logical_size = data->logical_offset;
return data->logical_size;
}
/* Handles deinterleaving of SFH blocked streams */
static STREAMFILE* setup_sfh_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t block_size, size_t clean_size, const char* extension) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
sfh_io_data io_data = {0};
size_t io_data_size = sizeof(sfh_io_data);
io_data.stream_offset = stream_offset;
io_data.stream_size = get_streamfile_size(streamFile) - stream_offset;
io_data.block_size = block_size;
io_data.logical_offset = -1; /* force phys offset reset */
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, sfh_io_read,sfh_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(new_streamFile,0x00, clean_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
if (extension) {
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,extension);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
}
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _SFH_STREAMFILE_H_ */

71
src/meta/strm_abylight.c Normal file
View File

@ -0,0 +1,71 @@
#include "meta.h"
#include "../coding/coding.h"
/* .STRM - from Abylight 3DS games [Cursed Castilla (3DS)] */
VGMSTREAM * init_vgmstream_strm_abylight(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, sample_rate;
size_t data_size;
/* check extension */
if ( !check_extensions(streamFile,"strm") )
goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */
goto fail;
if (read_32bitLE(0x04,streamFile) != 0x03E8) /* version 1000? */
goto fail;
loop_flag = 0;
channel_count = 2; /* there are various possible fields but all files are stereo */
sample_rate = read_32bitLE(0x08,streamFile);
start_offset = 0x1e;
data_size = read_32bitLE(0x10,streamFile);
if (data_size != get_streamfile_size(streamFile) - start_offset)
goto fail;
if (data_size != read_32bitLE(0x18,streamFile))
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = aac_get_samples(streamFile, start_offset, data_size);
vgmstream->meta_type = meta_STRM_ABYLIGHT;
#ifdef VGM_USE_FFMPEG
{
ffmpeg_codec_data *ffmpeg_data = NULL;
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,data_size);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* apparently none, or maybe ~600 */
//if (!ffmpeg_data->skipSamples)
// ffmpeg_set_skip_samples(ffmpeg_data, 1024);
//vgmstream->num_samples -= 1024;
}
#else
goto fail;
#endif
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -34,6 +34,7 @@ typedef enum {
PCM4 = 25, /* 4-bit signed PCM (3rd and 4th gen games) */ PCM4 = 25, /* 4-bit signed PCM (3rd and 4th gen games) */
PCM4_U = 26, /* 4-bit unsigned PCM (3rd and 4th gen games) */ PCM4_U = 26, /* 4-bit unsigned PCM (3rd and 4th gen games) */
OKI16 = 27, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */ OKI16 = 27, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */
AAC = 28, /* Advanced Audio Coding (raw without .mp4) */
} txth_type; } txth_type;
typedef struct { typedef struct {
@ -73,6 +74,8 @@ typedef struct {
uint32_t coef_spacing; uint32_t coef_spacing;
uint32_t coef_big_endian; uint32_t coef_big_endian;
uint32_t coef_mode; uint32_t coef_mode;
int coef_table_set;
uint8_t coef_table[0x02*16 * 16]; /* reasonable max */
int num_samples_data_size; int num_samples_data_size;
@ -84,6 +87,11 @@ typedef struct {
uint32_t name_offset; uint32_t name_offset;
uint32_t name_size; uint32_t name_size;
int subfile_set;
uint32_t subfile_offset;
uint32_t subfile_size;
char subfile_extension[32];
/* original STREAMFILE and its type (may be an unsupported "base" file or a .txth) */ /* original STREAMFILE and its type (may be an unsupported "base" file or a .txth) */
STREAMFILE *streamFile; STREAMFILE *streamFile;
int streamfile_is_txth; int streamfile_is_txth;
@ -98,12 +106,9 @@ typedef struct {
} txth_header; } txth_header;
static STREAMFILE * open_txth(STREAMFILE * streamFile); static STREAMFILE * open_txth(STREAMFILE * streamFile);
static VGMSTREAM *init_subfile(txth_header * txth);
static int parse_txth(txth_header * txth); static int parse_txth(txth_header * txth);
static int parse_keyval(STREAMFILE * streamFile, txth_header * txth, const char * key, char * val);
static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * val, uint32_t * out_value);
static int get_bytes_to_samples(txth_header * txth, uint32_t bytes);
/* TXTH - an artificial "generic" header for headerless streams. /* TXTH - an artificial "generic" header for headerless streams.
@ -148,6 +153,11 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
if (!parse_txth(&txth)) if (!parse_txth(&txth))
goto fail; goto fail;
/* special case of parsing subfiles */
if (txth.subfile_set) {
return init_subfile(&txth);
}
/* type to coding conversion */ /* type to coding conversion */
switch (txth.codec) { switch (txth.codec) {
@ -177,6 +187,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case XMA1: case XMA1:
case XMA2: case XMA2:
case AC3: case AC3:
case AAC:
case FFMPEG: coding = coding_FFmpeg; break; case FFMPEG: coding = coding_FFmpeg; break;
#endif #endif
case PCFX: coding = coding_PCFX; break; case PCFX: coding = coding_PCFX; break;
@ -343,11 +354,17 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
/* get coefs */ /* get coefs */
for (i = 0; i < vgmstream->channels; i++) { for (i = 0; i < vgmstream->channels; i++) {
int16_t (*read_16bit)(off_t , STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE; int16_t (*read_16bit)(off_t , STREAMFILE*) = txth.coef_big_endian ? read_16bitBE : read_16bitLE;
int16_t (*get_16bit)(uint8_t * p) = txth.coef_big_endian ? get_16bitBE : get_16bitLE;
/* normal/split coefs */ /* normal/split coefs */
if (txth.coef_mode == 0) { /* normal mode */ if (txth.coef_mode == 0) { /* normal mode */
for (j = 0; j < 16; j++) { for (j = 0; j < 16; j++) {
vgmstream->ch[i].adpcm_coef[j] = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2, txth.streamHead); int16_t coef;
if (txth.coef_table_set)
coef = get_16bit(txth.coef_table + i*txth.coef_spacing + j*2);
else
coef = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2, txth.streamHead);
vgmstream->ch[i].adpcm_coef[j] = coef;
} }
} }
else { /* split coefs */ else { /* split coefs */
@ -375,7 +392,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case coding_FFmpeg: { case coding_FFmpeg: {
ffmpeg_codec_data *ffmpeg_data = NULL; ffmpeg_codec_data *ffmpeg_data = NULL;
if (txth.codec == FFMPEG || txth.codec == AC3) { if (txth.codec == FFMPEG || txth.codec == AC3 || txth.codec == AAC) {
/* default FFmpeg */ /* default FFmpeg */
ffmpeg_data = init_ffmpeg_offset(txth.streamBody, txth.start_offset,txth.data_size); ffmpeg_data = init_ffmpeg_offset(txth.streamBody, txth.start_offset,txth.data_size);
if ( !ffmpeg_data ) goto fail; if ( !ffmpeg_data ) goto fail;
@ -490,6 +507,55 @@ fail:
return NULL; return NULL;
} }
static VGMSTREAM *init_subfile(txth_header * txth) {
VGMSTREAM *vgmstream = NULL;
char extension[PATH_LIMIT];
STREAMFILE * streamSubfile = NULL;
if (txth->subfile_size == 0)
txth->subfile_size = txth->data_size - txth->subfile_offset;
if (txth->subfile_extension[0] == '\0')
get_streamfile_ext(txth->streamFile,txth->subfile_extension,sizeof(txth->subfile_extension));
/* must detect a potential infinite loop:
* - init_vgmstream enters TXTH and reads .txth
* - TXTH subfile calls init, nothing is detected
* - init_vgmstream enters TXTH and reads .txth
* - etc
* to avoid it we set a particular fake extension and detect it when reading .txth
*/
strcpy(extension, "subfile_txth.");
strcat(extension, txth->subfile_extension);
streamSubfile = setup_subfile_streamfile(txth->streamBody, txth->subfile_offset, txth->subfile_size, extension);
if (!streamSubfile) goto fail;
vgmstream = init_vgmstream_from_STREAMFILE(streamSubfile);
if (!vgmstream) goto fail;
/* apply some fields */
if (txth->sample_rate)
vgmstream->sample_rate = txth->sample_rate;
if (txth->num_samples)
vgmstream->num_samples = txth->num_samples;
if (txth->loop_flag) {
vgmstream_force_loop(vgmstream, txth->loop_flag, txth->loop_start_sample, txth->loop_end_sample);
}
else if (txth->loop_flag_set && vgmstream->loop_flag) {
vgmstream_force_loop(vgmstream, 0, 0, 0);
}
close_streamfile(streamSubfile);
return vgmstream;
fail:
close_streamfile(streamSubfile);
close_vgmstream(vgmstream);
return NULL;
}
static STREAMFILE * open_txth(STREAMFILE * streamFile) { static STREAMFILE * open_txth(STREAMFILE * streamFile) {
char basename[PATH_LIMIT]; char basename[PATH_LIMIT];
@ -500,6 +566,8 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) {
/* try "(path/)(name.ext).txth" */ /* try "(path/)(name.ext).txth" */
get_streamfile_name(streamFile,filename,PATH_LIMIT); get_streamfile_name(streamFile,filename,PATH_LIMIT);
if (strstr(filename, "subfile_txth") != NULL)
return NULL; /* detect special case of subfile-within-subfile */
strcat(filename, ".txth"); strcat(filename, ".txth");
streamText = open_streamfile(streamFile,filename); streamText = open_streamfile(streamFile,filename);
if (streamText) return streamText; if (streamText) return streamText;
@ -539,6 +607,16 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) {
return NULL; return NULL;
} }
/* ****************************************************************** */
static int parse_keyval(STREAMFILE * streamFile, txth_header * txth, const char * key, char * val);
static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * val, uint32_t * out_value);
static int parse_string(STREAMFILE * streamFile, txth_header * txth, const char * val, char * str);
static int parse_coef_table(STREAMFILE * streamFile, txth_header * txth, const char * val, uint8_t * out_value, size_t out_size);
static int is_string(const char * val, const char * cmp);
static int is_substring(const char * val, const char * cmp);
static int get_bytes_to_samples(txth_header * txth, uint32_t bytes);
/* Simple text parser of "key = value" lines. /* Simple text parser of "key = value" lines.
* The code is meh and error handling not exactly the best. */ * The code is meh and error handling not exactly the best. */
static int parse_txth(txth_header * txth) { static int parse_txth(txth_header * txth) {
@ -573,8 +651,8 @@ static int parse_txth(txth_header * txth) {
txt_offset += bytes_read; txt_offset += bytes_read;
/* get key/val (ignores lead/trail spaces, stops at space/comment/separator) */ /* get key/val (ignores lead spaces, stops at space/comment/separator) */
ok = sscanf(line, " %[^ \t#=] = %[^ \t#\r\n] ", key,val); ok = sscanf(line, " %[^ \t#=] = %[^\t#\r\n] ", key,val);
if (ok != 2) /* ignore line if no key=val (comment or garbage) */ if (ok != 2) /* ignore line if no key=val (comment or garbage) */
continue; continue;
@ -599,38 +677,39 @@ fail:
static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char * key, char * val) { static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char * key, char * val) {
//;VGM_LOG("TXTH: key=%s, val=%s\n", key, val); //;VGM_LOG("TXTH: key=%s, val=%s\n", key, val);
if (0==strcmp(key,"codec")) { if (is_string(key,"codec")) {
if (0==strcmp(val,"PSX")) txth->codec = PSX; if (is_string(val,"PSX")) txth->codec = PSX;
else if (0==strcmp(val,"XBOX")) txth->codec = XBOX; else if (is_string(val,"XBOX")) txth->codec = XBOX;
else if (0==strcmp(val,"NGC_DTK")) txth->codec = NGC_DTK; else if (is_string(val,"NGC_DTK")) txth->codec = NGC_DTK;
else if (0==strcmp(val,"DTK")) txth->codec = NGC_DTK; else if (is_string(val,"DTK")) txth->codec = NGC_DTK;
else if (0==strcmp(val,"PCM16BE")) txth->codec = PCM16BE; else if (is_string(val,"PCM16BE")) txth->codec = PCM16BE;
else if (0==strcmp(val,"PCM16LE")) txth->codec = PCM16LE; else if (is_string(val,"PCM16LE")) txth->codec = PCM16LE;
else if (0==strcmp(val,"PCM8")) txth->codec = PCM8; else if (is_string(val,"PCM8")) txth->codec = PCM8;
else if (0==strcmp(val,"SDX2")) txth->codec = SDX2; else if (is_string(val,"SDX2")) txth->codec = SDX2;
else if (0==strcmp(val,"DVI_IMA")) txth->codec = DVI_IMA; else if (is_string(val,"DVI_IMA")) txth->codec = DVI_IMA;
else if (0==strcmp(val,"MPEG")) txth->codec = MPEG; else if (is_string(val,"MPEG")) txth->codec = MPEG;
else if (0==strcmp(val,"IMA")) txth->codec = IMA; else if (is_string(val,"IMA")) txth->codec = IMA;
else if (0==strcmp(val,"YAMAHA")) txth->codec = YAMAHA; else if (is_string(val,"YAMAHA")) txth->codec = YAMAHA;
else if (0==strcmp(val,"AICA")) txth->codec = YAMAHA; else if (is_string(val,"AICA")) txth->codec = YAMAHA;
else if (0==strcmp(val,"MSADPCM")) txth->codec = MSADPCM; else if (is_string(val,"MSADPCM")) txth->codec = MSADPCM;
else if (0==strcmp(val,"NGC_DSP")) txth->codec = NGC_DSP; else if (is_string(val,"NGC_DSP")) txth->codec = NGC_DSP;
else if (0==strcmp(val,"DSP")) txth->codec = NGC_DSP; else if (is_string(val,"DSP")) txth->codec = NGC_DSP;
else if (0==strcmp(val,"PCM8_U_int")) txth->codec = PCM8_U_int; else if (is_string(val,"PCM8_U_int")) txth->codec = PCM8_U_int;
else if (0==strcmp(val,"PSX_bf")) txth->codec = PSX_bf; else if (is_string(val,"PSX_bf")) txth->codec = PSX_bf;
else if (0==strcmp(val,"MS_IMA")) txth->codec = MS_IMA; else if (is_string(val,"MS_IMA")) txth->codec = MS_IMA;
else if (0==strcmp(val,"PCM8_U")) txth->codec = PCM8_U; else if (is_string(val,"PCM8_U")) txth->codec = PCM8_U;
else if (0==strcmp(val,"APPLE_IMA4")) txth->codec = APPLE_IMA4; else if (is_string(val,"APPLE_IMA4")) txth->codec = APPLE_IMA4;
else if (0==strcmp(val,"ATRAC3")) txth->codec = ATRAC3; else if (is_string(val,"ATRAC3")) txth->codec = ATRAC3;
else if (0==strcmp(val,"ATRAC3PLUS")) txth->codec = ATRAC3PLUS; else if (is_string(val,"ATRAC3PLUS")) txth->codec = ATRAC3PLUS;
else if (0==strcmp(val,"XMA1")) txth->codec = XMA1; else if (is_string(val,"XMA1")) txth->codec = XMA1;
else if (0==strcmp(val,"XMA2")) txth->codec = XMA2; else if (is_string(val,"XMA2")) txth->codec = XMA2;
else if (0==strcmp(val,"FFMPEG")) txth->codec = FFMPEG; else if (is_string(val,"FFMPEG")) txth->codec = FFMPEG;
else if (0==strcmp(val,"AC3")) txth->codec = AC3; else if (is_string(val,"AC3")) txth->codec = AC3;
else if (0==strcmp(val,"PCFX")) txth->codec = PCFX; else if (is_string(val,"PCFX")) txth->codec = PCFX;
else if (0==strcmp(val,"PCM4")) txth->codec = PCM4; else if (is_string(val,"PCM4")) txth->codec = PCM4;
else if (0==strcmp(val,"PCM4_U")) txth->codec = PCM4_U; else if (is_string(val,"PCM4_U")) txth->codec = PCM4_U;
else if (0==strcmp(val,"OKI16")) txth->codec = OKI16; else if (is_string(val,"OKI16")) txth->codec = OKI16;
else if (is_string(val,"AAC")) txth->codec = AAC;
else goto fail; else goto fail;
/* set common interleaves to simplify usage /* set common interleaves to simplify usage
@ -648,31 +727,31 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
} }
} }
} }
else if (0==strcmp(key,"codec_mode")) { else if (is_string(key,"codec_mode")) {
if (!parse_num(txth->streamHead,txth,val, &txth->codec_mode)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->codec_mode)) goto fail;
} }
else if (0==strcmp(key,"value_mul") || 0==strcmp(key,"value_*")) { else if (is_string(key,"value_mul") || is_string(key,"value_*")) {
if (!parse_num(txth->streamHead,txth,val, &txth->value_mul)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->value_mul)) goto fail;
} }
else if (0==strcmp(key,"value_div") || 0==strcmp(key,"value_/")) { else if (is_string(key,"value_div") || is_string(key,"value_/")) {
if (!parse_num(txth->streamHead,txth,val, &txth->value_div)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->value_div)) goto fail;
} }
else if (0==strcmp(key,"value_add") || 0==strcmp(key,"value_+")) { else if (is_string(key,"value_add") || is_string(key,"value_+")) {
if (!parse_num(txth->streamHead,txth,val, &txth->value_add)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->value_add)) goto fail;
} }
else if (0==strcmp(key,"value_sub") || 0==strcmp(key,"value_-")) { else if (is_string(key,"value_sub") || is_string(key,"value_-")) {
if (!parse_num(txth->streamHead,txth,val, &txth->value_sub)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->value_sub)) goto fail;
} }
else if (0==strcmp(key,"id_value")) { else if (is_string(key,"id_value")) {
if (!parse_num(txth->streamHead,txth,val, &txth->id_value)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->id_value)) goto fail;
} }
else if (0==strcmp(key,"id_offset")) { else if (is_string(key,"id_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->id_offset)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->id_offset)) goto fail;
if (txth->id_value != txth->id_offset) /* evaluate current ID */ if (txth->id_value != txth->id_offset) /* evaluate current ID */
goto fail; goto fail;
} }
else if (0==strcmp(key,"interleave")) { else if (is_string(key,"interleave")) {
if (0==strcmp(val,"half_size")) { if (is_string(val,"half_size")) {
if (txth->channels == 0) goto fail; if (txth->channels == 0) goto fail;
txth->interleave = txth->data_size / txth->channels; txth->interleave = txth->data_size / txth->channels;
} }
@ -680,8 +759,8 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
if (!parse_num(txth->streamHead,txth,val, &txth->interleave)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->interleave)) goto fail;
} }
} }
else if (0==strcmp(key,"interleave_last")) { else if (is_string(key,"interleave_last")) {
if (0==strcmp(val,"auto")) { if (is_string(val,"auto")) {
if (txth->channels > 0 && txth->interleave > 0) if (txth->channels > 0 && txth->interleave > 0)
txth->interleave_last = (txth->data_size % (txth->interleave * txth->channels)) / txth->channels; txth->interleave_last = (txth->data_size % (txth->interleave * txth->channels)) / txth->channels;
} }
@ -689,31 +768,31 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
if (!parse_num(txth->streamHead,txth,val, &txth->interleave_last)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->interleave_last)) goto fail;
} }
} }
else if (0==strcmp(key,"channels")) { else if (is_string(key,"channels")) {
if (!parse_num(txth->streamHead,txth,val, &txth->channels)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->channels)) goto fail;
} }
else if (0==strcmp(key,"sample_rate")) { else if (is_string(key,"sample_rate")) {
if (!parse_num(txth->streamHead,txth,val, &txth->sample_rate)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->sample_rate)) goto fail;
} }
else if (0==strcmp(key,"start_offset")) { else if (is_string(key,"start_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->start_offset)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->start_offset)) goto fail;
if (!txth->data_size_set) { if (!txth->data_size_set) {
txth->data_size = !txth->streamBody ? 0 : txth->data_size = !txth->streamBody ? 0 :
get_streamfile_size(txth->streamBody) - txth->start_offset; /* re-evaluate */ get_streamfile_size(txth->streamBody) - txth->start_offset; /* re-evaluate */
} }
} }
else if (0==strcmp(key,"data_size")) { else if (is_string(key,"data_size")) {
if (!parse_num(txth->streamHead,txth,val, &txth->data_size)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->data_size)) goto fail;
txth->data_size_set = 1; txth->data_size_set = 1;
} }
else if (0==strcmp(key,"sample_type")) { else if (is_string(key,"sample_type")) {
if (0==strcmp(val,"samples")) txth->sample_type = 0; if (is_string(val,"samples")) txth->sample_type = 0;
else if (0==strcmp(val,"bytes")) txth->sample_type = 1; else if (is_string(val,"bytes")) txth->sample_type = 1;
else if (0==strcmp(val,"blocks")) txth->sample_type = 2; else if (is_string(val,"blocks")) txth->sample_type = 2;
else goto fail; else goto fail;
} }
else if (0==strcmp(key,"num_samples")) { else if (is_string(key,"num_samples")) {
if (0==strcmp(val,"data_size")) { if (is_string(val,"data_size")) {
txth->num_samples = get_bytes_to_samples(txth, txth->data_size); txth->num_samples = get_bytes_to_samples(txth, txth->data_size);
txth->num_samples_data_size = 1; txth->num_samples_data_size = 1;
} }
@ -725,7 +804,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
txth->num_samples = get_bytes_to_samples(txth, txth->num_samples * (txth->interleave*txth->channels)); txth->num_samples = get_bytes_to_samples(txth, txth->num_samples * (txth->interleave*txth->channels));
} }
} }
else if (0==strcmp(key,"loop_start_sample")) { else if (is_string(key,"loop_start_sample")) {
if (!parse_num(txth->streamHead,txth,val, &txth->loop_start_sample)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->loop_start_sample)) goto fail;
if (txth->sample_type==1) if (txth->sample_type==1)
txth->loop_start_sample = get_bytes_to_samples(txth, txth->loop_start_sample); txth->loop_start_sample = get_bytes_to_samples(txth, txth->loop_start_sample);
@ -734,8 +813,8 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
if (txth->loop_adjust) if (txth->loop_adjust)
txth->loop_start_sample += txth->loop_adjust; txth->loop_start_sample += txth->loop_adjust;
} }
else if (0==strcmp(key,"loop_end_sample")) { else if (is_string(key,"loop_end_sample")) {
if (0==strcmp(val,"data_size")) { if (is_string(val,"data_size")) {
txth->loop_end_sample = get_bytes_to_samples(txth, txth->data_size); txth->loop_end_sample = get_bytes_to_samples(txth, txth->data_size);
} }
else { else {
@ -748,7 +827,7 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
if (txth->loop_adjust) if (txth->loop_adjust)
txth->loop_end_sample += txth->loop_adjust; txth->loop_end_sample += txth->loop_adjust;
} }
else if (0==strcmp(key,"skip_samples")) { else if (is_string(key,"skip_samples")) {
if (!parse_num(txth->streamHead,txth,val, &txth->skip_samples)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->skip_samples)) goto fail;
txth->skip_samples_set = 1; txth->skip_samples_set = 1;
if (txth->sample_type==1) if (txth->sample_type==1)
@ -756,15 +835,15 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
if (txth->sample_type==2) if (txth->sample_type==2)
txth->skip_samples = get_bytes_to_samples(txth, txth->skip_samples * (txth->interleave*txth->channels)); txth->skip_samples = get_bytes_to_samples(txth, txth->skip_samples * (txth->interleave*txth->channels));
} }
else if (0==strcmp(key,"loop_adjust")) { else if (is_string(key,"loop_adjust")) {
if (!parse_num(txth->streamHead,txth,val, &txth->loop_adjust)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->loop_adjust)) goto fail;
if (txth->sample_type==1) if (txth->sample_type==1)
txth->loop_adjust = get_bytes_to_samples(txth, txth->loop_adjust); txth->loop_adjust = get_bytes_to_samples(txth, txth->loop_adjust);
if (txth->sample_type==2) if (txth->sample_type==2)
txth->loop_adjust = get_bytes_to_samples(txth, txth->loop_adjust * (txth->interleave*txth->channels)); txth->loop_adjust = get_bytes_to_samples(txth, txth->loop_adjust * (txth->interleave*txth->channels));
} }
else if (0==strcmp(key,"loop_flag")) { else if (is_string(key,"loop_flag")) {
if (0==strcmp(val,"auto")) { if (is_string(val,"auto")) {
txth->loop_flag_auto = 1; txth->loop_flag_auto = 1;
} }
else { else {
@ -775,49 +854,65 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
} }
} }
} }
else if (0==strcmp(key,"coef_offset")) { else if (is_string(key,"coef_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->coef_offset)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->coef_offset)) goto fail;
} }
else if (0==strcmp(key,"coef_spacing")) { else if (is_string(key,"coef_spacing")) {
if (!parse_num(txth->streamHead,txth,val, &txth->coef_spacing)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->coef_spacing)) goto fail;
} }
else if (0==strcmp(key,"coef_endianness")) { else if (is_string(key,"coef_endianness")) {
if (val[0]=='B' && val[1]=='E') if (is_string(val, "BE"))
txth->coef_big_endian = 1; txth->coef_big_endian = 1;
else if (val[0]=='L' && val[1]=='E') else if (is_string(val, "LE"))
txth->coef_big_endian = 0; txth->coef_big_endian = 0;
else if (!parse_num(txth->streamHead,txth,val, &txth->coef_big_endian)) goto fail; else if (!parse_num(txth->streamHead,txth,val, &txth->coef_big_endian)) goto fail;
} }
else if (0==strcmp(key,"coef_mode")) { else if (is_string(key,"coef_mode")) {
if (!parse_num(txth->streamHead,txth,val, &txth->coef_mode)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->coef_mode)) goto fail;
} }
else if (0==strcmp(key,"psx_loops")) { else if (is_string(key,"coef_table")) {
if (!parse_coef_table(txth->streamHead,txth,val, txth->coef_table, sizeof(txth->coef_table))) goto fail;
txth->coef_table_set = 1;
}
else if (is_string(key,"psx_loops")) {
if (!parse_num(txth->streamHead,txth,val, &txth->coef_mode)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->coef_mode)) goto fail;
} }
else if (0==strcmp(key,"subsong_count")) { else if (is_string(key,"subsong_count")) {
if (!parse_num(txth->streamHead,txth,val, &txth->subsong_count)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->subsong_count)) goto fail;
} }
else if (0==strcmp(key,"subsong_offset")) { else if (is_string(key,"subsong_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->subsong_offset)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->subsong_offset)) goto fail;
} }
else if (0==strcmp(key,"name_offset")) { else if (is_string(key,"name_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->name_offset)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->name_offset)) goto fail;
txth->name_offset_set = 1; txth->name_offset_set = 1;
/* special subsong adjustment */ /* special subsong adjustment */
if (txth->subsong_offset) if (txth->subsong_offset)
txth->name_offset = txth->name_offset + txth->subsong_offset * (txth->target_subsong - 1); txth->name_offset = txth->name_offset + txth->subsong_offset * (txth->target_subsong - 1);
} }
else if (0==strcmp(key,"name_size")) { else if (is_string(key,"name_size")) {
if (!parse_num(txth->streamHead,txth,val, &txth->name_size)) goto fail; if (!parse_num(txth->streamHead,txth,val, &txth->name_size)) goto fail;
} }
else if (0==strcmp(key,"header_file")) { else if (is_string(key,"subfile_offset")) {
if (!parse_num(txth->streamHead,txth,val, &txth->subfile_offset)) goto fail;
txth->subfile_set = 1;
}
else if (is_string(key,"subfile_size")) {
if (!parse_num(txth->streamHead,txth,val, &txth->subfile_size)) goto fail;
txth->subfile_set = 1;
}
else if (is_string(key,"subfile_extension")) {
if (!parse_string(txth->streamHead,txth,val, txth->subfile_extension)) goto fail;
txth->subfile_set = 1;
}
else if (is_string(key,"header_file")) {
if (txth->streamhead_opened) { if (txth->streamhead_opened) {
close_streamfile(txth->streamHead); close_streamfile(txth->streamHead);
txth->streamHead = NULL; txth->streamHead = NULL;
txth->streamhead_opened = 0; txth->streamhead_opened = 0;
} }
if (0==strcmp(val,"null")) { /* reset */ if (is_string(val,"null")) { /* reset */
if (!txth->streamfile_is_txth) { if (!txth->streamfile_is_txth) {
txth->streamHead = txth->streamFile; txth->streamHead = txth->streamFile;
} }
@ -835,14 +930,14 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
txth->streamhead_opened = 1; txth->streamhead_opened = 1;
} }
} }
else if (0==strcmp(key,"body_file")) { else if (is_string(key,"body_file")) {
if (txth->streambody_opened) { if (txth->streambody_opened) {
close_streamfile(txth->streamBody); close_streamfile(txth->streamBody);
txth->streamBody = NULL; txth->streamBody = NULL;
txth->streambody_opened = 0; txth->streambody_opened = 0;
} }
if (0==strcmp(val,"null")) { /* reset */ if (is_string(val,"null")) { /* reset */
if (!txth->streamfile_is_txth) { if (!txth->streamfile_is_txth) {
txth->streamBody = txth->streamFile; txth->streamBody = txth->streamFile;
} }
@ -878,6 +973,72 @@ fail:
return 0; return 0;
} }
static int is_string(const char * val, const char * cmp) {
int len = is_substring(val, cmp);
if (!len) return 0;
/* also test that after string there aren't other values
* (comments are already removed but trailing spaces are allowed) */
while (val[len] != '\0') {
if (val[len] != ' ')
return 0;
len++;
}
return len;
}
static int is_substring(const char * val, const char * cmp) {
int len = strlen(cmp);
if (strncmp(val, cmp, len) != 0)
return 0;
/* string in val must be a full word (end with null or space) to
* avoid mistaking stuff like "interleave" with "interleave_last"
* (could also check , except when used for math */
if (val[len] != '\0' && val[len] != ' ')
return 0;
return len;
}
static int parse_string(STREAMFILE * streamFile, txth_header * txth, const char * val, char * str) {
int n = 0;
/* read string without trailing spaces */
if (sscanf(val, " %s%n[^ ]%n", str, &n, &n) != 1)
return 0;
return n;
}
static int parse_coef_table(STREAMFILE * streamFile, txth_header * txth, const char * val, uint8_t * out_value, size_t out_size) {
uint32_t byte;
int done = 0;
/* read 2 char pairs = 1 byte ('N' 'N' 'M' 'M' = 0xNN 0xMM )*/
while (val[0] != '\0') {
if (val[0] == ' ') {
val++;
continue;
}
if (val[0] == '0' && val[1] == 'x') /* allow "0x" before values */
val += 2;
if (sscanf(val, " %2x", &byte) != 1)
goto fail;
if (done + 1 >= out_size)
goto fail;
out_value[done] = (uint8_t)byte;
done++;
val += 2;
}
return 1;
fail:
return 0;
}
static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * val, uint32_t * out_value) { static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * val, uint32_t * out_value) {
/* out_value can be these, save before modifying */ /* out_value can be these, save before modifying */
uint32_t value_mul = txth->value_mul; uint32_t value_mul = txth->value_mul;
@ -886,91 +1047,148 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v
uint32_t value_sub = txth->value_sub; uint32_t value_sub = txth->value_sub;
uint32_t subsong_offset = txth->subsong_offset; uint32_t subsong_offset = txth->subsong_offset;
if (val[0] == '@') { /* offset */ char op = ' ';
uint32_t offset = 0; int brackets = 0;
char ed1 = 'L', ed2 = 'E'; uint32_t result = 0;
int size = 4;
int big_endian = 0;
int hex = (val[1]=='0' && val[2]=='x');
/* can happen when loading .txth and not setting body/head */ //;VGM_LOG("TXTH: initial val '%s'\n", val);
if (!streamFile)
goto fail;
/* read exactly N fields in the expected format */
if (strchr(val,':') && strchr(val,'$')) { /* read "val" format: @(offset) (op) (field) (op) (number) ... */
if (sscanf(val, hex ? "@%x:%c%c$%i" : "@%u:%c%c$%i", &offset, &ed1,&ed2, &size) != 4) goto fail; while (val[0] != '\0') {
} else if (strchr(val,':')) { uint32_t value = 0;
if (sscanf(val, hex ? "@%x:%c%c" : "@%u:%c%c", &offset, &ed1,&ed2) != 3) goto fail; char type = val[0];
} else if (strchr(val,'$')) { int value_read = 0;
if (sscanf(val, hex ? "@%x$%i" : "@%u$%i", &offset, &size) != 2) goto fail; int n = 0;
} else {
if (sscanf(val, hex ? "@%x" : "@%u", &offset) != 1) goto fail; if (type == ' ') { /* ignore */
n = 1;
}
else if (type == '(') { /* bracket */
brackets++;
n = 1;
}
else if (type == ')') { /* bracket */
if (brackets == 0) goto fail;
brackets--;
n = 1;
}
else if (type == '+' || type == '-' || type == '/' || type == '*') { /* op */
op = type;
n = 1;
}
else if (type == '@') { /* offset */
uint32_t offset = 0;
char ed1 = 'L', ed2 = 'E';
int size = 4;
int big_endian = 0;
int hex = (val[1]=='0' && val[2]=='x');
/* can happen when loading .txth and not setting body/head */
if (!streamFile)
goto fail;
/* read exactly N fields in the expected format */
if (strchr(val,':') && strchr(val,'$')) {
if (sscanf(val, hex ? "@%x:%c%c$%i%n" : "@%u:%c%c$%i%n", &offset, &ed1,&ed2, &size, &n) != 4) goto fail;
} else if (strchr(val,':')) {
if (sscanf(val, hex ? "@%x:%c%c%n" : "@%u:%c%c%n", &offset, &ed1,&ed2, &n) != 3) goto fail;
} else if (strchr(val,'$')) {
if (sscanf(val, hex ? "@%x$%i%n" : "@%u$%i%n", &offset, &size, &n) != 2) goto fail;
} else {
if (sscanf(val, hex ? "@%x%n" : "@%u%n", &offset, &n) != 1) goto fail;
}
if (/*offset < 0 ||*/ offset > get_streamfile_size(streamFile))
goto fail;
if (ed1 == 'B' && ed2 == 'E')
big_endian = 1;
else if (!(ed1 == 'L' && ed2 == 'E'))
goto fail;
if (subsong_offset)
offset = offset + subsong_offset * (txth->target_subsong - 1);
switch(size) {
case 1: value = read_8bit(offset,streamFile); break;
case 2: value = big_endian ? (uint16_t)read_16bitBE(offset,streamFile) : (uint16_t)read_16bitLE(offset,streamFile); break;
case 3: value = (big_endian ? (uint32_t)read_32bitBE(offset,streamFile) : (uint32_t)read_32bitLE(offset,streamFile)) & 0x00FFFFFF; break;
case 4: value = big_endian ? (uint32_t)read_32bitBE(offset,streamFile) : (uint32_t)read_32bitLE(offset,streamFile); break;
default: goto fail;
}
value_read = 1;
}
else if (type >= '0' && type <= '9') { /* unsigned constant */
int hex = (val[0]=='0' && val[1]=='x');
if (sscanf(val, hex ? "%x%n" : "%u%n", &value, &n) != 1)
goto fail;
value_read = 1;
}
else { /* known field */
if ((n = is_substring(val,"interleave"))) value = txth->interleave;
if ((n = is_substring(val,"interleave_last"))) value = txth->interleave_last;
else if ((n = is_substring(val,"channels"))) value = txth->channels;
else if ((n = is_substring(val,"sample_rate"))) value = txth->sample_rate;
else if ((n = is_substring(val,"start_offset"))) value = txth->start_offset;
else if ((n = is_substring(val,"data_size"))) value = txth->data_size;
else if ((n = is_substring(val,"num_samples"))) value = txth->num_samples;
else if ((n = is_substring(val,"loop_start_sample"))) value = txth->loop_start_sample;
else if ((n = is_substring(val,"loop_end_sample"))) value = txth->loop_end_sample;
else if ((n = is_substring(val,"subsong_count"))) value = txth->subsong_count;
else if ((n = is_substring(val,"subsong_offset"))) value = txth->subsong_offset;
else goto fail;
value_read = 1;
} }
if (/*offset < 0 ||*/ offset > get_streamfile_size(streamFile)) /* apply simple left-to-right math though, for now "(" ")" are counted and validated
goto fail; * (could use good ol' shunting-yard algo but whatevs) */
if (value_read) {
//;VGM_ASSERT(op != ' ', "MIX: %i %c %i\n", result, op, value);
switch(op) {
case '+': value = result + value; break;
case '-': value = result - value; break;
case '*': value = result * value; break;
case '/': if (value == 0) goto fail; value = result / value; break;
default: break;
}
op = ' '; /* consume */
if (ed1 == 'B' && ed2 == 'E') result = value;
big_endian = 1;
else if (!(ed1 == 'L' && ed2 == 'E'))
goto fail;
if (subsong_offset)
offset = offset + subsong_offset * (txth->target_subsong - 1);
switch(size) {
case 1: *out_value = read_8bit(offset,streamFile); break;
case 2: *out_value = big_endian ? (uint16_t)read_16bitBE(offset,streamFile) : (uint16_t)read_16bitLE(offset,streamFile); break;
case 3: *out_value = (big_endian ? (uint32_t)read_32bitBE(offset,streamFile) : (uint32_t)read_32bitLE(offset,streamFile)) & 0x00FFFFFF; break;
case 4: *out_value = big_endian ? (uint32_t)read_32bitBE(offset,streamFile) : (uint32_t)read_32bitLE(offset,streamFile); break;
default: goto fail;
} }
}
else if (val[0] >= '0' && val[0] <= '9') { /* unsigned constant */
int hex = (val[0]=='0' && val[1]=='x');
if (sscanf(val, hex ? "%x" : "%u", out_value)!=1) /* move to next field (if any) */
goto fail; val += n;
}
else { /* known field */ //;VGM_LOG("TXTH: val='%s', n=%i, brackets=%i, result=%i\n", val, n, brackets, result);
if (0==strcmp(val,"interleave")) *out_value = txth->interleave;
if (0==strcmp(val,"interleave_last")) *out_value = txth->interleave_last;
else if (0==strcmp(val,"channels")) *out_value = txth->channels;
else if (0==strcmp(val,"sample_rate")) *out_value = txth->sample_rate;
else if (0==strcmp(val,"start_offset")) *out_value = txth->start_offset;
else if (0==strcmp(val,"data_size")) *out_value = txth->data_size;
else if (0==strcmp(val,"num_samples")) *out_value = txth->num_samples;
else if (0==strcmp(val,"loop_start_sample")) *out_value = txth->loop_start_sample;
else if (0==strcmp(val,"loop_end_sample")) *out_value = txth->loop_end_sample;
else if (0==strcmp(val,"subsong_count")) *out_value = txth->subsong_count;
else if (0==strcmp(val,"subsong_offset")) *out_value = txth->subsong_offset;
else goto fail;
} }
/* operators, but only if current value wasn't set to 0 right before */ /* unbalanced brackets */
if (brackets > 0)
goto fail;
/* global operators, but only if current value wasn't set to 0 right before */
if (value_mul && txth->value_mul) if (value_mul && txth->value_mul)
*out_value = (*out_value) * value_mul; result = result * value_mul;
if (value_div && txth->value_div) if (value_div && txth->value_div)
*out_value = (*out_value) / value_div; result = result / value_div;
if (value_add && txth->value_add) if (value_add && txth->value_add)
*out_value = (*out_value) + value_add; result = result + value_add;
if (value_sub && txth->value_sub) if (value_sub && txth->value_sub)
*out_value = (*out_value) - value_sub; result = result - value_sub;
//;VGM_LOG("TXTH: val=%s, read %u (0x%x)\n", val, *out_value, *out_value); *out_value = result;
//;VGM_LOG("TXTH: final result %u (0x%x)\n", result, result);
return 1; return 1;
fail: fail:
return 0; return 0;
} }
static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) { static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
if (!txth->channels)
return 0; /* div-by-zero is no fun */
switch(txth->codec) { switch(txth->codec) {
case MS_IMA: case MS_IMA:
if (!txth->interleave) return 0;
return ms_ima_bytes_to_samples(bytes, txth->interleave, txth->channels); return ms_ima_bytes_to_samples(bytes, txth->interleave, txth->channels);
case XBOX: case XBOX:
return xbox_ima_bytes_to_samples(bytes, txth->channels); return xbox_ima_bytes_to_samples(bytes, txth->channels);
@ -990,24 +1208,23 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
case PCM4_U: case PCM4_U:
return pcm_bytes_to_samples(bytes, txth->channels, 4); return pcm_bytes_to_samples(bytes, txth->channels, 4);
case MSADPCM: case MSADPCM:
if (!txth->interleave) return 0;
return msadpcm_bytes_to_samples(bytes, txth->interleave, txth->channels); return msadpcm_bytes_to_samples(bytes, txth->interleave, txth->channels);
case ATRAC3: case ATRAC3:
if (!txth->interleave) return 0;
return atrac3_bytes_to_samples(bytes, txth->interleave); return atrac3_bytes_to_samples(bytes, txth->interleave);
case ATRAC3PLUS: case ATRAC3PLUS:
if (!txth->interleave) return 0;
return atrac3plus_bytes_to_samples(bytes, txth->interleave); return atrac3plus_bytes_to_samples(bytes, txth->interleave);
case AAC:
return aac_get_samples(txth->streamBody, txth->start_offset, bytes);
case MPEG:
return mpeg_get_samples(txth->streamBody, txth->start_offset, bytes);
case AC3:
return ac3_bytes_to_samples(bytes, txth->interleave, txth->channels);
/* XMA bytes-to-samples is done at the end as the value meanings are a bit different */ /* XMA bytes-to-samples is done at the end as the value meanings are a bit different */
case XMA1: case XMA1:
case XMA2: case XMA2:
return bytes; /* preserve */ return bytes; /* preserve */
case AC3:
if (!txth->interleave) return 0;
return bytes / txth->interleave * 256 * txth->channels;
case IMA: case IMA:
case DVI_IMA: case DVI_IMA:
return ima_bytes_to_samples(bytes, txth->channels); return ima_bytes_to_samples(bytes, txth->channels);
@ -1026,7 +1243,6 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
if (!txth->interleave) return 0; if (!txth->interleave) return 0;
return (bytes / txth->interleave) * (txth->interleave - 2) * 2; return (bytes / txth->interleave) * (txth->interleave - 2) * 2;
case MPEG: /* a bit complex */
case FFMPEG: /* too complex, try after init */ case FFMPEG: /* too complex, try after init */
default: default:
return 0; return 0;

View File

@ -1,9 +1,61 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#ifdef VGMSTREAM_MIXING
#include "../mixing.h"
#endif
#define TXTP_LINE_MAX 1024 #define TXTP_LINE_MAX 1024
#define TXTP_MIXING_MAX 128
#ifdef VGMSTREAM_MIXING
/* mixing info */
typedef enum {
MIX_SWAP,
MIX_ADD,
MIX_ADD_VOLUME,
MIX_VOLUME,
MIX_LIMIT,
MIX_DOWNMIX,
MIX_KILLMIX,
MIX_UPMIX,
MIX_FADE,
MACRO_VOLUME,
MACRO_TRACK,
MACRO_LAYER,
MACRO_CROSSTRACK,
MACRO_CROSSLAYER,
} txtp_mix_t;
typedef struct {
txtp_mix_t command;
/* common */
int ch_dst;
int ch_src;
double vol;
/* fade envelope */
double vol_start;
double vol_end;
char shape;
int32_t sample_pre;
int32_t sample_start;
int32_t sample_end;
int32_t sample_post;
double time_pre;
double time_start;
double time_end;
double time_post;
/* macros */
int max;
uint32_t mask;
int overlap;
} txtp_mix_data;
#endif
typedef struct { typedef struct {
@ -16,7 +68,7 @@ typedef struct {
#endif #endif
#ifdef VGMSTREAM_MIXING #ifdef VGMSTREAM_MIXING
int mixing_count; int mixing_count;
mix_config_data mixing[VGMSTREAM_MAX_MIXING]; txtp_mix_data mixing[TXTP_MIXING_MAX];
#endif #endif
double config_loop_count; double config_loop_count;
@ -55,7 +107,7 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile);
static void clean_txtp(txtp_header* txtp); static void clean_txtp(txtp_header* txtp);
static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current); static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current);
#ifdef VGMSTREAM_MIXING #ifdef VGMSTREAM_MIXING
void add_mixing(txtp_entry* cfg, mix_config_data* mix, mix_command_t command); void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command);
#endif #endif
/* TXTP - an artificial playlist-like format to play files with segments/layers/config */ /* TXTP - an artificial playlist-like format to play files with segments/layers/config */
@ -232,10 +284,10 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
if (current->loop_install) { if (current->loop_install) {
if (current->loop_start_second > 0 || current->loop_end_second > 0) { if (current->loop_start_second > 0 || current->loop_end_second > 0) {
current->loop_start_sample = current->loop_start_second * (double)vgmstream->sample_rate; current->loop_start_sample = current->loop_start_second * vgmstream->sample_rate;
current->loop_end_sample = current->loop_end_second * (double)vgmstream->sample_rate; current->loop_end_sample = current->loop_end_second * vgmstream->sample_rate;
if (current->loop_end_sample > vgmstream->num_samples && if (current->loop_end_sample > vgmstream->num_samples &&
current->loop_end_sample - vgmstream->num_samples <= 0.1 * (double)vgmstream->sample_rate) current->loop_end_sample - vgmstream->num_samples <= 0.1 * vgmstream->sample_rate)
current->loop_end_sample = vgmstream->num_samples; /* allow some rounding leeway */ current->loop_end_sample = vgmstream->num_samples; /* allow some rounding leeway */
} }
@ -252,7 +304,7 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
int ch; int ch;
for (ch = 0; ch < vgmstream->channels; ch++) { for (ch = 0; ch < vgmstream->channels; ch++) {
if (!((current->channel_mask >> ch) & 1)) { if (!((current->channel_mask >> ch) & 1)) {
mix_config_data mix = {0}; txtp_mix_data mix = {0};
mix.ch_dst = ch; mix.ch_dst = ch;
mix.vol = 0.0f; mix.vol = 0.0f;
add_mixing(current, &mix, MIX_VOLUME); add_mixing(current, &mix, MIX_VOLUME);
@ -262,10 +314,47 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
/* copy mixing list (should be done last as some mixes depend on config) */ /* copy mixing list (should be done last as some mixes depend on config) */
if (current->mixing_count > 0) { if (current->mixing_count > 0) {
int i; int m;
for (i = 0; i < current->mixing_count; i++) { for (m = 0; m < current->mixing_count; m++) {
vgmstream_add_mixing(vgmstream, current->mixing[i]); txtp_mix_data mix = current->mixing[m];
switch(mix.command) {
/* base mixes */
case MIX_SWAP: mixing_push_swap(vgmstream, mix.ch_dst, mix.ch_src); break;
case MIX_ADD: mixing_push_add(vgmstream, mix.ch_dst, mix.ch_src, 1.0); break;
case MIX_ADD_VOLUME: mixing_push_add(vgmstream, mix.ch_dst, mix.ch_src, mix.vol); break;
case MIX_VOLUME: mixing_push_volume(vgmstream, mix.ch_dst, mix.vol); break;
case MIX_LIMIT: mixing_push_limit(vgmstream, mix.ch_dst, mix.vol); break;
case MIX_UPMIX: mixing_push_upmix(vgmstream, mix.ch_dst); break;
case MIX_DOWNMIX: mixing_push_downmix(vgmstream, mix.ch_dst); break;
case MIX_KILLMIX: mixing_push_killmix(vgmstream, mix.ch_dst); break;
case MIX_FADE:
/* Convert from time to samples now that sample rate is final.
* Samples and time values may be mixed though, so it's done for every
* value (if one is 0 the other will be too, though) */
if (mix.time_pre > 0.0) mix.sample_pre = mix.time_pre * vgmstream->sample_rate;
if (mix.time_start > 0.0) mix.sample_start = mix.time_start * vgmstream->sample_rate;
if (mix.time_end > 0.0) mix.sample_end = mix.time_end * vgmstream->sample_rate;
if (mix.time_post > 0.0) mix.sample_post = mix.time_post * vgmstream->sample_rate;
/* convert special meaning too */
if (mix.time_pre < 0.0) mix.sample_pre = -1;
if (mix.time_post < 0.0) mix.sample_post = -1;
mixing_push_fade(vgmstream, mix.ch_dst, mix.vol_start, mix.vol_end, mix.shape,
mix.sample_pre, mix.sample_start, mix.sample_end, mix.sample_post);
break;
/* macro mixes */
case MACRO_VOLUME: mixing_macro_volume(vgmstream, mix.vol, mix.mask); break;
case MACRO_TRACK: mixing_macro_track(vgmstream, mix.mask); break;
case MACRO_LAYER: mixing_macro_layer(vgmstream, mix.max, mix.mask, mix.overlap); break;
case MACRO_CROSSTRACK: mixing_macro_crosstrack(vgmstream, mix.max); break;
case MACRO_CROSSLAYER: mixing_macro_crosslayer(vgmstream, mix.max); break;
default:
break;
}
} }
} }
#endif #endif
@ -357,13 +446,24 @@ static int get_time(const char * config, double *value_f, int32_t *value_i) {
return n; return n;
} }
/* test is format is hex samples: 0xN */
m = sscanf(config, " 0x%x%n", &temp_i1,&n);
if (m == 1) {
/* allow negative samples for special meanings */
//if (temp_i1 < 0)
// return 0;
*value_i = temp_i1;
return n;
}
/* assume format is samples: N */ /* assume format is samples: N */
m = sscanf(config, " %i%n", &temp_i1,&n); m = sscanf(config, " %i%n", &temp_i1,&n);
if (m == 1) { if (m == 1) {
if (temp_i1 < 0) /* allow negative samples for special meanings */
return 0; //if (temp_i1 < 0)
// return 0;
//*is_time_i = 1;
*value_i = temp_i1; *value_i = temp_i1;
return n; return n;
} }
@ -431,31 +531,107 @@ static int get_mask(const char * config, uint32_t *value) {
#ifdef VGMSTREAM_MIXING #ifdef VGMSTREAM_MIXING
static int get_fade(const char * config, mix_config_data *mix, int *out_n) { static int get_fade(const char * config, txtp_mix_data *mix, int *out_n) {
int n, m; int n, m, tn = 0;
char type, separator;
//todo add { } shortcuts / time / etc m = sscanf(config, " %d %c%n", &mix->ch_dst, &type, &n);
if (n == 0 || m != 2) goto fail;
config += n;
tn += n;
m = sscanf(config, " %d ^ %f ~ %f = %c @ %f ~ %f + %f ~ %f%n", if (type == '^') {
&mix->ch_dst, /* full definition */
&mix->vol_start, &mix->vol_end, &mix->shape, m = sscanf(config, " %lf ~ %lf = %c @%n", &mix->vol_start, &mix->vol_end, &mix->shape, &n);
&mix->time_pre, &mix->time_start, &mix->time_end, &mix->time_post, if (n == 0 || m != 3) goto fail;
&n); config += n;
tn += n;
VGM_LOG("curve m=%i, n=%i\n", m,n); n = get_time(config, &mix->time_pre, &mix->sample_pre);
if (m == 8 && n != 0) { if (n == 0) goto fail;
mix->time_end += mix->time_start; config += n;
*out_n = n; tn += n;
return 1;
m = sscanf(config, " %c%n", &separator, &n);
if (n == 0 || m != 1 || separator != '~') goto fail;
config += n;
tn += n;
n = get_time(config, &mix->time_start, &mix->sample_start);
if (n == 0) goto fail;
config += n;
tn += n;
m = sscanf(config, " %c%n", &separator, &n);
if (n == 0 || m != 1 || separator != '+') goto fail;
config += n;
tn += n;
n = get_time(config, &mix->time_end, &mix->sample_end);
if (n == 0) goto fail;
config += n;
tn += n;
m = sscanf(config, " %c%n", &separator, &n);
if (n == 0 || m != 1 || separator != '~') goto fail;
config += n;
tn += n;
n = get_time(config, &mix->time_post, &mix->sample_post);
if (n == 0) goto fail;
config += n;
tn += n;
}
else {
/* simplified definition */
if (type == '{' || type == '(') {
mix->vol_start = 0.0;
mix->vol_end = 1.0;
}
else if (type == '}' || type == ')') {
mix->vol_start = 1.0;
mix->vol_end = 0.0;
}
else {
goto fail;
}
mix->shape = type; /* internally converted */
mix->time_pre = -1.0;
mix->sample_pre = -1;
n = get_time(config, &mix->time_start, &mix->sample_start);
if (n == 0) goto fail;
config += n;
tn += n;
m = sscanf(config, " %c%n", &separator, &n);
if (n == 0 || m != 1 || separator != '+') goto fail;
config += n;
tn += n;
n = get_time(config, &mix->time_end, &mix->sample_end);
if (n == 0) goto fail;
config += n;
tn += n;
mix->time_post = -1.0;
mix->sample_post = -1;
} }
mix->time_end = mix->time_start + mix->time_end; /* defined as length */
*out_n = tn;
return 1;
fail:
return 0; return 0;
} }
#endif #endif
#ifdef VGMSTREAM_MIXING #ifdef VGMSTREAM_MIXING
void add_mixing(txtp_entry* cfg, mix_config_data* mix, mix_command_t command) { void add_mixing(txtp_entry* cfg, txtp_mix_data* mix, txtp_mix_t command) {
if (cfg->mixing_count + 1 > VGMSTREAM_MAX_MIXING) { if (cfg->mixing_count + 1 > TXTP_MIXING_MAX) {
VGM_LOG("TXTP: too many mixes\n"); VGM_LOG("TXTP: too many mixes\n");
return; return;
} }
@ -465,6 +641,7 @@ void add_mixing(txtp_entry* cfg, mix_config_data* mix, mix_command_t command) {
mix->ch_dst--; mix->ch_dst--;
mix->ch_src--; mix->ch_src--;
mix->command = command; mix->command = command;
cfg->mixing[cfg->mixing_count] = *mix; /* memcpy'ed */ cfg->mixing[cfg->mixing_count] = *mix; /* memcpy'ed */
cfg->mixing_count++; cfg->mixing_count++;
} }
@ -516,7 +693,7 @@ static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filenam
} }
static int add_filename(txtp_header * txtp, char *filename, int is_default) { static int add_filename(txtp_header * txtp, char *filename, int is_default) {
int i, n, nc, mc; int i, n, nc, nm, mc;
txtp_entry cfg = {0}; txtp_entry cfg = {0};
size_t range_start, range_end; size_t range_start, range_end;
char command[TXTP_LINE_MAX] = {0}; char command[TXTP_LINE_MAX] = {0};
@ -596,10 +773,11 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
char cmd; char cmd;
while (config[0] != '\0') { while (config[0] != '\0') {
mix_config_data mix = {0}; txtp_mix_data mix = {0};
//;VGM_LOG("TXTP: subcommand='%s'\n", config); //;VGM_LOG("TXTP: subcommand='%s'\n", config);
//todo use strchr instead?
if (sscanf(config, " %c%n", &cmd, &n) == 1 && n != 0 && cmd == ',') { if (sscanf(config, " %c%n", &cmd, &n) == 1 && n != 0 && cmd == ',') {
config += n; config += n;
continue; continue;
@ -612,8 +790,8 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
continue; continue;
} }
if ((sscanf(config, " %d + %d * %f%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0) || if ((sscanf(config, " %d + %d * %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0) ||
(sscanf(config, " %d + %d x %f%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0)) { (sscanf(config, " %d + %d x %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0)) {
//;VGM_LOG("TXTP: mix %i+%i*%f\n", mix.ch_dst, mix.ch_src, mix.vol); //;VGM_LOG("TXTP: mix %i+%i*%f\n", mix.ch_dst, mix.ch_src, mix.vol);
add_mixing(&cfg, &mix, MIX_ADD_VOLUME); /* N+M*V: mixes M*volume to N */ add_mixing(&cfg, &mix, MIX_ADD_VOLUME); /* N+M*V: mixes M*volume to N */
config += n; config += n;
@ -627,15 +805,15 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
continue; continue;
} }
if ((sscanf(config, " %d * %f%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0) || if ((sscanf(config, " %d * %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0) ||
(sscanf(config, " %d x %f%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) { (sscanf(config, " %d x %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) {
//;VGM_LOG("TXTP: mix %i*%f\n", mix.ch_dst, mix.vol); //;VGM_LOG("TXTP: mix %i*%f\n", mix.ch_dst, mix.vol);
add_mixing(&cfg, &mix, MIX_VOLUME); /* N*V: changes volume of N */ add_mixing(&cfg, &mix, MIX_VOLUME); /* N*V: changes volume of N */
config += n; config += n;
continue; continue;
} }
if ((sscanf(config, " %d = %f%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) { if ((sscanf(config, " %d = %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) {
//;VGM_LOG("TXTP: mix %i=%f\n", mix.ch_dst, mix.vol); //;VGM_LOG("TXTP: mix %i=%f\n", mix.ch_dst, mix.vol);
add_mixing(&cfg, &mix, MIX_LIMIT); /* N=V: limits volume of N */ add_mixing(&cfg, &mix, MIX_LIMIT); /* N=V: limits volume of N */
config += n; config += n;
@ -644,7 +822,7 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
if (sscanf(config, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'D') { if (sscanf(config, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'D') {
//;VGM_LOG("TXTP: mix %iD\n", mix.ch_dst); //;VGM_LOG("TXTP: mix %iD\n", mix.ch_dst);
add_mixing(&cfg, &mix, MIX_DOWNMIX_REST); /* ND: downmix N and all following channels */ add_mixing(&cfg, &mix, MIX_KILLMIX); /* ND: downmix N and all following channels */
config += n; config += n;
continue; continue;
} }
@ -744,6 +922,58 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
//;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", cfg.loop_install, cfg.loop_end_max, //;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", cfg.loop_install, cfg.loop_end_max,
// cfg.loop_start_sample, cfg.loop_end_sample, cfg.loop_start_second, cfg.loop_end_second); // cfg.loop_start_sample, cfg.loop_end_sample, cfg.loop_start_second, cfg.loop_end_second);
} }
#ifdef VGMSTREAM_MIXING
//todo cleanup
else if (strcmp(command,"@volume") == 0) {
txtp_mix_data mix = {0};
nm = get_double(config, &mix.vol);
config += nm;
if (nm == 0) continue;
nm = get_mask(config, &mix.mask);
config += nm;
add_mixing(&cfg, &mix, MACRO_VOLUME);
}
else if (strcmp(command,"@track") == 0) {
txtp_mix_data mix = {0};
nm = get_mask(config, &mix.mask);
config += nm;
if (nm == 0) continue;
add_mixing(&cfg, &mix, MACRO_TRACK);
}
else if (strcmp(command,"@layer") == 0 || strcmp(command,"@overlap") == 0) {
txtp_mix_data mix = {0};
nm = get_int(config, &mix.max);
config += nm;
if (nm == 0) continue;
nm = get_mask(config, &mix.mask);
config += nm;
mix.overlap = (strcmp(command,"@overlap") == 0);
add_mixing(&cfg, &mix, MACRO_LAYER);
}
else if (strcmp(command,"@crosslayer") == 0 || strcmp(command,"@crosstrack") == 0) {
txtp_mix_data mix = {0};
txtp_mix_t type;
if (strcmp(command,"@crosstrack") == 0)
type = MACRO_CROSSTRACK;
else
type = MACRO_CROSSLAYER;
nm = get_int(config, &mix.max);
config += nm;
if (nm == 0) continue;
add_mixing(&cfg, &mix, type);
}
#endif
else if (config[nc] == ' ') { else if (config[nc] == ' ') {
//;VGM_LOG("TXTP: comment\n"); //;VGM_LOG("TXTP: comment\n");
break; /* comment, ignore rest */ break; /* comment, ignore rest */

View File

@ -233,7 +233,7 @@ VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) {
temp_streamFile = setup_subfile_streamfile(streamFile, start_offset, data_size, "msf"); temp_streamFile = setup_subfile_streamfile(streamFile, start_offset, data_size, "msf");
if (!temp_streamFile) goto fail; if (!temp_streamFile) goto fail;
temp_vgmstream = init_vgmstream_ps3_msf(temp_streamFile); temp_vgmstream = init_vgmstream_msf(temp_streamFile);
close_streamfile(temp_streamFile); close_streamfile(temp_streamFile);
if (!temp_vgmstream) goto fail; if (!temp_vgmstream) goto fail;

View File

@ -118,9 +118,16 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
ww.bits_per_sample = (uint16_t)read_16bit(ww.fmt_offset+0x0e,streamFile); ww.bits_per_sample = (uint16_t)read_16bit(ww.fmt_offset+0x0e,streamFile);
if (ww.fmt_size > 0x10 && ww.format != 0x0165 && ww.format != 0x0166) /* ignore XMAWAVEFORMAT */ if (ww.fmt_size > 0x10 && ww.format != 0x0165 && ww.format != 0x0166) /* ignore XMAWAVEFORMAT */
ww.extra_size = (uint16_t)read_16bit(ww.fmt_offset+0x10,streamFile); ww.extra_size = (uint16_t)read_16bit(ww.fmt_offset+0x10,streamFile);
if (ww.extra_size >= 0x06) { /* mostly WAVEFORMATEXTENSIBLE's bitmask, see AkSpeakerConfig.h */ if (ww.extra_size >= 0x06) { /* always present (actual RIFFs only have it in WAVEFORMATEXTENSIBLE) */
/* always present (actual RIFFs only have it in WAVEFORMATEXTENSIBLE) */ /* mostly WAVEFORMATEXTENSIBLE's bitmask (see AkSpeakerConfig.h) */
ww.channel_layout = read_32bit(ww.fmt_offset+0x14,streamFile); ww.channel_layout = read_32bit(ww.fmt_offset+0x14,streamFile);
/* latest games have a pseudo-format instead to handle more cases:
* - 8b: uNumChannels
* - 4b: eConfigType (0=none, 1=standard, 2=ambisonic)
* - 19b: uChannelMask */
if ((ww.channel_layout & 0xFF) == ww.channels) {
ww.channel_layout = (ww.channel_layout >> 12);
}
} }
} }

86
src/meta/xwma_konami.c Normal file
View File

@ -0,0 +1,86 @@
#include "meta.h"
#include "../coding/coding.h"
#include "xwma_konami_streamfile.h"
/* MSFC - Konami (Armature?) variation [Metal Gear Solid 2 HD (X360), Metal Gear Solid 3 HD (X360)] */
VGMSTREAM * init_vgmstream_xwma_konami(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, codec, sample_rate;
size_t data_size;
STREAMFILE *temp_streamFile = NULL;
/* checks */
if (!check_extensions(streamFile,"xwma"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x58574D41) /* "XWMA" */
goto fail;
codec = read_32bitBE(0x04,streamFile);
channel_count = read_32bitBE(0x08,streamFile);
sample_rate = read_32bitBE(0x0c,streamFile);
data_size = read_32bitBE(0x10,streamFile); /* data size without padding */
loop_flag = 0;
start_offset = 0x20;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->meta_type = meta_XWMA_KONAMI;
#ifdef VGM_USE_FFMPEG
{
uint8_t buf[0x100];
int bytes, avg_bps, block_align;
/* 0x10: related to size? */
avg_bps = read_32bitBE(0x14, streamFile);
block_align = read_32bitBE(0x18, streamFile);
/* data has padding (unrelated to KCEJ blocks) */
temp_streamFile = setup_xwma_konami_streamfile(streamFile, start_offset, block_align);
if (!temp_streamFile) goto fail;
bytes = ffmpeg_make_riff_xwma(buf,0x100, codec, data_size, channel_count, sample_rate, avg_bps, block_align);
vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00,data_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
/* manually find total samples */
{
ms_sample_data msd = {0};
msd.channels = vgmstream->channels;
msd.data_offset = 0x00;
msd.data_size = data_size;
if (codec == 0x0161)
wma_get_samples(&msd, temp_streamFile, block_align, vgmstream->sample_rate,0x001F);
//else //todo not correct
// wmapro_get_samples(&msd, temp_streamFile, block_align, vgmstream->sample_rate,0x00E0);
vgmstream->num_samples = msd.num_samples;
if (vgmstream->num_samples == 0)
vgmstream->num_samples = (int32_t)((ffmpeg_codec_data*)vgmstream->codec_data)->totalSamples; /* from avg-br */
//num_samples seem to be found in the last "seek" table entry too, as: entry / channels / 2
}
}
#else
goto fail;
#endif
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -0,0 +1,125 @@
#ifndef _XWMA_KONAMI_STREAMFILE_H_
#define _XWMA_KONAMI_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
/* config */
off_t stream_offset;
size_t stream_size;
size_t block_align;
/* state */
off_t logical_offset; /* fake offset */
off_t physical_offset; /* actual offset */
size_t block_size; /* current size */
size_t skip_size; /* size from block start to reach data */
size_t data_size; /* usable size in a block */
size_t logical_size;
} xwma_konami_io_data;
static size_t xwma_konami_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, xwma_konami_io_data* data) {
size_t total_read = 0;
/* re-start when previous offset (can't map logical<>physical offsets) */
if (data->logical_offset < 0 || offset < data->logical_offset) {
data->physical_offset = data->stream_offset;
data->logical_offset = 0x00;
data->data_size = 0;
}
/* read blocks */
while (length > 0) {
/* ignore EOF */
if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
break;
}
/* process new block */
if (data->data_size == 0) {
data->block_size = align_size_to_block(data->block_align, 0x10);
data->data_size = data->block_align;
data->skip_size = 0x00;
}
/* move to next block */
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
data->physical_offset += data->block_size;
data->logical_offset += data->data_size;
data->data_size = 0;
continue;
}
/* read data */
{
size_t bytes_consumed, bytes_done, to_read;
bytes_consumed = offset - data->logical_offset;
to_read = data->data_size - bytes_consumed;
if (to_read > length)
to_read = length;
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
total_read += bytes_done;
dest += bytes_done;
offset += bytes_done;
length -= bytes_done;
if (bytes_done != to_read || bytes_done == 0) {
break; /* error/EOF */
}
}
}
return total_read;
}
static size_t xwma_konami_io_size(STREAMFILE *streamfile, xwma_konami_io_data* data) {
uint8_t buf[1];
if (data->logical_size)
return data->logical_size;
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
xwma_konami_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
data->logical_size = data->logical_offset;
return data->logical_size;
}
/* Handles de-padding Konami XWMA blocked streams */
static STREAMFILE* setup_xwma_konami_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t block_align) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
xwma_konami_io_data io_data = {0};
size_t io_data_size = sizeof(xwma_konami_io_data);
io_data.stream_offset = stream_offset;
io_data.stream_size = get_streamfile_size(streamFile) - stream_offset;
io_data.block_align = block_align;
io_data.logical_offset = -1; /* force phys offset reset */
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, xwma_konami_io_read,xwma_konami_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _XWMA_KONAMI_STREAMFILE_H_ */

View File

@ -6,6 +6,55 @@
#include "streamfile.h" #include "streamfile.h"
#if 0
/* ****************************************** */
/* PLAYER: simplifies plugin code */
/* ****************************************** */
/* opaque player state */
typedef struct VGMSTREAM_PLAYER VGMSTREAM_PLAYER;
typedef struct {
//...
} VGMSTREAM_PLAYER_INFO;
VGMSTREAM_PLAYER* vgmstream_player_init(...);
VGMSTREAM_PLAYER* vgmstream_player_format_check(...);
VGMSTREAM_PLAYER* vgmstream_player_set_format_whilelist(...);
VGMSTREAM_PLAYER* vgmstream_player_set_format_blacklist(...);
VGMSTREAM_PLAYER* vgmstream_player_set_file(...);
VGMSTREAM_PLAYER* vgmstream_player_get_config(...);
VGMSTREAM_PLAYER* vgmstream_player_set_config(...);
VGMSTREAM_PLAYER* vgmstream_player_get_buffer(...);
VGMSTREAM_PLAYER* vgmstream_player_get_info(...);
VGMSTREAM_PLAYER* vgmstream_player_describe(...);
VGMSTREAM_PLAYER* vgmstream_player_get_title(...);
VGMSTREAM_PLAYER* vgmstream_player_get_tagfile(...);
VGMSTREAM_PLAYER* vgmstream_player_play(...);
VGMSTREAM_PLAYER* vgmstream_player_seek(...);
VGMSTREAM_PLAYER* vgmstream_player_close(...);
#endif
/* ****************************************** */
/* TAGS: loads key=val tags from a file */
/* ****************************************** */
/* opaque tag state */ /* opaque tag state */
typedef struct VGMSTREAM_TAGS VGMSTREAM_TAGS; typedef struct VGMSTREAM_TAGS VGMSTREAM_TAGS;
@ -26,10 +75,22 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile);
/* Closes tag file */ /* Closes tag file */
void vgmstream_tags_close(VGMSTREAM_TAGS* tags); void vgmstream_tags_close(VGMSTREAM_TAGS* tags);
#ifdef VGMSTREAM_MIXING #ifdef VGMSTREAM_MIXING
/* ****************************************** */
/* MIXING: modifies vgmstream output */
/* ****************************************** */
/* Enables mixing effects, with max outbuf samples as a hint. Once active, plugin /* Enables mixing effects, with max outbuf samples as a hint. Once active, plugin
* must use returned input_channels to create outbuf and output_channels to output audio. */ * must use returned input_channels to create outbuf and output_channels to output audio.
void vgmstream_enable_mixing(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels); * Needs to be enabled last after adding effects. */
void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels);
/* sets a fadeout */
//void vgmstream_mixing_fadeout(VGMSTREAM *vgmstream, float start_second, float duration_seconds);
/* sets downmixing if needed */
//void vgmstream_mixing_downmix(VGMSTREAM *vgmstream, int max_channels)
#endif #endif
#endif /* _PLUGINS_H_ */ #endif /* _PLUGINS_H_ */

View File

@ -9,6 +9,9 @@
#include "meta/meta.h" #include "meta/meta.h"
#include "layout/layout.h" #include "layout/layout.h"
#include "coding/coding.h" #include "coding/coding.h"
#ifdef VGMSTREAM_MIXING
#include "mixing.h"
#endif
static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *streamFile, VGMSTREAM* (*init_vgmstream_function)(STREAMFILE*)); static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *streamFile, VGMSTREAM* (*init_vgmstream_function)(STREAMFILE*));
@ -37,7 +40,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_cstr, init_vgmstream_cstr,
init_vgmstream_gcsw, init_vgmstream_gcsw,
init_vgmstream_ps2_ads, init_vgmstream_ps2_ads,
init_vgmstream_ps2_npsf, init_vgmstream_nps,
init_vgmstream_rwsd, init_vgmstream_rwsd,
init_vgmstream_xa, init_vgmstream_xa,
init_vgmstream_ps2_rxws, init_vgmstream_ps2_rxws,
@ -248,7 +251,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ps2_ster, init_vgmstream_ps2_ster,
init_vgmstream_ps2_wb, init_vgmstream_ps2_wb,
init_vgmstream_bnsf, init_vgmstream_bnsf,
init_vgmstream_s14_sss,
init_vgmstream_ps2_gcm, init_vgmstream_ps2_gcm,
init_vgmstream_ps2_smpl, init_vgmstream_ps2_smpl,
init_vgmstream_ps2_msa, init_vgmstream_ps2_msa,
@ -279,7 +281,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ngc_nst_dsp, init_vgmstream_ngc_nst_dsp,
init_vgmstream_baf, init_vgmstream_baf,
init_vgmstream_baf_badrip, init_vgmstream_baf_badrip,
init_vgmstream_ps3_msf, init_vgmstream_msf,
init_vgmstream_nub_vag, init_vgmstream_nub_vag,
init_vgmstream_ps3_past, init_vgmstream_ps3_past,
init_vgmstream_sgxd, init_vgmstream_sgxd,
@ -298,7 +300,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_pc_adp_otns, init_vgmstream_pc_adp_otns,
init_vgmstream_eb_sfx, init_vgmstream_eb_sfx,
init_vgmstream_eb_sf0, init_vgmstream_eb_sf0,
init_vgmstream_ps2_mtaf, init_vgmstream_mtaf,
init_vgmstream_tun, init_vgmstream_tun,
init_vgmstream_wpd, init_vgmstream_wpd,
init_vgmstream_mn_str, init_vgmstream_mn_str,
@ -344,7 +346,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ta_aac_mobile_vorbis, init_vgmstream_ta_aac_mobile_vorbis,
init_vgmstream_ta_aac_vita, init_vgmstream_ta_aac_vita,
init_vgmstream_va3, init_vgmstream_va3,
init_vgmstream_ps3_mta2, init_vgmstream_mta2,
init_vgmstream_mta2_container,
init_vgmstream_ngc_ulw, init_vgmstream_ngc_ulw,
init_vgmstream_pc_xa30, init_vgmstream_pc_xa30,
init_vgmstream_wii_04sw, init_vgmstream_wii_04sw,
@ -468,12 +471,18 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_dsp_ds2, init_vgmstream_dsp_ds2,
init_vgmstream_ffdl, init_vgmstream_ffdl,
init_vgmstream_mus_vc, init_vgmstream_mus_vc,
init_vgmstream_strm_abylight,
init_vgmstream_sfh,
init_vgmstream_ea_schl_video,
init_vgmstream_msf_konami,
init_vgmstream_xwma_konami,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
init_vgmstream_ps2_int, /* .int raw PS-ADPCM */ init_vgmstream_ps2_int, /* .int raw PS-ADPCM */
init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */ init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */
init_vgmstream_pc_snds, /* .snds PC, after ps_headerless */ init_vgmstream_pc_snds, /* .snds PC, after ps_headerless */
init_vgmstream_s14_sss, /* .raw siren14 */
init_vgmstream_raw, /* .raw PCM */ init_vgmstream_raw, /* .raw PCM */
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */ init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */
@ -545,8 +554,28 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) {
} }
#endif #endif
/* some players are picky with incorrect channel layouts */
if (vgmstream->channel_layout > 0) {
int output_channels = vgmstream->channels;
int ch, count = 0, max_ch = 32;
for (ch = 0; ch < max_ch; ch++) {
int bit = (vgmstream->channel_layout >> ch) & 1;
if (ch > 17 && bit) {
VGM_LOG("VGMSTREAM: wrong bit %i in channel_layout %x\n", ch, vgmstream->channel_layout);
vgmstream->channel_layout = 0;
break;
}
count += bit;
}
if (count > output_channels) {
VGM_LOG("VGMSTREAM: wrong totals %i in channel_layout %x\n", count, vgmstream->channel_layout);
vgmstream->channel_layout = 0;
}
}
/* files can have thousands subsongs, but let's put a limit */ /* files can have thousands subsongs, but let's put a limit */
if (vgmstream->num_streams < 0 || vgmstream->num_streams > 65535) { if (vgmstream->num_streams < 0 || vgmstream->num_streams > VGMSTREAM_MAX_SUBSONGS) {
VGM_LOG("VGMSTREAM: wrong num_streams (ns=%i)\n", vgmstream->num_streams); VGM_LOG("VGMSTREAM: wrong num_streams (ns=%i)\n", vgmstream->num_streams);
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
continue; continue;
@ -570,15 +599,6 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) {
void setup_vgmstream(VGMSTREAM * vgmstream) { void setup_vgmstream(VGMSTREAM * vgmstream) {
#ifdef VGMSTREAM_MIXING
/* fill default config to simplify external code (mixing off will always happen
* initially, and if they contain values it means mixing must be enabled) */
if (!vgmstream->mixing_on || vgmstream->input_channels <= 0)
vgmstream->input_channels = vgmstream->channels;
if (!vgmstream->mixing_on || vgmstream->output_channels <= 0)
vgmstream->output_channels = vgmstream->channels;
#endif
/* save start things so we can restart when seeking */ /* save start things so we can restart when seeking */
memcpy(vgmstream->start_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); memcpy(vgmstream->start_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels);
memcpy(vgmstream->start_vgmstream, vgmstream, sizeof(VGMSTREAM)); memcpy(vgmstream->start_vgmstream, vgmstream, sizeof(VGMSTREAM));
@ -756,8 +776,7 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int loop_flag) {
vgmstream->loop_flag = loop_flag; vgmstream->loop_flag = loop_flag;
#ifdef VGMSTREAM_MIXING #ifdef VGMSTREAM_MIXING
/* fixed arrays, for now */ mixing_init(vgmstream); /* pre-init */
vgmstream->mixing_size = VGMSTREAM_MAX_MIXING;
#endif #endif
//vgmstream->stream_name_size = STREAM_NAME_SIZE; //vgmstream->stream_name_size = STREAM_NAME_SIZE;
return vgmstream; return vgmstream;
@ -767,6 +786,9 @@ fail:
free(vgmstream->start_ch); free(vgmstream->start_ch);
free(vgmstream->loop_ch); free(vgmstream->loop_ch);
free(vgmstream->start_vgmstream); free(vgmstream->start_vgmstream);
#ifdef VGMSTREAM_MIXING
mixing_close(vgmstream);
#endif
} }
free(vgmstream); free(vgmstream);
return NULL; return NULL;
@ -905,7 +927,9 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
} }
} }
} }
#ifdef VGMSTREAM_MIXING
mixing_close(vgmstream);
#endif
free(vgmstream->ch); free(vgmstream->ch);
free(vgmstream->start_ch); free(vgmstream->start_ch);
free(vgmstream->loop_ch); free(vgmstream->loop_ch);
@ -2501,14 +2525,18 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea
if (opened_vgmstream->channels != 1) if (opened_vgmstream->channels != 1)
return; return;
/* custom codec/layouts aren't designed for this (should never get here anyway) */
if (opened_vgmstream->codec_data || opened_vgmstream->layout_data)
return;
/* vgmstream's layout stuff currently assumes a single file */ /* vgmstream's layout stuff currently assumes a single file */
// fastelbja : no need ... this one works ok with dual file // fastelbja : no need ... this one works ok with dual file
//if (opened_vgmstream->layout != layout_none) return; //if (opened_vgmstream->layout != layout_none) return;
//todo force layout_none if layout_interleave? //todo force layout_none if layout_interleave?
streamFile->get_name(streamFile,new_filename,sizeof(new_filename)); get_streamfile_name(streamFile,new_filename,sizeof(new_filename));
if (strlen(new_filename) < 2) return; /* we need at least a base and a name ending to replace */ if (strlen(new_filename) < 2) return; /* we need at least a base and a name ending to replace */
ext = (char *)filename_extension(new_filename); ext = (char *)filename_extension(new_filename);
if (ext-new_filename >= 1 && ext[-1]=='.') ext--; /* including "." */ if (ext-new_filename >= 1 && ext[-1]=='.') ext--; /* including "." */
@ -2545,7 +2573,7 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea
/* try to init other channel (new_filename now has the opposite name) */ /* try to init other channel (new_filename now has the opposite name) */
dual_streamFile = streamFile->open(streamFile,new_filename,STREAMFILE_DEFAULT_BUFFER_SIZE); dual_streamFile = open_streamfile(streamFile,new_filename);
if (!dual_streamFile) goto fail; if (!dual_streamFile) goto fail;
new_vgmstream = init_vgmstream_function(dual_streamFile); /* use the init that just worked, no other should work */ new_vgmstream = init_vgmstream_function(dual_streamFile); /* use the init that just worked, no other should work */
@ -2630,6 +2658,10 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea
/* discard the second VGMSTREAM */ /* discard the second VGMSTREAM */
free(new_vgmstream); free(new_vgmstream);
#ifdef VGMSTREAM_MIXING
mixing_update_channel(opened_vgmstream); /* notify of new channel hacked-in */
#endif
} }
fail: fail:
@ -2817,15 +2849,16 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
} }
/* stereo codecs interleave in 2ch pairs (interleave size should still be: full_block_size / channels) */ /* stereo codecs interleave in 2ch pairs (interleave size should still be: full_block_size / channels) */
if (vgmstream->layout_type == layout_interleave && vgmstream->coding_type == coding_XBOX_IMA) { if (vgmstream->layout_type == layout_interleave &&
(vgmstream->coding_type == coding_XBOX_IMA || vgmstream->coding_type == coding_MTAF)) {
is_stereo_codec = 1; is_stereo_codec = 1;
} }
streamFile->get_name(streamFile,filename,sizeof(filename)); get_streamfile_name(streamFile,filename,sizeof(filename));
/* open the file for reading by each channel */ /* open the file for reading by each channel */
{ {
if (!use_streamfile_per_channel) { if (!use_streamfile_per_channel) {
file = streamFile->open(streamFile,filename, STREAMFILE_DEFAULT_BUFFER_SIZE); file = open_streamfile(streamFile,filename);
if (!file) goto fail; if (!file) goto fail;
} }
@ -2842,7 +2875,7 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
/* open new one if needed */ /* open new one if needed */
if (use_streamfile_per_channel) { if (use_streamfile_per_channel) {
file = streamFile->open(streamFile,filename, STREAMFILE_DEFAULT_BUFFER_SIZE); file = open_streamfile(streamFile,filename);
if (!file) goto fail; if (!file) goto fail;
} }

View File

@ -5,15 +5,13 @@
#ifndef _VGMSTREAM_H #ifndef _VGMSTREAM_H
#define _VGMSTREAM_H #define _VGMSTREAM_H
/* reasonable maxs */ /* reasonable limits */
enum { PATH_LIMIT = 32768 }; enum { PATH_LIMIT = 32768 };
enum { STREAM_NAME_SIZE = 255 }; enum { STREAM_NAME_SIZE = 255 };
enum { VGMSTREAM_MAX_CHANNELS = 64 }; enum { VGMSTREAM_MAX_CHANNELS = 64 };
enum { VGMSTREAM_MIN_SAMPLE_RATE = 300 }; /* 300 is Wwise min */ enum { VGMSTREAM_MIN_SAMPLE_RATE = 300 }; /* 300 is Wwise min */
enum { VGMSTREAM_MAX_SAMPLE_RATE = 96000 }; enum { VGMSTREAM_MAX_SAMPLE_RATE = 96000 };
#ifdef VGMSTREAM_MIXING enum { VGMSTREAM_MAX_SUBSONGS = 65535 };
enum { VGMSTREAM_MAX_MIXING = 64 };
#endif
#include "streamfile.h" #include "streamfile.h"
@ -339,7 +337,7 @@ typedef enum {
meta_XA, /* CD-ROM XA */ meta_XA, /* CD-ROM XA */
meta_PS2_SShd, /* .ADS with SShd header */ meta_PS2_SShd, /* .ADS with SShd header */
meta_PS2_NPSF, /* Namco Production Sound File */ meta_NPS,
meta_PS2_RXWS, /* Sony games (Genji, Okage Shadow King, Arc The Lad Twilight of Spirits) */ meta_PS2_RXWS, /* Sony games (Genji, Okage Shadow King, Arc The Lad Twilight of Spirits) */
meta_PS2_RAW, /* RAW Interleaved Format */ meta_PS2_RAW, /* RAW Interleaved Format */
meta_PS2_EXST, /* Shadow of Colossus EXST */ meta_PS2_EXST, /* Shadow of Colossus EXST */
@ -570,7 +568,7 @@ typedef enum {
meta_BAF, /* Bizarre Creations (Blur, James Bond) */ meta_BAF, /* Bizarre Creations (Blur, James Bond) */
meta_XVAG, /* Ratchet & Clank Future: Quest for Booty (PS3) */ meta_XVAG, /* Ratchet & Clank Future: Quest for Booty (PS3) */
meta_PS3_CPS, /* Eternal Sonata (PS3) */ meta_PS3_CPS, /* Eternal Sonata (PS3) */
meta_PS3_MSF, /* MSF header */ meta_MSF,
meta_NUB_VAG, /* Namco VAG from NUB archives */ meta_NUB_VAG, /* Namco VAG from NUB archives */
meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */ meta_PS3_PAST, /* Bakugan Battle Brawlers (PS3) */
meta_SGXD, /* Sony: Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */ meta_SGXD, /* Sony: Folklore, Genji, Tokyo Jungle (PS3), Brave Story, Kurohyo (PSP) */
@ -591,7 +589,7 @@ typedef enum {
meta_OTNS_ADP, /* Omikron: The Nomad Soul .adp (PC/DC) */ meta_OTNS_ADP, /* Omikron: The Nomad Soul .adp (PC/DC) */
meta_EB_SFX, /* Excitebots .sfx */ meta_EB_SFX, /* Excitebots .sfx */
meta_EB_SF0, /* Excitebots .sf0 */ meta_EB_SF0, /* Excitebots .sf0 */
meta_PS2_MTAF, /* Metal Gear Solid 3 MTAF */ meta_MTAF,
meta_PS2_VAG1, /* Metal Gear Solid 3 VAG1 */ meta_PS2_VAG1, /* Metal Gear Solid 3 VAG1 */
meta_PS2_VAG2, /* Metal Gear Solid 3 VAG2 */ meta_PS2_VAG2, /* Metal Gear Solid 3 VAG2 */
meta_TUN, /* LEGO Racers (PC) */ meta_TUN, /* LEGO Racers (PC) */
@ -629,7 +627,7 @@ typedef enum {
meta_TA_AAC_X360, /* tri-Ace AAC (Star Ocean 4, End of Eternity, Infinite Undiscovery) */ meta_TA_AAC_X360, /* tri-Ace AAC (Star Ocean 4, End of Eternity, Infinite Undiscovery) */
meta_TA_AAC_PS3, /* tri-Ace AAC (Star Ocean International, Resonance of Fate) */ meta_TA_AAC_PS3, /* tri-Ace AAC (Star Ocean International, Resonance of Fate) */
meta_TA_AAC_MOBILE, /* tri-Ace AAC (Star Ocean Anamnesis, Heaven x Inferno) */ meta_TA_AAC_MOBILE, /* tri-Ace AAC (Star Ocean Anamnesis, Heaven x Inferno) */
meta_PS3_MTA2, /* Metal Gear Solid 4 MTA2 */ meta_MTA2,
meta_NGC_ULW, /* Burnout 1 (GC only) */ meta_NGC_ULW, /* Burnout 1 (GC only) */
meta_PC_XA30, /* Driver - Parallel Lines (PC) */ meta_PC_XA30, /* Driver - Parallel Lines (PC) */
meta_WII_04SW, /* Driver - Parallel Lines (Wii) */ meta_WII_04SW, /* Driver - Parallel Lines (Wii) */
@ -728,6 +726,9 @@ typedef enum {
meta_208, meta_208,
meta_DSP_DS2, meta_DSP_DS2,
meta_MUS_VC, meta_MUS_VC,
meta_STRM_ABYLIGHT,
meta_MSF_KONAMI,
meta_XWMA_KONAMI,
} meta_t; } meta_t;
@ -773,37 +774,6 @@ typedef enum {
mapping_7POINT1_surround = speaker_FL | speaker_FR | speaker_FC | speaker_LFE | speaker_BL | speaker_BR | speaker_SL | speaker_SR, mapping_7POINT1_surround = speaker_FL | speaker_FR | speaker_FC | speaker_LFE | speaker_BL | speaker_BR | speaker_SL | speaker_SR,
} mapping_t; } mapping_t;
#ifdef VGMSTREAM_MIXING
/* mixing info */
typedef enum {
MIX_SWAP,
MIX_ADD,
MIX_ADD_VOLUME,
MIX_VOLUME,
MIX_LIMIT,
MIX_DOWNMIX,
MIX_DOWNMIX_REST,
MIX_UPMIX,
MIX_FADE
} mix_command_t;
typedef struct {
mix_command_t command;
/* common */
int ch_dst;
int ch_src;
float vol;
/* fade envelope */
float vol_start; /* volume from pre to start */
float vol_end; /* volume from end to post */
char shape; /* curve type */
float time_pre; /* position before curve where vol_str applies (-1 = beginning) */
float time_start; /* curve start position where vol changes from src to dst */
float time_end; /* curve end position where vol changes from src to dst */
float time_post; /* position after curve where vol_dst applies (-1 = end) */
} mix_config_data;
#endif
/* info for a single vgmstream channel */ /* info for a single vgmstream channel */
typedef struct { typedef struct {
@ -888,15 +858,7 @@ typedef struct {
int channel_mappings_on; /* channel mappings are active */ int channel_mappings_on; /* channel mappings are active */
int channel_mappings[32]; /* swap channel "i" with "[i]" */ int channel_mappings[32]; /* swap channel "i" with "[i]" */
#endif #endif
#ifdef VGMSTREAM_MIXING
/* may be ignored if plugin doesn't support it, but fields will be always set to simplify plugin's code */
int input_channels; /* starting channels before mixing (outbuf must be this big) */
int output_channels; /* resulting channels after mixing */
int mixing_on; /* mixing allowed */
int mixing_count; /* mixing number */
size_t mixing_size; /* mixing max */
mix_config_data mixing_chain[VGMSTREAM_MAX_MIXING]; /* effects to apply (could be alloc'ed but to simplify...) */
#endif
/* config requests, players must read and honor these values */ /* config requests, players must read and honor these values */
/* (ideally internally would work as a player, but for now player must do it manually) */ /* (ideally internally would work as a player, but for now player must do it manually) */
double config_loop_count; double config_loop_count;
@ -940,13 +902,18 @@ typedef struct {
VGMSTREAMCHANNEL * loop_ch; /* shallow copy of channels as they were at the loop point (for loops) */ VGMSTREAMCHANNEL * loop_ch; /* shallow copy of channels as they were at the loop point (for loops) */
void* start_vgmstream; /* shallow copy of the VGMSTREAM as it was at the beginning of the stream (for resets) */ void* start_vgmstream; /* shallow copy of the VGMSTREAM as it was at the beginning of the stream (for resets) */
/* Data the codec needs for the whole stream. This is for codecs too #ifdef VGMSTREAM_MIXING
void * mixing_data; /* state for mixing effects */
#endif
/* Optional data the codec needs for the whole stream. This is for codecs too
* different from vgmstream's structure to be reasonably shoehorned. * different from vgmstream's structure to be reasonably shoehorned.
* Note also that support must be added for resetting, looping and * Note also that support must be added for resetting, looping and
* closing for every codec that uses this, as it will not be handled. */ * closing for every codec that uses this, as it will not be handled. */
void * codec_data; void * codec_data;
/* Same, for special layouts. layout_data + codec_data may exist at the same time. */ /* Same, for special layouts. layout_data + codec_data may exist at the same time. */
void * layout_data; void * layout_data;
} VGMSTREAM; } VGMSTREAM;
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS
@ -1381,15 +1348,6 @@ VGMSTREAM * allocate_vgmstream(int channel_count, int looped);
/* Prepare the VGMSTREAM's initial state once parsed and ready, but before playing. */ /* Prepare the VGMSTREAM's initial state once parsed and ready, but before playing. */
void setup_vgmstream(VGMSTREAM * vgmstream); void setup_vgmstream(VGMSTREAM * vgmstream);
#ifdef VGMSTREAM_MIXING
/* Applies mixing commands to the vgmstream to the sample buffer.
* Mixing must be enabled and outbuf must be big enough for output_channels*samples_to_do big. */
void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream);
/* Add a new internal mix. Always use this as it validates mixes. */
void vgmstream_add_mixing(VGMSTREAM* vgmstream, mix_config_data mix);
#endif
/* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */ /* Get the number of samples of a single frame (smallest self-contained sample group, 1/N channels) */
int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream); int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream);
/* Get the number of bytes of a single frame (smallest self-contained byte group, 1/N channels) */ /* Get the number of bytes of a single frame (smallest self-contained byte group, 1/N channels) */