mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 01:30:49 +01:00
976e1f3efe
- 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
255 lines
11 KiB
Markdown
255 lines
11 KiB
Markdown
# TXTH FORMAT
|
|
|
|
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 (for instance "bgm01.snd"), vgmstream tries to find a TXTH header in the same dir, in this order:
|
|
- (filename.ext).txth
|
|
- .(ext).txth
|
|
- .txth
|
|
|
|
If found and parsed correctly (the TXTH may be rejected if incorrect commands are found) vgmstream will try to play the file as described. Extension must be accepted/added to vgmstream (plugins like foobar2000 only load extensions from a whitelist in formats.c), or one could rename to any supported extension (like .vgmstream), or leave the file extensionless.
|
|
|
|
|
|
## Example of a TXTH file
|
|
For an unsupported bgm01.vag this would be a simple TXTH for it:
|
|
```
|
|
id_value = 0x534E4420 #test that file starts with "SND "
|
|
id_offset = @0x00:BE #test is done at offset 0, big endian value
|
|
codec = PSX
|
|
sample_rate = @0x10$2 #get sample rate at offset 0x10, 16 bit value
|
|
channels = @0x14 #get number of channels at offset 14
|
|
interleave = 0x1000 #fixed value
|
|
start_offset = 0x100
|
|
num_samples = data_size #find automatically number of samples in the file
|
|
loop_flag = auto
|
|
```
|
|
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
|
|
|
|
```
|
|
######################################################
|
|
|
|
# The file is made lines of "key = value" describing a header.
|
|
# 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 (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)
|
|
# * $1|2|3|4: value has size of 8/16/24/32 bit (optional, defaults to 4)
|
|
# Examples: @0x10:BE$2 (get big endian 16b value at 0x10)
|
|
# - {string}: special values for certain keys, described below
|
|
|
|
# Codec used to encode the data [REQUIRED]
|
|
# Accepted codec strings:
|
|
# - PSX PlayStation ADPCM
|
|
# - XBOX Xbox IMA ADPCM
|
|
# - 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)
|
|
# - IMA IMA ADPCM
|
|
# - AICA Yamaha AICA ADPCM (Dreamcast)
|
|
# - MSADPCM Microsoft ADPCM (Windows)
|
|
# - 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 8-bit unsigned
|
|
# - APPLE_IMA4 Apple Quicktime IMA ADPCM
|
|
# - ATRAC3 Sony ATRAC3
|
|
# - ATRAC3PLUS Sony ATRAC3plus
|
|
# - XMA1 Microsoft XMA1
|
|
# - XMA2 Microsoft XMA2
|
|
# - FFMPEG Any headered FFmpeg format
|
|
# - AC3 AC3/SPDIF
|
|
codec = (codec string)
|
|
|
|
# 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]
|
|
# - 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)|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 (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)
|
|
# 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
|
|
|
|
# Various sample values [REQUIRED (num_samples) / OPTIONAL (rest)]
|
|
num_samples = (number)|(offset)|data_size
|
|
loop_start_sample = (number)|(offset)
|
|
loop_end_sample = (number)|(offset)|data_size
|
|
|
|
# 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
|
|
loop_flag = (number)|(offset)|auto
|
|
|
|
# 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 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 (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
|
|
```
|