mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-29 19:37:30 +01:00
Add TXTH dual file/bigfile/subsong/multiplier handling
- Can loading a separate header_file or body_file for dual files - Can load from .txth directly while setting header_file/body_file for bigfiles - Can set value_multiplier for use with sector and similar values that must be multiplied - Can set subsong_count and subsong_offset for subsong headers suboffset
This commit is contained in:
parent
e8ca19ba2c
commit
976e1f3efe
190
doc/TXTH.md
190
doc/TXTH.md
@ -2,7 +2,7 @@
|
||||
|
||||
TXTH is a simple text file that uses text commands to simulate a header for files unsupported by vgmstream, mainly headerless audio.
|
||||
|
||||
When an unsupported file is loaded, vgmstream tries to find a TXTH header in the same dir, in this order:
|
||||
When an unsupported file is loaded (for instance "bgm01.snd"), vgmstream tries to find a TXTH header in the same dir, in this order:
|
||||
- (filename.ext).txth
|
||||
- .(ext).txth
|
||||
- .txth
|
||||
@ -31,16 +31,16 @@ A text file with the above commands must be saved as ".vag.txth" or ".txth", not
|
||||
```
|
||||
######################################################
|
||||
|
||||
# comments start with #, can be inline
|
||||
# The file is made lines of "key = value" describing a header.
|
||||
# Spaces are optional: key=value, key= value, and so on are all ok.
|
||||
# Comments start with #, can be inlined.
|
||||
# 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.
|
||||
# Examples: 44100, 40, 0x40 (dec=64)
|
||||
# Examples: 44100, 40, 0x40 (decimal=64)
|
||||
# - (offset): format is @(number)[:LE|BE][$1|2|3|4]
|
||||
# * @(number): value at offset (required)
|
||||
# * :LE|BE: value is little/big endian (optional, defaults to LE)
|
||||
@ -52,73 +52,76 @@ A text file with the above commands must be saved as ".vag.txth" or ".txth", not
|
||||
# Accepted codec strings:
|
||||
# - PSX PlayStation ADPCM
|
||||
# - XBOX Xbox IMA ADPCM
|
||||
# - NGC_DTK Nintendo ADP/DTK ADPCM
|
||||
# - PCM16BE PCM RAW 16bit big endian
|
||||
# - PCM16LE PCM RAW 16bit little endian
|
||||
# - PCM8 PCM RAW 8bit
|
||||
# - NGC_DTK|DTK Nintendo ADP/DTK ADPCM
|
||||
# - PCM16BE PCM 16-bit big endian
|
||||
# - PCM16LE PCM 16-bit little endian
|
||||
# - PCM8 PCM 8-bit
|
||||
# - SDX2 Squareroot-delta-exact 8-bit DPCM (3DO games)
|
||||
# - DVI_IMA DVI IMA ADPCM
|
||||
# - MPEG MPEG Audio Layer File (MP1/2/3)
|
||||
# - MPEG MPEG Audio Layer file (MP1/2/3)
|
||||
# - IMA IMA ADPCM
|
||||
# - AICA Yamaha AICA ADPCM (Dreamcast)
|
||||
# - MSADPCM Microsoft ADPCM (Windows)
|
||||
# - NGC_DSP Nintendo GameCube ADPCM
|
||||
# - NGC_DSP|DSP Nintendo GameCube ADPCM
|
||||
# - PCM8_U_int PCM RAW 8bit unsigned (interleaved)
|
||||
# - PSX_bf PlayStation ADPCM with bad flags
|
||||
# - MS_IMA Microsoft IMA ADPCM
|
||||
# - PCM8_U PCM RAW 8bit unsigned
|
||||
# - PCM8_U PCM 8-bit unsigned
|
||||
# - APPLE_IMA4 Apple Quicktime IMA ADPCM
|
||||
# - ATRAC3 raw ATRAC3
|
||||
# - ATRAC3PLUS raw ATRAC3PLUS
|
||||
# - XMA1 raw XMA1
|
||||
# - XMA2 raw XMA2
|
||||
# - FFMPEG any headered FFmpeg format
|
||||
# - ATRAC3 Sony ATRAC3
|
||||
# - ATRAC3PLUS Sony ATRAC3plus
|
||||
# - XMA1 Microsoft XMA1
|
||||
# - XMA2 Microsoft XMA2
|
||||
# - FFMPEG Any headered FFmpeg format
|
||||
# - AC3 AC3/SPDIF
|
||||
codec = (codec string)
|
||||
|
||||
# Varies with codec:
|
||||
|
||||
# Codec variations [OPTIONAL, depends on codec]
|
||||
# - NGC_DSP: 0=normal interleave, 1=byte interleave, 2=no interleave
|
||||
# - 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)
|
||||
# - XBOX: 0=standard (mono or stereo interleave), 1=force mono interleave mode
|
||||
# - others: ignored
|
||||
codec_mode = (number)
|
||||
|
||||
|
||||
# Multiplies any next read values [OPTIONAL]
|
||||
# Will change to "(key) = (number)|(offset) * value_multiplier",
|
||||
# useful if a size is given in 0x800 sectors or such.
|
||||
# Should be set to 0 when done using, as it affects ANY value.
|
||||
value_multiplier = (number)|(offset)
|
||||
|
||||
# Interleave or block size [REQUIRED/OPTIONAL, depends on codec]
|
||||
# For mono/interleaved codecs it's the amount of data between channels.
|
||||
# For codecs with variable-sized frames (MSADPCM, MS-IMA, ATRAC3/plus)
|
||||
# it's the block size (size of a single frame).
|
||||
# - half_size: sets interleave as data_size / channels
|
||||
# For mono/interleaved codecs it's the amount of data between channels,
|
||||
# and for codecs with variable-sized frames (MSADPCM, MS-IMA, ATRAC3/plus)
|
||||
# means block size (size of a single frame).
|
||||
# Interleave 0 means "stereo mode" for some codecs (IMA, AICA, etc).
|
||||
interleave = (number)|(offset)
|
||||
interleave = (number)|(offset)|half_size
|
||||
|
||||
# Validate that id_value matches value at id_offset [OPTIONAL]
|
||||
# Can be redefined several times, it's checked whenever a new id_offset is found.
|
||||
id_value = (number)|(offset)
|
||||
id_offset = (number)|(offset)
|
||||
|
||||
|
||||
# Number of channels [REQUIRED]
|
||||
channels = (number)|(offset)
|
||||
|
||||
|
||||
# Music frequency in hz [REQUIRED]
|
||||
sample_rate = (number)|(offset)
|
||||
|
||||
|
||||
# Data start [OPTIONAL, default to 0]
|
||||
start_offset = (number)|(offset)
|
||||
|
||||
|
||||
# Variable that can be used in sample values [OPTIONAL]
|
||||
# Defaults to (file_size - start_offset), re-calculated when start_offset is set.
|
||||
# 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).
|
||||
data_size = (number)|(offset)
|
||||
|
||||
|
||||
|
||||
# Modifies the meaning of sample fields when set *before* them [OPTIONAL, defaults to samples]
|
||||
# - samples: exact sample
|
||||
# - bytes: automatically converts bytes/offset to samples
|
||||
# - blocks: same as bytes, but value is given in blocks/frames
|
||||
# Value is internally converted from blocks to bytes first: bytes = (value * interleave*channels)
|
||||
# It's possible to re-define values multiple times:
|
||||
# * samples_type=bytes ... num_samples=@0x10
|
||||
# * samples_type=sample ... loop_end_sample=@0x14
|
||||
# Sometimes "bytes" values are given for a single channel only. In that case you can temporally set 1 channel
|
||||
# * channels=1 ... sample_type=bytes ... num_samples=@0x10 ... channels=2
|
||||
# Some codecs can't convert bytes-to-samples at the moment: MPEG/FFMPEG
|
||||
# For XMA1/2 bytes does special parsing, with loop values being bit offsets within data.
|
||||
sample_type = samples|bytes|blocks
|
||||
@ -128,29 +131,124 @@ num_samples = (number)|(offset)|data_size
|
||||
loop_start_sample = (number)|(offset)
|
||||
loop_end_sample = (number)|(offset)|data_size
|
||||
|
||||
# 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
|
||||
# (rarely a format may have rough loop offset/bytes, then a loop adjust in samples).
|
||||
loop_adjust = (number)|(offset)
|
||||
|
||||
# Force loop, on (>0) or off (0), as loop start/end may be defined but not used [OPTIONAL]
|
||||
# - auto: tries to autodetect loop points for PS-ADPCM data, which may include loop flags.
|
||||
# By default it loops when loop_end_sample is defined
|
||||
# auto tries to autodetect loop points for PS-ADPCM data, which may include loop flags.
|
||||
loop_flag = (number)|(offset)|auto
|
||||
|
||||
# beginning samples to skip (encoder delay), for codecs that need them (ATRAC3/XMA/etc)
|
||||
# Loop start/end modifier [OPTIONAL]
|
||||
# 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
|
||||
# (a format may rarely have rough loop offset/bytes, then a loop adjust in samples).
|
||||
loop_adjust = (number)|(offset)
|
||||
|
||||
# Beginning samples to skip (encoder delay) [OPTIONAL]
|
||||
# Only some codecs use them (ATRAC3/ATRAC3PLUS/XMA/FFMPEG/AC3)
|
||||
skip_samples = (number)|(offset)
|
||||
|
||||
|
||||
# DSP coefficients [REQUIRED for NGC_DSP]
|
||||
# Coefs start
|
||||
# DSP decoding coefficients [REQUIRED for NGC_DSP]
|
||||
# These coefs are a list of 8*2 16-bit values per channel, starting from offset.
|
||||
coef_offset = (number)|(offset)
|
||||
# offset separation per channel, usually 0x20
|
||||
# - Example: channel N coefs are read at coef_offset + coef_spacing * N
|
||||
# Offset separation per channel, usually 0x20 (16 values * 2 bytes)
|
||||
# Channel N coefs are read at coef_offset + coef_spacing * N
|
||||
coef_spacing = (number)|(offset)
|
||||
# Format, usually BE; with (offset): 0=LE, >0=BE
|
||||
coef_endianness = BE|LE|(offset)
|
||||
# Split/normal coefs [NOT IMPLEMENTED YET]
|
||||
#coef_mode = (number)|(offset)
|
||||
|
||||
|
||||
# Change header/body to external files [OPTIONAL]
|
||||
# TXTH commands are done on a "header", and decoding on "body".
|
||||
# When loading an unsupported file it becomes the "base" file
|
||||
# that loads the .txth, and is both header and body.
|
||||
#
|
||||
# You can alter those, mainly for files that split header and body
|
||||
# in separate files (load base file and txth sets header on another file).
|
||||
# It's also possible to load the .txth directly with a set body, as a sort of
|
||||
# "reverse TXTH" (useful with bigfiles, as you could have one .txth per song).
|
||||
#
|
||||
# Allowed values:
|
||||
# - (filename): open any file, subdirs also work (dir/filename)
|
||||
# - *.(extension): opens with same name as the "base" file plus another extension
|
||||
# - null: unloads file and goes back to defaults (body/header = base file).
|
||||
header_file = (filename)|*.(extension)|null
|
||||
body_file = (filename)|*.(extension)|null
|
||||
|
||||
# Subsongs [OPTIONAL]
|
||||
# Sets the number of subsongs in the file, adjusting reads per subsong N:
|
||||
# "value = @(offset) + subsong_offset*N". Mainly for bigfiles with consecutive
|
||||
# headers per subsong, set subsong_offset to 0 when done as it affects any reads.
|
||||
# The current subsong number is handled externally by plugins or TXTP.
|
||||
subsong_count = (number)|(offset)
|
||||
subsong_offset = (number)|(offset)
|
||||
```
|
||||
|
||||
## Usages
|
||||
|
||||
### Temporary values
|
||||
Most commands are evaluated and calculated immediatedly, every time they are found. This is by design, as it can be used to adjust and trick for certain calculations.
|
||||
|
||||
For example, normally you are given a data_size in bytes, that can be used to calculate num_samples for all channels.
|
||||
```
|
||||
channels = 2
|
||||
sample_type = bytes
|
||||
num_samples = @0x10 #calculated from data_size
|
||||
```
|
||||
|
||||
But sometimes this size is for a single channel only (even though the file may be stereo). You can set temporally change the channel number to force a correct calculation.
|
||||
```
|
||||
channels = 1 #not the actual number of channels
|
||||
sample_type = bytes
|
||||
num_samples = @0x10 #calculated from channel_size
|
||||
channels = 2 #change once calculations are done
|
||||
```
|
||||
|
||||
### Redefining values
|
||||
Some commands alter the function of all next commands and can be redefined as needed:
|
||||
```
|
||||
samples_type = bytes
|
||||
num_samples = @0x10
|
||||
|
||||
samples_type = sample
|
||||
loop_end_sample = @0x14
|
||||
```
|
||||
|
||||
### External files
|
||||
When setting external files all commands are done on the "header" file, but with some creativity you can read in multiple files.
|
||||
```
|
||||
body_file = bgm01.bdy
|
||||
header_file = bgm01.hdr
|
||||
channels = @0x10 #base info in bgm01.hdr
|
||||
header_file = bgm01.bdy
|
||||
coef_offset = 0x00 #DSP coefs in bgm01.bdy
|
||||
```
|
||||
Note that DSP coefs are special in that aren't read immediately, and will use *last* header_file set.
|
||||
|
||||
|
||||
### Resetting values
|
||||
Values may need to be reset (to 0 or other sensible value) when done. Subsong example:
|
||||
```
|
||||
subsong_count = 5
|
||||
subsong_offset = 0x20 # there are 5 subsong headers, 0x20 each
|
||||
channel_count = @0x10 # reads channels at 0x10+0x20*subsong
|
||||
# 1st subsong: 0x10+0x20*0: 0x10
|
||||
# 2nd subsong: 0x10+0x20*1: 0x30
|
||||
# 2nd subsong: 0x10+0x20*2: 0x50
|
||||
# ...
|
||||
start_offset = @0x14 # reads offset within data at 0x14+0x20*subsong
|
||||
|
||||
subsong_offset = 0 # reset value
|
||||
sample_rate = 0x04 # sample rate is the same for all subsongs
|
||||
# Nth subsong ch: 0x04+0x00*N: 0x08
|
||||
```
|
||||
|
||||
### Multipliers
|
||||
Sometimes header values are in "sectors" or similar concepts (typical in DVD games), and need to be adjusted to a real value.
|
||||
```
|
||||
value_multiplier = 0x800 # offsets are in DVD sector size
|
||||
start_offset = @0x10 # 2*0x800, for example
|
||||
value_multiplier = 0 # next values don't need to be multiplied
|
||||
start_offset = @0x14
|
||||
```
|
||||
|
@ -392,6 +392,7 @@ static const char* extension_list[] = {
|
||||
"trj",
|
||||
"trm",
|
||||
"tun",
|
||||
"txth",
|
||||
"txtp",
|
||||
"tydsp",
|
||||
|
||||
|
320
src/meta/txth.c
320
src/meta/txth.c
@ -35,6 +35,7 @@ typedef enum {
|
||||
typedef struct {
|
||||
txth_type codec;
|
||||
uint32_t codec_mode;
|
||||
uint32_t value_multiplier;
|
||||
uint32_t interleave;
|
||||
|
||||
uint32_t id_value;
|
||||
@ -66,12 +67,29 @@ typedef struct {
|
||||
|
||||
int num_samples_data_size;
|
||||
|
||||
int target_subsong;
|
||||
uint32_t subsong_count;
|
||||
uint32_t subsong_offset;
|
||||
|
||||
/* original STREAMFILE and its type (may be an unsupported "base" file or a .txth) */
|
||||
STREAMFILE *streamFile;
|
||||
int streamfile_is_txth;
|
||||
|
||||
/* configurable STREAMFILEs and if we opened it (thus must close it later) */
|
||||
STREAMFILE *streamText;
|
||||
STREAMFILE *streamHead;
|
||||
STREAMFILE *streamBody;
|
||||
int streamtext_opened;
|
||||
int streamhead_opened;
|
||||
int streambody_opened;
|
||||
|
||||
} txth_header;
|
||||
|
||||
|
||||
static STREAMFILE * open_txth(STREAMFILE * streamFile);
|
||||
static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth);
|
||||
static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth, const char * key, const char * val);
|
||||
static int parse_num(STREAMFILE * streamFile, const char * val, uint32_t * out_value);
|
||||
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);
|
||||
|
||||
|
||||
@ -79,23 +97,42 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes);
|
||||
* Similar to GENH, but with a single separate .txth file in the dir and text-based. */
|
||||
VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * streamText = NULL;
|
||||
txth_header txth = {0};
|
||||
coding_t coding;
|
||||
int i, j;
|
||||
|
||||
|
||||
/* reject .txth as the CLI can open and decode with itself */
|
||||
if (check_extensions(streamFile, "txth"))
|
||||
goto fail;
|
||||
/* accept .txth (should set body_file or will fail later) */
|
||||
if (check_extensions(streamFile, "txth")) {
|
||||
txth.streamFile = streamFile;
|
||||
txth.streamfile_is_txth = 1;
|
||||
|
||||
/* no need for ID or ext checks -- if a .TXTH exists all is good
|
||||
* (player still needs to accept the streamfile's ext, so at worst rename to .vgmstream) */
|
||||
streamText = open_txth(streamFile);
|
||||
if (!streamText) goto fail;
|
||||
txth.streamText = streamFile;
|
||||
txth.streamHead = NULL;
|
||||
txth.streamBody = NULL;
|
||||
txth.streamtext_opened = 0;
|
||||
txth.streamhead_opened = 0;
|
||||
txth.streambody_opened = 0;
|
||||
}
|
||||
else {
|
||||
/* accept base file (no need for ID or ext checks --if a companion .TXTH exists all is good)
|
||||
* (player still needs to accept the streamfile's ext, so at worst rename to .vgmstream) */
|
||||
STREAMFILE * streamText = open_txth(streamFile);
|
||||
if (!streamText) goto fail;
|
||||
|
||||
txth.streamFile = streamFile;
|
||||
txth.streamfile_is_txth = 0;
|
||||
|
||||
txth.streamText = streamText;
|
||||
txth.streamHead = streamFile;
|
||||
txth.streamBody = streamFile;
|
||||
txth.streamtext_opened = 1;
|
||||
txth.streamhead_opened = 0;
|
||||
txth.streambody_opened = 0;
|
||||
}
|
||||
|
||||
/* process the text file */
|
||||
if (!parse_txth(streamFile, streamText, &txth))
|
||||
if (!parse_txth(&txth))
|
||||
goto fail;
|
||||
|
||||
|
||||
@ -135,8 +172,8 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
||||
|
||||
/* try to autodetect PS-ADPCM loop data */
|
||||
if (txth.loop_flag_auto && coding == coding_PSX) {
|
||||
size_t data_size = get_streamfile_size(streamFile) - txth.start_offset;
|
||||
txth.loop_flag = ps_find_loop_offsets(streamFile, txth.start_offset, data_size, txth.channels, txth.interleave,
|
||||
size_t data_size = get_streamfile_size(txth.streamBody) - txth.start_offset;
|
||||
txth.loop_flag = ps_find_loop_offsets(txth.streamBody, txth.start_offset, data_size, txth.channels, txth.interleave,
|
||||
(int32_t*)&txth.loop_start_sample, (int32_t*)&txth.loop_end_sample);
|
||||
}
|
||||
|
||||
@ -149,6 +186,10 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
||||
vgmstream->num_samples = txth.num_samples;
|
||||
vgmstream->loop_start_sample = txth.loop_start_sample;
|
||||
vgmstream->loop_end_sample = txth.loop_end_sample;
|
||||
if (txth.subsong_count) {
|
||||
vgmstream->num_streams = txth.subsong_count;
|
||||
vgmstream->stream_size = txth.data_size;
|
||||
}
|
||||
|
||||
/* codec specific (taken from GENH with minimal changes) */
|
||||
switch (coding) {
|
||||
@ -262,15 +303,15 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
||||
/* normal/split coefs */
|
||||
if (txth.coef_mode == 0) {
|
||||
for (j=0;j<16;j++) {
|
||||
vgmstream->ch[i].adpcm_coef[j] = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2,streamFile);
|
||||
vgmstream->ch[i].adpcm_coef[j] = read_16bit(txth.coef_offset + i*txth.coef_spacing + j*2,txth.streamHead);
|
||||
}
|
||||
}
|
||||
else {
|
||||
goto fail; //IDK what is this
|
||||
/*
|
||||
for (j=0;j<8;j++) {
|
||||
vgmstream->ch[i].adpcm_coef[j*2]=read_16bit(coef[i]+j*2,streamFile);
|
||||
vgmstream->ch[i].adpcm_coef[j*2+1]=read_16bit(coef_splitted[i]+j*2,streamFile);
|
||||
vgmstream->ch[i].adpcm_coef[j*2]=read_16bit(coef[i]+j*2,txth.streamHead);
|
||||
vgmstream->ch[i].adpcm_coef[j*2+1]=read_16bit(coef_splitted[i]+j*2,txth.streamHead);
|
||||
}
|
||||
*/
|
||||
}
|
||||
@ -280,7 +321,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
||||
#ifdef VGM_USE_MPEG
|
||||
case coding_MPEG_layer3:
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->codec_data = init_mpeg(streamFile, txth.start_offset, &coding, vgmstream->channels);
|
||||
vgmstream->codec_data = init_mpeg(txth.streamBody, txth.start_offset, &coding, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
break;
|
||||
@ -291,7 +332,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
||||
|
||||
if (txth.codec == FFMPEG || txth.codec == AC3) {
|
||||
/* default FFmpeg */
|
||||
ffmpeg_data = init_ffmpeg_offset(streamFile, 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 (vgmstream->num_samples == 0)
|
||||
@ -336,7 +377,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, txth.start_offset,txth.data_size);
|
||||
ffmpeg_data = init_ffmpeg_header_offset(txth.streamBody, buf,bytes, txth.start_offset,txth.data_size);
|
||||
if ( !ffmpeg_data ) goto fail;
|
||||
}
|
||||
|
||||
@ -344,7 +385,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
if (txth.codec == XMA1 || txth.codec == XMA2) {
|
||||
xma_fix_raw_samples(vgmstream, streamFile, txth.start_offset,txth.data_size, 0, 0,0);
|
||||
xma_fix_raw_samples(vgmstream, txth.streamBody, txth.start_offset,txth.data_size, 0, 0,0);
|
||||
} else if (txth.skip_samples_set) { /* force encoder delay */
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, txth.skip_samples);
|
||||
}
|
||||
@ -373,14 +414,13 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
||||
msd.loop_end_subframe = txth.loop_adjust >> 4; /* upper 4b: subframe where the loop ends, 0..3 */
|
||||
}
|
||||
|
||||
xma_get_samples(&msd, streamFile);
|
||||
xma_get_samples(&msd, txth.streamBody);
|
||||
|
||||
vgmstream->num_samples = msd.num_samples;
|
||||
if (txth.sample_type==1) {
|
||||
vgmstream->loop_start_sample = msd.loop_start_sample;
|
||||
vgmstream->loop_end_sample = msd.loop_end_sample;
|
||||
}
|
||||
//skip_samples = msd.skip_samples; //todo add skip samples
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -389,14 +429,18 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
||||
vgmstream->allow_dual_stereo = 1;
|
||||
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream,streamFile,txth.start_offset) )
|
||||
if ( !vgmstream_open_stream(vgmstream,txth.streamBody,txth.start_offset) )
|
||||
goto fail;
|
||||
|
||||
if (streamText) close_streamfile(streamText);
|
||||
if (txth.streamtext_opened) close_streamfile(txth.streamText);
|
||||
if (txth.streamhead_opened) close_streamfile(txth.streamHead);
|
||||
if (txth.streambody_opened) close_streamfile(txth.streamBody);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (streamText) close_streamfile(streamText);
|
||||
if (txth.streamtext_opened) close_streamfile(txth.streamText);
|
||||
if (txth.streamhead_opened) close_streamfile(txth.streamHead);
|
||||
if (txth.streambody_opened) close_streamfile(txth.streamBody);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
@ -434,15 +478,25 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) {
|
||||
|
||||
/* Simple text parser of "key = value" lines.
|
||||
* The code is meh and error handling not exactly the best. */
|
||||
static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth) {
|
||||
static int parse_txth(txth_header * txth) {
|
||||
off_t txt_offset = 0x00;
|
||||
off_t file_size = get_streamfile_size(streamText);
|
||||
off_t file_size = get_streamfile_size(txth->streamText);
|
||||
|
||||
/* setup txth defaults */
|
||||
if (txth->streamBody)
|
||||
txth->data_size = get_streamfile_size(txth->streamBody);
|
||||
txth->target_subsong = txth->streamFile->stream_index;
|
||||
if (txth->target_subsong == 0) txth->target_subsong = 1;
|
||||
|
||||
txth->data_size = get_streamfile_size(streamFile); /* for later use */
|
||||
|
||||
/* skip BOM if needed */
|
||||
if ((uint16_t)read_16bitLE(0x00, streamText) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamText) == 0xFEFF)
|
||||
if ((uint16_t)read_16bitLE(0x00, txth->streamText) == 0xFFFE ||
|
||||
(uint16_t)read_16bitLE(0x00, txth->streamText) == 0xFEFF) {
|
||||
txt_offset = 0x02;
|
||||
}
|
||||
else if (((uint32_t)read_32bitBE(0x00, txth->streamText) & 0xFFFFFF00) == 0xEFBBBF00) {
|
||||
txt_offset = 0x03;
|
||||
}
|
||||
|
||||
/* read lines */
|
||||
while (txt_offset < file_size) {
|
||||
@ -450,7 +504,7 @@ static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_hea
|
||||
char key[TXT_LINE_MAX] = {0}, val[TXT_LINE_MAX] = {0}; /* at least as big as a line to avoid overflows (I hope) */
|
||||
int ok, bytes_read, line_done;
|
||||
|
||||
bytes_read = get_streamfile_text_line(TXT_LINE_MAX,line, txt_offset,streamText, &line_done);
|
||||
bytes_read = get_streamfile_text_line(TXT_LINE_MAX,line, txt_offset,txth->streamText, &line_done);
|
||||
if (!line_done) goto fail;
|
||||
|
||||
txt_offset += bytes_read;
|
||||
@ -460,49 +514,58 @@ static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_hea
|
||||
if (ok != 2) /* ignore line if no key=val (comment or garbage) */
|
||||
continue;
|
||||
|
||||
if (!parse_keyval(streamFile, streamText, txth, key, val)) /* read key/val */
|
||||
if (!parse_keyval(txth->streamFile, txth, key, val)) /* read key/val */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!txth->loop_flag_set)
|
||||
txth->loop_flag = txth->loop_end_sample && txth->loop_end_sample != 0xFFFFFFFF;
|
||||
|
||||
if (!txth->streamBody)
|
||||
goto fail;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_header * txth, const char * key, const 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);
|
||||
|
||||
if (0==strcmp(key,"codec")) {
|
||||
if (0==strcmp(val,"PSX")) txth->codec = PSX;
|
||||
else if (0==strcmp(val,"XBOX")) txth->codec = XBOX;
|
||||
else if (0==strcmp(val,"NGC_DTK")) txth->codec = NGC_DTK;
|
||||
else if (0==strcmp(val,"PCM16BE")) txth->codec = PCM16BE;
|
||||
else if (0==strcmp(val,"PCM16LE")) txth->codec = PCM16LE;
|
||||
else if (0==strcmp(val,"PCM8")) txth->codec = PCM8;
|
||||
else if (0==strcmp(val,"SDX2")) txth->codec = SDX2;
|
||||
else if (0==strcmp(val,"DVI_IMA")) txth->codec = DVI_IMA;
|
||||
else if (0==strcmp(val,"MPEG")) txth->codec = MPEG;
|
||||
else if (0==strcmp(val,"IMA")) txth->codec = IMA;
|
||||
else if (0==strcmp(val,"AICA")) txth->codec = AICA;
|
||||
else if (0==strcmp(val,"MSADPCM")) txth->codec = MSADPCM;
|
||||
else if (0==strcmp(val,"NGC_DSP")) txth->codec = NGC_DSP;
|
||||
else if (0==strcmp(val,"PCM8_U_int")) txth->codec = PCM8_U_int;
|
||||
else if (0==strcmp(val,"PSX_bf")) txth->codec = PSX_bf;
|
||||
else if (0==strcmp(val,"MS_IMA")) txth->codec = MS_IMA;
|
||||
else if (0==strcmp(val,"PCM8_U")) txth->codec = PCM8_U;
|
||||
else if (0==strcmp(val,"APPLE_IMA4")) txth->codec = APPLE_IMA4;
|
||||
else if (0==strcmp(val,"ATRAC3")) txth->codec = ATRAC3;
|
||||
else if (0==strcmp(val,"ATRAC3PLUS")) txth->codec = ATRAC3PLUS;
|
||||
else if (0==strcmp(val,"XMA1")) txth->codec = XMA1;
|
||||
else if (0==strcmp(val,"XMA2")) txth->codec = XMA2;
|
||||
else if (0==strcmp(val,"FFMPEG")) txth->codec = FFMPEG;
|
||||
else if (0==strcmp(val,"AC3")) txth->codec = AC3;
|
||||
if (0==strcmp(val,"PSX")) txth->codec = PSX;
|
||||
else if (0==strcmp(val,"XBOX")) txth->codec = XBOX;
|
||||
else if (0==strcmp(val,"NGC_DTK")) txth->codec = NGC_DTK;
|
||||
else if (0==strcmp(val,"DTK")) txth->codec = NGC_DTK;
|
||||
else if (0==strcmp(val,"PCM16BE")) txth->codec = PCM16BE;
|
||||
else if (0==strcmp(val,"PCM16LE")) txth->codec = PCM16LE;
|
||||
else if (0==strcmp(val,"PCM8")) txth->codec = PCM8;
|
||||
else if (0==strcmp(val,"SDX2")) txth->codec = SDX2;
|
||||
else if (0==strcmp(val,"DVI_IMA")) txth->codec = DVI_IMA;
|
||||
else if (0==strcmp(val,"MPEG")) txth->codec = MPEG;
|
||||
else if (0==strcmp(val,"IMA")) txth->codec = IMA;
|
||||
else if (0==strcmp(val,"AICA")) txth->codec = AICA;
|
||||
else if (0==strcmp(val,"MSADPCM")) txth->codec = MSADPCM;
|
||||
else if (0==strcmp(val,"NGC_DSP")) txth->codec = NGC_DSP;
|
||||
else if (0==strcmp(val,"DSP")) txth->codec = NGC_DSP;
|
||||
else if (0==strcmp(val,"PCM8_U_int")) txth->codec = PCM8_U_int;
|
||||
else if (0==strcmp(val,"PSX_bf")) txth->codec = PSX_bf;
|
||||
else if (0==strcmp(val,"MS_IMA")) txth->codec = MS_IMA;
|
||||
else if (0==strcmp(val,"PCM8_U")) txth->codec = PCM8_U;
|
||||
else if (0==strcmp(val,"APPLE_IMA4")) txth->codec = APPLE_IMA4;
|
||||
else if (0==strcmp(val,"ATRAC3")) txth->codec = ATRAC3;
|
||||
else if (0==strcmp(val,"ATRAC3PLUS")) txth->codec = ATRAC3PLUS;
|
||||
else if (0==strcmp(val,"XMA1")) txth->codec = XMA1;
|
||||
else if (0==strcmp(val,"XMA2")) txth->codec = XMA2;
|
||||
else if (0==strcmp(val,"FFMPEG")) txth->codec = FFMPEG;
|
||||
else if (0==strcmp(val,"AC3")) txth->codec = AC3;
|
||||
else goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"codec_mode")) {
|
||||
if (!parse_num(streamFile,val, &txth->codec_mode)) goto fail;
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->codec_mode)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"value_multiplier")) {
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->value_multiplier)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"interleave")) {
|
||||
if (0==strcmp(val,"half_size")) {
|
||||
@ -510,30 +573,32 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
|
||||
txth->interleave = txth->data_size / txth->channels;
|
||||
}
|
||||
else {
|
||||
if (!parse_num(streamFile,val, &txth->interleave)) goto fail;
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->interleave)) goto fail;
|
||||
}
|
||||
}
|
||||
else if (0==strcmp(key,"id_value")) {
|
||||
if (!parse_num(streamFile,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")) {
|
||||
if (!parse_num(streamFile,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 */
|
||||
goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"channels")) {
|
||||
if (!parse_num(streamFile,val, &txth->channels)) goto fail;
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->channels)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"sample_rate")) {
|
||||
if (!parse_num(streamFile,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")) {
|
||||
if (!parse_num(streamFile,val, &txth->start_offset)) goto fail;
|
||||
if (!txth->data_size_set)
|
||||
txth->data_size = get_streamfile_size(streamFile) - txth->start_offset; /* re-evaluate */
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->start_offset)) goto fail;
|
||||
if (!txth->data_size_set) {
|
||||
txth->data_size = !txth->streamBody ? 0 :
|
||||
get_streamfile_size(txth->streamBody) - txth->start_offset; /* re-evaluate */
|
||||
}
|
||||
}
|
||||
else if (0==strcmp(key,"data_size")) {
|
||||
if (!parse_num(streamFile,val, &txth->data_size)) goto fail;
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->data_size)) goto fail;
|
||||
txth->data_size_set = 1;
|
||||
}
|
||||
else if (0==strcmp(key,"sample_type")) {
|
||||
@ -548,7 +613,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
|
||||
txth->num_samples_data_size = 1;
|
||||
}
|
||||
else {
|
||||
if (!parse_num(streamFile,val, &txth->num_samples)) goto fail;
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->num_samples)) goto fail;
|
||||
if (txth->sample_type==1)
|
||||
txth->num_samples = get_bytes_to_samples(txth, txth->num_samples);
|
||||
if (txth->sample_type==2)
|
||||
@ -556,7 +621,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
|
||||
}
|
||||
}
|
||||
else if (0==strcmp(key,"loop_start_sample")) {
|
||||
if (!parse_num(streamFile,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)
|
||||
txth->loop_start_sample = get_bytes_to_samples(txth, txth->loop_start_sample);
|
||||
if (txth->sample_type==2)
|
||||
@ -569,7 +634,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
|
||||
txth->loop_end_sample = get_bytes_to_samples(txth, txth->data_size);
|
||||
}
|
||||
else {
|
||||
if (!parse_num(streamFile,val, &txth->loop_end_sample)) goto fail;
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->loop_end_sample)) goto fail;
|
||||
if (txth->sample_type==1)
|
||||
txth->loop_end_sample = get_bytes_to_samples(txth, txth->loop_end_sample);
|
||||
if (txth->sample_type==2)
|
||||
@ -579,7 +644,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
|
||||
txth->loop_end_sample += txth->loop_adjust;
|
||||
}
|
||||
else if (0==strcmp(key,"skip_samples")) {
|
||||
if (!parse_num(streamFile,val, &txth->skip_samples)) goto fail;
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->skip_samples)) goto fail;
|
||||
txth->skip_samples_set = 1;
|
||||
if (txth->sample_type==1)
|
||||
txth->skip_samples = get_bytes_to_samples(txth, txth->skip_samples);
|
||||
@ -587,7 +652,7 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
|
||||
txth->skip_samples = get_bytes_to_samples(txth, txth->skip_samples * (txth->interleave*txth->channels));
|
||||
}
|
||||
else if (0==strcmp(key,"loop_adjust")) {
|
||||
if (!parse_num(streamFile,val, &txth->loop_adjust)) goto fail;
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->loop_adjust)) goto fail;
|
||||
if (txth->sample_type==1)
|
||||
txth->loop_adjust = get_bytes_to_samples(txth, txth->loop_adjust);
|
||||
if (txth->sample_type==2)
|
||||
@ -598,28 +663,87 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h
|
||||
txth->loop_flag_auto = 1;
|
||||
}
|
||||
else {
|
||||
if (!parse_num(streamFile,val, &txth->loop_flag)) goto fail;
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->loop_flag)) goto fail;
|
||||
txth->loop_flag_set = 1;
|
||||
}
|
||||
}
|
||||
else if (0==strcmp(key,"coef_offset")) {
|
||||
if (!parse_num(streamFile,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")) {
|
||||
if (!parse_num(streamFile,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")) {
|
||||
if (val[0]=='B' && val[1]=='E')
|
||||
txth->coef_big_endian = 1;
|
||||
else if (val[0]=='L' && val[1]=='E')
|
||||
txth->coef_big_endian = 0;
|
||||
else if (!parse_num(streamFile,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")) {
|
||||
if (!parse_num(streamFile,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")) {
|
||||
if (!parse_num(streamFile,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")) {
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->subsong_count)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"subsong_offset")) {
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->subsong_offset)) goto fail;
|
||||
}
|
||||
else if (0==strcmp(key,"header_file")) {
|
||||
if (txth->streamhead_opened) {
|
||||
close_streamfile(txth->streamHead);
|
||||
txth->streamHead = NULL;
|
||||
txth->streamhead_opened = 0;
|
||||
}
|
||||
|
||||
if (0==strcmp(val,"null")) { /* reset */
|
||||
if (!txth->streamfile_is_txth) {
|
||||
txth->streamHead = txth->streamFile;
|
||||
}
|
||||
}
|
||||
else if (val[0]=='*' && val[1]=='.') { /* basename + extension */
|
||||
txth->streamHead = open_streamfile_by_ext(txth->streamFile, (val+2));
|
||||
if (!txth->streamHead) goto fail;
|
||||
txth->streamhead_opened = 1;
|
||||
}
|
||||
else { /* open file */
|
||||
fix_dir_separators(val); /* clean paths */
|
||||
|
||||
txth->streamHead = open_streamfile_by_filename(txth->streamFile, val);
|
||||
if (!txth->streamHead) goto fail;
|
||||
txth->streamhead_opened = 1;
|
||||
}
|
||||
}
|
||||
else if (0==strcmp(key,"body_file")) {
|
||||
if (txth->streambody_opened) {
|
||||
close_streamfile(txth->streamBody);
|
||||
txth->streamBody = NULL;
|
||||
txth->streambody_opened = 0;
|
||||
}
|
||||
|
||||
if (0==strcmp(val,"null")) { /* reset */
|
||||
if (!txth->streamfile_is_txth) {
|
||||
txth->streamBody = txth->streamFile;
|
||||
}
|
||||
}
|
||||
else if (val[0]=='*' && val[1]=='.') { /* basename + extension */
|
||||
txth->streamBody = open_streamfile_by_ext(txth->streamFile, (val+2));
|
||||
if (!txth->streamBody) goto fail;
|
||||
txth->streambody_opened = 1;
|
||||
}
|
||||
else { /* open file */
|
||||
fix_dir_separators(val); /* clean paths */
|
||||
|
||||
txth->streamBody = open_streamfile_by_filename(txth->streamFile, val);
|
||||
if (!txth->streamBody) goto fail;
|
||||
txth->streambody_opened = 1;
|
||||
}
|
||||
|
||||
txth->data_size = !txth->streamBody ? 0 :
|
||||
get_streamfile_size(txth->streamBody) - txth->start_offset; /* re-evaluate */
|
||||
}
|
||||
else {
|
||||
VGM_LOG("TXTH: unknown key=%s, val=%s\n", key,val);
|
||||
@ -631,27 +755,34 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_num(STREAMFILE * streamFile, 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 values, save before modifying */
|
||||
uint32_t value_multiplier = txth->value_multiplier;
|
||||
uint32_t subsong_offset = txth->subsong_offset;
|
||||
|
||||
if (val[0] == '@') { /* offset */
|
||||
uint32_t off = 0;
|
||||
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" : "@%u:%c%c$%i", &off, &ed1,&ed2, &size) != 4) goto fail;
|
||||
if (sscanf(val, hex ? "@%x:%c%c$%i" : "@%u:%c%c$%i", &offset, &ed1,&ed2, &size) != 4) goto fail;
|
||||
} else if (strchr(val,':')) {
|
||||
if (sscanf(val, hex ? "@%x:%c%c" : "@%u:%c%c", &off, &ed1,&ed2) != 3) goto fail;
|
||||
if (sscanf(val, hex ? "@%x:%c%c" : "@%u:%c%c", &offset, &ed1,&ed2) != 3) goto fail;
|
||||
} else if (strchr(val,'$')) {
|
||||
if (sscanf(val, hex ? "@%x$%i" : "@%u$%i", &off, &size) != 2) goto fail;
|
||||
if (sscanf(val, hex ? "@%x$%i" : "@%u$%i", &offset, &size) != 2) goto fail;
|
||||
} else {
|
||||
if (sscanf(val, hex ? "@%x" : "@%u", &off) != 1) goto fail;
|
||||
if (sscanf(val, hex ? "@%x" : "@%u", &offset) != 1) goto fail;
|
||||
}
|
||||
|
||||
if (off < 0 || off > get_streamfile_size(streamFile))
|
||||
if (offset < 0 || offset > get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
if (ed1 == 'B' && ed2 == 'E')
|
||||
@ -659,21 +790,28 @@ static int parse_num(STREAMFILE * streamFile, const char * val, uint32_t * out_v
|
||||
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(off,streamFile); break;
|
||||
case 2: *out_value = big_endian ? (uint16_t)read_16bitBE(off,streamFile) : (uint16_t)read_16bitLE(off,streamFile); break;
|
||||
case 3: *out_value = (big_endian ? (uint32_t)read_32bitBE(off,streamFile) : (uint32_t)read_32bitLE(off,streamFile)) & 0x00FFFFFF; break;
|
||||
case 4: *out_value = big_endian ? (uint32_t)read_32bitBE(off,streamFile) : (uint32_t)read_32bitLE(off,streamFile); break;
|
||||
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 { /* constant */
|
||||
int hex = (val[0]=='0' && val[1]=='x');
|
||||
|
||||
if (sscanf(val, hex ? "%x" : "%u", out_value)!=1) goto fail;
|
||||
if (sscanf(val, hex ? "%x" : "%u", out_value)!=1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//VGM_LOG("TXTH: value=%s, read %u\n", val, *out_value);
|
||||
if (value_multiplier)
|
||||
*out_value = (*out_value) * value_multiplier;
|
||||
|
||||
//;VGM_LOG("TXTH: val=%s, read %u (0x%x)\n", val, *out_value, *out_value);
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user