Merge pull request #292 from bnnm/hca-bcstm-metas-docs

hca, bcstm, metas, docs
This commit is contained in:
Christopher Snowhill 2018-09-02 15:12:49 -07:00 committed by GitHub
commit 7b30d8bc9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 3482 additions and 2425 deletions

View File

@ -27,6 +27,7 @@
<SccProvider>
</SccProvider>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
<TargetName>test</TargetName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">

132
doc/BUILD.md Normal file
View File

@ -0,0 +1,132 @@
# vgmstream build help
## Compilation requirements
**GCC / Make**: In Windows this means one of these two somewhere in PATH:
- MinGW-w64 (32bit version): https://sourceforge.net/projects/mingw-w64/
- Use this for easier standalone executables
- Latest online installer with any config should work (for example: gcc-7.2.0, i686, win32, sjlj).
- MSYS2 with the MinGW-w64_shell (32bit) package: https://msys2.github.io/
**MSVC / Visual Studio**: Microsoft's Visual C++ and MSBuild, bundled in either:
- Visual Studio (2015/2017/latest): https://www.visualstudio.com/downloads/
- Visual Studio Community should work (free, but must register after trial period)
- Visual C++ Build Tools (no IDE): http://landinghub.visualstudio.com/visual-cpp-build-tools
**Git**: optional, to generate version numbers:
- Git for Windows: https://git-scm.com/download/win
## Compiling modules
### CLI (test.exe/vgmstream-cli) / Winamp plugin (in_vgmstream) / XMPlay plugin (xmp-vgmstream)
**With GCC**: use the *./Makefile* in the root folder, see inside for options. For compilation flags check the *Makefile* in each folder.
You may need to manually rebuild if you change a *.h* file (use *make clean*).
In Linux, Makefiles can be used to cross-compile with the MingW headers, but may not be updated to generate native code at the moment. It should be fixable with some effort. Autotools should build it as vgmstream-cli instead (see the Audacious section).
Windows CMD example:
```
prompt $P$G$_$S
set PATH=C:\Program Files (x86)\Git\usr\bin;%PATH%
set PATH=C:\Program Files (x86)\mingw-w64\i686-5.4.0-win32-sjlj-rt_v5-rev0\mingw32\bin;%PATH%
cd vgmstream
mingw32-make.exe vgmstream_cli -f Makefile ^
VGM_ENABLE_FFMPEG=1 ^
SHELL=sh.exe CC=gcc.exe AR=ar.exe STRIP=strip.exe DLLTOOL=dlltool.exe WINDRES=windres.exe
```
**With MSVC**: To build in Visual Studio, run *./init-build.bat*, open *./vgmstream_full.sln* and compile. To build from the command line, run *./build.bat*.
The build script will automatically handle obtaining dependencies and making the project changes listed in the foobar2000 section (you may need to get some PowerShell .NET packages).
You can also call MSBuild directly in the command line (see the foobar2000 section for dependencies and examples)
### foobar2000 plugin (foo\_input\_vgmstream)
Requires MSVC (foobar/SDK only links to MSVC C++ DLLs) and these dependencies:
- foobar2000 SDK (2018), in *(vgmstream)/dependencies/foobar/*: http://www.foobar2000.org/SDK
- FDK-AAC, in *(vgmstream)/dependencies/fdk-aac/*: https://github.com/kode54/fdk-aac
- QAAC, in *(vgmstream)/dependencies/qaac/*: https://github.com/kode54/qaac
- WTL (if needed), in *(vgmstream)/dependencies/WTL/*: http://wtl.sourceforge.net/
The following project modifications are required:
- For *foobar2000_ATL_helpers* add *../../../WTL/Include* to the compilers's *additional includes*
FDK-AAC/QAAC can be safely disabled by removing *VGM_USE_MP4V2* and *VGM_USE_FDKAAC* in the compiler/linker options and the project dependencies, as FFmpeg is used instead to support their codecs.
You can also manually use the command line to compile with MSBuild, if you don't want to touch the .vcxproj files, register VS after trial, get PowerShell dependencies for the build script, or only have VC++/MSBuild tools.
Windows CMD example for foobar2000:
```
prompt $P$G$_$S
set PATH=C:\Program Files (x86)\Git\usr\bin;%PATH%
set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH%
cd vgmstream
set CL=/I"C:\projects\WTL\Include"
set LINK="C:\projects\foobar\foobar2000\shared\shared.lib"
msbuild fb2k/foo_input_vgmstream.vcxproj ^
/t:Clean
msbuild fb2k/foo_input_vgmstream.vcxproj ^
/t:Build ^
/p:Platform=Win32 ^
/p:PlatformToolset=v140 ^
/p:Configuration=Release ^
/p:DependenciesDir=../..
```
### Audacious plugin
Requires the dev version of Audacious (and dependencies), automake/autoconf, and gcc/make (C++11). It must be compiled and installed into Audacious, where it should appear in the plugin list as "vgmstream".
The plugin needs Audacious 3.5 or higher. New Audacious releases can break plugin compatibility so it may not work with the latest version unless adapted first.
libvorbis and libmpg123 will be used if found, while FFmpeg and other external libraries aren't enabled, thus some formats won't work.
Windows builds aren't supported at the moment (should be possible but there are complex dependency chains).
Terminal example, assuming a Ubuntu-based Linux distribution:
```
# build requirements
sudo apt-get update
sudo apt-get install gcc g++ make
sudo apt-get install autoconf automake libtool
sudo apt-get install git
# vgmstream dependencies
sudo apt-get install libmpg123-dev libvorbis-dev
# Audacious player and dependencies
sudo apt-get install audacious
sudo apt-get install audacious-dev libglib2.0-dev libgtk2.0-dev libpango1.0-dev
# check Audacious version >= 3.5
pkg-config --modversion audacious
# build
git clone https://github.com/kode54/vgmstream
cd vgmstream
./bootstrap
./configure
make -f Makefile.autotools
# copy to audacious plugins
sudo make -f Makefile.autotools install
# optional post-cleanup
make -f Makefile.autotools clean
find . -name ".deps" -type d -exec rm -r "{}" \;
./unbootstrap
## WARNING, removes *all* untracked files not in .gitignore
git clean -fd
```
# vgmstream123 player
Should be buildable with Autotools, much like the Audacious plugin, though requires libao (libao-dev).
Windows builds are possible with libao.dll and includes, but some features are disabled.

View File

@ -1,153 +1,37 @@
# vgmstream
# vgmstream development help
## Compilation requirements
**GCC / Make**: In Windows this means one of these two somewhere in PATH:
- MinGW-w64 (32bit version): https://sourceforge.net/projects/mingw-w64/
- Use this for easier standalone executables
- Latest online installer with any config should work (for example: gcc-7.2.0, i686, win32, sjlj).
- MSYS2 with the MinGW-w64_shell (32bit) package: https://msys2.github.io/
**MSVC / Visual Studio**: Microsoft's Visual C++ and MSBuild, bundled in either:
- Visual Studio (2015/2017/latest): https://www.visualstudio.com/downloads/
- Visual Studio Community should work (free, but must register after trial period)
- Visual C++ Build Tools (no IDE): http://landinghub.visualstudio.com/visual-cpp-build-tools
**Git**: optional, to generate version numbers:
- Git for Windows: https://git-scm.com/download/win
## Compiling modules
### CLI (test.exe/vgmstream-cli) / Winamp plugin (in_vgmstream) / XMPlay plugin (xmp-vgmstream)
**With GCC**: use the *./Makefile* in the root folder, see inside for options. For compilation flags check the *Makefile* in each folder.
You may need to manually rebuild if you change a *.h* file (use *make clean*).
In Linux, Makefiles can be used to cross-compile with the MingW headers, but may not be updated to generate native code at the moment. It should be fixable with some effort. Autotools should build it as vgmstream-cli instead (see the Audacious section).
Windows CMD example:
```
prompt $P$G$_$S
set PATH=C:\Program Files (x86)\Git\usr\bin;%PATH%
set PATH=C:\Program Files (x86)\mingw-w64\i686-5.4.0-win32-sjlj-rt_v5-rev0\mingw32\bin;%PATH%
cd vgmstream
mingw32-make.exe vgmstream_cli -f Makefile ^
VGM_ENABLE_FFMPEG=1 ^
SHELL=sh.exe CC=gcc.exe AR=ar.exe STRIP=strip.exe DLLTOOL=dlltool.exe WINDRES=windres.exe
```
**With MSVC**: To build in Visual Studio, run *./init-build.bat*, open *./vgmstream_full.sln* and compile. To build from the command line, run *./build.bat*.
The build script will automatically handle obtaining dependencies and making the project changes listed in the foobar2000 section (you may need to get some PowerShell .NET packages).
You can also call MSBuild directly in the command line (see the foobar2000 section for dependencies and examples)
### foobar2000 plugin (foo\_input\_vgmstream)
Requires MSVC (foobar/SDK only links to MSVC C++ DLLs) and these dependencies:
- foobar2000 SDK (2018), in *(vgmstream)/dependencies/foobar/*: http://www.foobar2000.org/SDK
- FDK-AAC, in *(vgmstream)/dependencies/fdk-aac/*: https://github.com/kode54/fdk-aac
- QAAC, in *(vgmstream)/dependencies/qaac/*: https://github.com/kode54/qaac
- WTL (if needed), in *(vgmstream)/dependencies/WTL/*: http://wtl.sourceforge.net/
The following project modifications are required:
- For *foobar2000_ATL_helpers* add *../../../WTL/Include* to the compilers's *additional includes*
FDK-AAC/QAAC can be safely disabled by removing *VGM_USE_MP4V2* and *VGM_USE_FDKAAC* in the compiler/linker options and the project dependencies, as FFmpeg is used instead to support their codecs.
You can also manually use the command line to compile with MSBuild, if you don't want to touch the .vcxproj files, register VS after trial, get PowerShell dependencies for the build script, or only have VC++/MSBuild tools.
Windows CMD example for foobar2000:
```
prompt $P$G$_$S
set PATH=C:\Program Files (x86)\Git\usr\bin;%PATH%
set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH%
cd vgmstream
set CL=/I"C:\projects\WTL\Include"
set LINK="C:\projects\foobar\foobar2000\shared\shared.lib"
msbuild fb2k/foo_input_vgmstream.vcxproj ^
/t:Clean
msbuild fb2k/foo_input_vgmstream.vcxproj ^
/t:Build ^
/p:Platform=Win32 ^
/p:PlatformToolset=v140 ^
/p:Configuration=Release ^
/p:DependenciesDir=../..
```
### Audacious plugin
Requires the dev version of Audacious (and dependencies), automake/autoconf, and gcc/make (C++11). It must be compiled and installed into Audacious, where it should appear in the plugin list as "vgmstream".
The plugin needs Audacious 3.5 or higher. New Audacious releases can break plugin compatibility so it may not work with the latest version unless adapted first.
libvorbis and libmpg123 will be used if found, while FFmpeg and other external libraries aren't enabled, thus some formats won't work.
Windows builds aren't supported at the moment (should be possible but there are complex dependency chains).
Terminal example, assuming a Ubuntu-based Linux distribution:
```
# build requirements
sudo apt-get update
sudo apt-get install gcc g++ make
sudo apt-get install autoconf automake libtool
sudo apt-get install git
# vgmstream dependencies
sudo apt-get install libmpg123-dev libvorbis-dev
# Audacious player and dependencies
sudo apt-get install audacious
sudo apt-get install audacious-dev libglib2.0-dev libgtk2.0-dev libpango1.0-dev
# check Audacious version >= 3.5
pkg-config --modversion audacious
# build
git clone https://github.com/kode54/vgmstream
cd vgmstream
./bootstrap
./configure
make -f Makefile.autotools
# copy to audacious plugins
sudo make -f Makefile.autotools install
# optional post-cleanup
make -f Makefile.autotools clean
find . -name ".deps" -type d -exec rm -r "{}" \;
./unbootstrap
## WARNING, removes *all* untracked files not in .gitignore
git clean -fd
```
# vgmstream123 player
Should be buildable with Autotools, much like the Audacious plugin, though requires libao (libao-dev).
Windows builds are possible with libao.dll and includes, but some features are disabled.
## Development
### Code
## Code
vgmstream uses C (C89 when possible), and C++ for the foobar2000 and Audacious plugins.
C should be restricted to features VS2010 understands. This mainly means declaring variables at the start of a { .. } block (declare+initialize is fine, as long as it doesn't reference variables declared in the same block) and avoiding C99 features like variable-length arrays (but certain others like // comments are fine).
There are no hard coding rules but for consistency should follow general C conventions and the style used in most files: 4 spaces instead of tabs, underscore_and_lowercase_names, brackets starting in the same line (`if (..) { CRLF ... }`), etc. Some of the code may be a bit inefficient or duplicated at places, but it isn't much of a problem if gives clarity. vgmstream's performance is fast enough (as it mainly deals with playing songs in real time) so that favors clarity over optimization. Similarly some code may segfault or even cause infinite loops on bad data, but it's fixed as encountered rather than worrying to much about improbable cases.
There are no hard coding rules but for consistency one could follow the style used in most files:
- general C conventions
- 4 spaces instead of tabs
- underscore_and_lowercase_names instead of CamelCase
- /* C89 comments */ for general comments, //C99 comments for special comments (like disabling code but leaving it there for visibility)
- brackets starting in the same line
ex `if (..) { CRLF ... }`
- line length ~100, more is ok for 'noise code' (uninteresting calcs or function defs)
- offsets/sizes in hex, counts/numbers in decimal
- test functions may return 1=ok, 0=ko for simplicity.
- free(ptr) no need to NULL-check per standard, close_stuff(ptr) should follow when possible
- lowercase_helper_structs, UPPERCASE_MAIN_STRUCTS
- spaces in calcs/ifs/etc may be added as desired for clarity
ex. `if (simple_check)` or `if ( complex_and_important_stuff(weird + weird) )`
But other styles may be found, this isn't very important as most files are isolated.
Some of the code may be a bit inefficient or duplicated at places, but it isn't much of a problem if gives clarity. vgmstream's performance is fast enough (as it mainly deals with playing songs in real time) so that favors clarity over optimization. Similarly, some code may segfault or even cause infinite loops on bad data, but it's fixed as encountered rather than worrying too much about improbable cases.
### Structure
## Structure
```
./ docs, scripts
./ scripts
./audacious/ Audacious plugin
./cli/ CLI tools
./doc/ docs
./ext_includes/ external includes for compiling
./ext_libs/ external libs/DLLs for linking
./fb2k/ foobar2000 plugin
@ -159,7 +43,7 @@ There are no hard coding rules but for consistency should follow general C conve
./xmplay/ XMPlay plugin
```
### Overview
## Overview
vgmstream works by parsing a music stream header (*meta/*), preparing/controlling data and sample buffers (*layout/*) and decoding the compressed data into listenable PCM samples (*coding/*).
Very simplified it goes like this:
@ -174,21 +58,29 @@ Very simplified it goes like this:
- layout moves offsets back to loop_start when loop_end is reached *[vgmstream_do_loop]*
- player closes the VGMSTREAM once the stream is finished
### Components
vgsmtream's main code (located in src) may be considered "libvgmstream", and plugins interface it through vgmstream.h, mainly the part commented as "vgmstream public API". There isn't a clean external API at the moment, this may be improved later.
#### STREAMFILEs
## Components
### STREAMFILEs
Structs with I/O callbacks that vgmstream uses in place of stdio/FILEs. All I/O must be done through STREAMFILEs as it lets plugins set up their own. This includes reading data or opening other STREAMFILEs (ex. when a header has companion files that need to be parsed, or during setup).
Players should open a base STREAMFILE and pass it to init_vgmstream. Once it's done this STREAMFILE must be closed, as internally vgmstream opens its own copy (using the base one's callbacks).
For optimization purposes vgmstream may open a copy of the FILE per channel, as each has its own I/O buffer, and channel data can be too separate to fit a single buffer.
Custom STREAMFILEs wrapping base STREAMFILEs may be used for complex I/O cases (ex. if data needs decryption, parts needs to be skipped on the fly, or a file is composed of multiple sub-files), as some codecs are not easy to feed chunked data.
Custom STREAMFILEs wrapping base STREAMFILEs may be used for complex I/O cases:
- file is a container of another format (`fakename/clamp_streamfile`)
- data needs decryption (`io_streamfile`)
- data must be expanded/reduced on the fly for codecs that are not easy to feed chunked data (`io_streamfile`)
- data is divided in multiple physical files, but must be read as a single (`multifile_streamfile`)
Certain metas combine those streamfiles together with special layouts to support very complex cases, that would require massive changes in vgmstream to support in a cleaner (possible undesirable) way.
#### VGMSTREAM
### VGMSTREAM
The VGMSTREAM (caps) is the main struct created during init when a file is successfully recognized and parsed. It holds the file's configuration (channels, sample rate, decoder, layout, samples, loop points, etc) and decoder state (STREAMFILEs, offsets per channel, current sample, etc), and is used to interact with the API.
#### metas
### metas
Metadata (header) parsers that identify and handle formats.
To add a new one:
@ -210,7 +102,7 @@ Different formats may use the same extension but this isn't a problem as long as
If the format supports subsongs it should read the stream index (subsong number) in the passed STREAMFILE, and use it to parse a section of the file. Then it must report the number of subsongs in the VGMSTREAM, to signal this feature is enabled. The index is 1-based (first subsong is 1, 0 is default/first). This makes possible to directly use bank-like formats like .FSB, and while vgmstream could technically support any container (like generic bigfiles or even .zip) it should be restricted to files that actually are audio banks.
#### layouts
### layouts
Layouts control most of the main logic:
- receive external buffer to fill with PCM samples
- detect when looping must be done
@ -220,16 +112,18 @@ Layouts control most of the main logic:
- repeat until buffer is filled
Available layouts, depending on how codec data is laid out:
- none/flat: straight data. Decoder should handle channel offsets and other details normally.
- flat: straight data. Decoder should handle channel offsets and other details normally.
- interleave: one data block per channel, mixed in configurable sizes. Once one channel block is fully decoded this layout skips the other channels, so the decoder only handles one at a time.
- blocked: data is divided into blocks, often with a header. Layout detects when a block is done and asks a helper function to fix offsets (skipping the header and pointing to data per channel), depending on the block format.
- others: uncommon cases may need its own custom layout (ex.- multistream/subfiles)
- segmented: file is divided into consecutive but separate segments, each one is setup as a fully separate VGMSTREAM.
- layered: file is divided into multichannel layers that play at the same time, each one is setup as a fully separate VGMSTREAM.
- others: uncommon cases may need its own custom layout, but may be dealt with using custom IO STREAMFILEs instead.
The layout used mainly depends on the decoder. MP3 data (that may have 1 or 2 channels per frame) uses flat layout, while DSP ADPCM (that only decodes one channel at a time) is interleaved. In case of mono files either could be used as there won't be any actual difference.
Layouts expect the VGMSTREAM to be properly initialized during the meta processing (channel offsets must point to each channel start offset).
#### decoders
### decoders
Decoders take a sample buffer, convert data to PCM samples and fill one or multiple channels at a time, depending on the decoder itself. Usually its data is divided into frames with a number of samples, and should only need to do one frame at a time (when size is fixed/informed; vgmstream gives flexibility to the decoder), but must take into account that the sample buffer may be smaller than the frame samples, and that may start some samples into the frame.
Every call the decoder will need to find out the current frame offset (usually per channel). This is usually done with a base channel offset (from the VGMSTREAM) plus deriving the frame number (thus sub-offset, but only if frames are fixed) through the current sample, or manually updating the channel offsets every frame. This second method is not suitable to use with the interleave layout as it advances the offsets assuming they didn't change (this is a limitation/bug at the moment). Similarly, the blocked layout cannot contain interleaved data, and must use alt decoders with internal interleave (also a current limitation). Thus, some decoders and layouts don't mix.
@ -252,7 +146,7 @@ Adding a new decoder involves:
- *src/vgmstream.c*: add parser init to the init list
- if the codec depends on a external library don't forget to mark parts with: *#ifdef VGM_USE_X ... #endif*
#### core
### core
The vgmstream core simply consists of functions gluing the above together and some helpers (ex.- extension list, loop adjust, etc).
The *Overview* section should give an idea about how it's used.

9
doc/GENH.md Normal file
View File

@ -0,0 +1,9 @@
# GENH FORMAT
GENH is a generic binary header with fixed values, to make unsupported files playable. This binary header is appended to the beginning of a file and file is renamed to .genh, so vgmstream will read the GENH header and play the file with the new info.
GENH as been mostly superseded by TXTH, as it can do anything that GENH does and more, plus it's cleaner and much simpler to create, so TXTH is the recommended way to make vgmstream play unsupported files.
If you still want to create files with GENH headers, the easiest way is to use VGMToolBox's GENH creator, that provides a simple Windows interface.
For programmers looking for a formal definition the place to check would be vgmstream's parser, located in `genh.c` (particularly `parse_genh`), as new features or fixes may be added anytime.

153
doc/TXTH.md Normal file
View File

@ -0,0 +1,153 @@
# 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, 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
```
######################################################
# 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.
# 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)
# - (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 Nintendo ADP/DTK ADPCM
# - PCM16BE PCM RAW 16bit big endian
# - PCM16LE PCM RAW 16bit little endian
# - PCM8 PCM RAW 8bit
# - 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 Nintengo 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
# - APPLE_IMA4 Apple Quicktime IMA ADPCM
# - ATRAC3 raw ATRAC3
# - ATRAC3PLUS raw ATRAC3PLUS
# - XMA1 raw XMA1
# - XMA2 raw XMA2
# - FFMPEG any headered FFmpeg format
codec = (codec string)
# Varies with 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, 1=force mono/interleave mode
# - others: ignored
codec_mode = (number)
# Interleave or block size [REQUIRED/OPTIONAL, depends on codec]
# Interleave 0 means "stereo mode" for some codecs (IMA, AICA, etc)
interleave = (number)|(offset)
# 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.
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
# 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
# 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]
# 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)
skip_samples = (number)|(offset)
# DSP coefficients [REQUIRED for NGC_DSP]
# Coefs start
coef_offset = (number)|(offset)
# offset separation per channel, usually 0x20
# - Example: 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)
```

128
doc/TXTP.md Normal file
View File

@ -0,0 +1,128 @@
# TXTP FORMAT
TXTP is a mini-playlist file for various purposes, mainly to make more playable certain games that use uncommon ways to play their audio.
Simply create a file named `(filename).txtp`, and inside write the commands described below.
## TXTP features
### Play separate intro + loop files together as a single track
- __Ratchet & Clank (PS2)__: _bgm01.txtp_
```
# define several files to play as one (there is no limit)
BGM01_BEGIN.VAG
BGM01_LOOPED.VAG
# multi-files must define loops
loop_start_segment = 2 # 2nd file start
loop_end_segment = 2 # optional, default is last
#channel number must be equal, mixing sample rates is ok (uses first)
```
### Minifiles for bank formats without splitters
- __Super Robot Taisen OG Saga - Masou Kishin III - Pride of Justice (Vita)__: _bgm_12.txtp_
```
# select subsong 12
bigfiles/bgm.sxd2#12 #relative paths are ok too for TXTP
# single files loop normally by default
# if loop segment is defined it forces a full loop (0..num_samples)
#loop_start_segment = 1
```
### Play segmented subsongs as one
- __Prince of Persia Sands of Time__: _song_01.txtp_
```
# can use ranges ~ to avoid so much C&P
amb_fx.sb0#254
amb_fx.sb0#122~144
amb_fx.sb0#121 #notice "#" works as config or comment
#3rd segment = subsong 123, not 3rd subsong
loop_start_segment = 3
```
### Channel mask for channel subsongs/layers
- __Final Fantasy XIII-2__: _music_Home_01.ps3.txtp_
```
#plays channels 1 and 2 = 1st subsong
music_Home.ps3.scd#c1,2
```
- __Final Fantasy XIII-2__: _music_Home_02.ps3.txtp_
```
#plays channels 3 and 4 = 2nd subsong
music_Home.ps3.scd#c3,4
# song still has 4 channels, just mutes some
```
### Multilayered songs
TXTP "layers" play songs with channels/parts divided into files as one
- __Nier Automata__: _BGM_0_012_song2.txtp_
```
# mix dynamic sections (2ch * 2)
BGM_0_012_04.wem
BGM_0_012_07.wem
mode = layers
```
- __Life is Strange__: _BIK_E1_6A_DialEnd.txtp_
```
# bik multichannel isn't autodetectable so must mix manually (1ch * 3)
BIK_E1_6A_DialEnd_00000000.audio.multi.bik#1
BIK_E1_6A_DialEnd_00000000.audio.multi.bik#2
BIK_E1_6A_DialEnd_00000000.audio.multi.bik#3
mode = layers
```
### Channel mapping
TXTP can swap channels for custom channel mappings. Note that channel masking applies after mappings. Format is:
```
#ch1 = first
file1.ext#m2-3 # "FL BL FR BR" to "FL FR BL BR"
#do note the order specified affects swapping
file2.ext#m2-3,4-5,4-6 # ogg "FL CN FR BL BR SB" to wav "FL FR CN SB BL BR"
```
### Force plugin extensions
vgmstream supports a few common extensions that confuse plugins, like .wav/ogg/aac/opus/etc, so for them those extensions are disabled and are expected to be renamed to .lwav/logg/laac/lopus/etc. TXTP can make plugins play those disabled extensions, since it calls files directly by filename.
Combined with TXTH, this can also be used for extensions that aren't normally accepted by vgmstream.
### TXTP combos
TXTP may even reference other TXTP, or files that require TXTH, for extra complex cases. Each file defined in TXTP is internally parsed like it was a completely separate file, so there is a bunch of valid ways to mix them.
## Mini TXTP
To simplify TXTP creation, if the .txtp is empty (0 bytes) its filename is used directly as a command.
- _bgm.sxd2#12.txtp_: plays subsong 12
- _Ryoshima Coast 1 & 2.aix#c1,2.txtp_: channel mask
- etc
## Other examples
_Join "segments" (intro+body):_
```
#files must have same number of channels
Song001_intro.ogg
Song001_body.ogg
loop_start_segment = 2
```
_Join "layers" (ex. main+vocals):_
```
#files must have same number of samples
Song001_main.ogg
Song001_vocals.ogg
mode = layers
```

View File

@ -5,61 +5,82 @@
extern "C" {
#endif
enum { clHCA_samplesPerBlock = 0x80 * 8 };
/* Must pass at least 8 bytes of data to this function. Returns -1 on non-match, or
* positive byte count on success. */
int clHCA_isOurFile0(const void *data);
/* Must pass a full header block for success. Returns 0 on success, -1 on failure. */
int clHCA_isOurFile1(const void *data, unsigned int size);
/* Must pass at least 8 bytes of data to this function.
* Returns <0 on non-match, or header size on success. */
int clHCA_isOurFile(const void *data, unsigned int size);
/* The opaque state structure. */
typedef struct clHCA clHCA;
/* In case you wish to allocate the structure on your own. */
/* In case you wish to allocate and reset the structure on your own. */
int clHCA_sizeof();
void clHCA_clear(clHCA *, unsigned int ciphKey1, unsigned int ciphKey2);
void clHCA_clear(clHCA *);
void clHCA_done(clHCA *);
/* Or you could let the library allocate it. */
clHCA * clHCA_new(unsigned int ciphKey1, unsigned int ciphKey2);
clHCA * clHCA_new();
void clHCA_delete(clHCA *);
/* Requires a pre-allocated data structure.
* Before any decoding may be performed, the header block must be passed in.
* The recommended way of doing this is to detect the header length with
* clHCA_isOurFile0, validate the header with clHCA_isOurFile1, then pass
* it to this function, with the address of 0.
* Subsequent decodes with non-zero address are assumed to be sample blocks,
* and should be of the blockSize returned by the clHCA_getInfo function.
* Returns 0 on success, -1 on failure. */
int clHCA_Decode(clHCA *, void *data, unsigned int size, unsigned int address);
/* This is the simplest decode function, for signed and clipped 16 bit samples.
* May be called after clHCA_Decode, and will return the same data until the next
* block of sample data is passed to clHCA_Decode. */
void clHCA_DecodeSamples16(clHCA *, signed short * outSamples);
/* Parses the HCA header. Must be called before any decoding may be performed,
* and size must be at least headerSize long. The recommended way is to detect
* the header length with clHCA_isOurFile, then read data and call this.
* May be called multiple times to reset decoder state.
* Returns 0 on success, <0 on failure. */
int clHCA_DecodeHeader(clHCA *, void *data, unsigned int size);
typedef struct clHCA_stInfo {
unsigned int version;
unsigned int dataOffset;
unsigned int headerSize;
unsigned int samplingRate;
unsigned int channelCount;
unsigned int blockSize;
unsigned int blockCount;
unsigned int encoderDelay; /* samples appended to the beginning */
unsigned int encoderPadding; /* samples appended to the end */
unsigned int loopEnabled;
unsigned int loopStart;
unsigned int loopEnd;
unsigned int loopStartBlock;
unsigned int loopEndBlock;
unsigned int loopStartDelay; /* samples in block before loop starts */
unsigned int loopEndPadding; /* samples in block after loop ends */
unsigned int samplesPerBlock; /* should be 1024 */
const char *comment;
unsigned int encryptionEnabled; /* requires keycode */
/* Derived sample formulas:
* - sample count: blockCount*samplesPerBlock - encoderDelay - encoderPadding;
* - loop start sample = loopStartBlock*samplesPerBlock - encoderDelay + loopStartDelay
* - loop end sample = loopEndBlock*samplesPerBlock - encoderDelay + (samplesPerBlock - info.loopEndPadding)
*/
} clHCA_stInfo;
/* Retrieve information relevant for decoding and playback with this function.
* Must be called after successfully decoding a header block with clHCA_Decode,
* or else it will fail.
* Returns 0 on success, -1 on failure. */
/* Retrieves header information for decoding and playback (it's the caller's responsability
* to apply looping, encoder delay/skip samples, etc). May be called after clHCA_DecodeHeader.
* Returns 0 on success, <0 on failure. */
int clHCA_getInfo(clHCA *, clHCA_stInfo *out);
/* Decodes a single frame, from data after headerSize. Should be called after
* clHCA_DecodeHeader and size must be at least blockSize long.
* Returns 0 on success, <0 on failure. */
int clHCA_DecodeBlock(clHCA *, void *data, unsigned int size);
/* Extracts signed and clipped 16 bit samples into sample buffer.
* May be called after clHCA_DecodeBlock, and will return the same data until
* next decode. Buffer must be at least (samplesPerBlock*channels) long. */
void clHCA_ReadSamples16(clHCA *, signed short * outSamples);
/* Sets a 64 bit encryption key, to properly decode blocks. This may be called
* multiple times to change the key, before or after clHCA_DecodeHeader.
* Key is ignored if the file is not encrypted. */
void clHCA_SetKey(clHCA *, unsigned long long keycode);
/* Tests a single frame for validity, mainly to test if current key is correct.
* Returns <0 on incorrect block (wrong key), 0 on silent block (not useful to determine)
* and >0 if block is correct (the closer to 1 the more likely).
* Incorrect keys may give a few valid frames, so it's best to test a number of them
* and select the key with scores closer to 1. */
int clHCA_TestBlock(clHCA *hca, void *data, unsigned int size);
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -16,14 +16,15 @@ struct atrac9_codec_data {
int samples_to_discard;
atrac9_config config;
void *handle; /* decoder handle */
Atrac9CodecInfo info; /* decoder info */
};
atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
int status;
uint8_t config_data[4];
Atrac9CodecInfo info = {0};
atrac9_codec_data *data = NULL;
data = calloc(1, sizeof(atrac9_codec_data));
@ -36,20 +37,20 @@ atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
status = Atrac9InitDecoder(data->handle, config_data);
if (status < 0) goto fail;
status = Atrac9GetCodecInfo(data->handle, &info);
status = Atrac9GetCodecInfo(data->handle, &data->info);
if (status < 0) goto fail;
//;VGM_LOG("ATRAC9: config=%x, sf-size=%x, sub-frames=%i x %i samples\n", cfg->config_data, info.superframeSize, info.framesInSuperframe, info.frameSamples);
if (cfg->channels && cfg->channels != info.channels) {
VGM_LOG("ATRAC9: channels in header %i vs config %i don't match\n", cfg->channels, info.channels);
if (cfg->channels && cfg->channels != data->info.channels) {
VGM_LOG("ATRAC9: channels in header %i vs config %i don't match\n", cfg->channels, data->info.channels);
goto fail; /* unknown multichannel layout */
}
/* must hold at least one superframe and its samples */
data->data_buffer_size = info.superframeSize;
data->data_buffer_size = data->info.superframeSize;
data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size);
data->sample_buffer = calloc(sizeof(sample), info.channels * info.frameSamples * info.framesInSuperframe);
data->sample_buffer = calloc(sizeof(sample), data->info.channels * data->info.frameSamples * data->info.framesInSuperframe);
data->samples_to_discard = cfg->encoder_delay;
@ -100,31 +101,28 @@ void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do,
int bytes_used = 0;
uint8_t *buffer = data->data_buffer;
size_t bytes;
Atrac9CodecInfo info = {0};
data->samples_used = 0;
/* ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives
* superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples). */
status = Atrac9GetCodecInfo(data->handle, &info);
if (status < 0) goto decode_fail;
/* read one raw block (superframe) and advance offsets */
bytes = read_streamfile(data->data_buffer,stream->offset, info.superframeSize,stream->streamfile);
bytes = read_streamfile(data->data_buffer,stream->offset, data->info.superframeSize,stream->streamfile);
if (bytes != data->data_buffer_size) {
VGM_LOG("ATRAC9: read %x vs expected %x bytes at %lx\n", bytes, info.superframeSize, stream->offset);
VGM_LOG("ATRAC9: read %x vs expected %x bytes at %lx\n", bytes, data->info.superframeSize, stream->offset);
goto decode_fail;
}
stream->offset += bytes;
/* decode all frames in the superframe block */
for (iframe = 0; iframe < info.framesInSuperframe; iframe++) {
for (iframe = 0; iframe < data->info.framesInSuperframe; iframe++) {
status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used);
if (status < 0) goto decode_fail;
buffer += bytes_used;
data->samples_filled += info.frameSamples;
data->samples_filled += data->info.frameSamples;
}
}
}
@ -162,7 +160,7 @@ void reset_atrac9(VGMSTREAM *vgmstream) {
data->samples_used = 0;
data->samples_filled = 0;
data->samples_to_discard = 0;
data->samples_to_discard = data->config.encoder_delay;
return;
@ -176,12 +174,43 @@ void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) {
reset_atrac9(vgmstream);
/* find closest offset to desired sample, and samples to discard after that offset to reach loop */
{
int32_t seek_sample = data->config.encoder_delay + num_sample;
off_t seek_offset;
int32_t seek_discard;
int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe;
size_t superframe_number, superframe_back;
superframe_number = (seek_sample / superframe_samples); /* closest */
/* decoded frames affect each other slightly, so move offset back to make PCM stable
* and equivalent to a full discard loop */
superframe_back = 1; /* 1 seems enough (even when only 1 subframe in superframe) */
if (superframe_back > superframe_number)
superframe_back = superframe_number;
seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples);
seek_offset = (superframe_number - superframe_back) * data->info.superframeSize;
data->samples_to_discard = seek_discard; /* already includes encoder delay */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + seek_offset;
}
#if 0
//old full discard loop
{
data->samples_to_discard = num_sample;
data->samples_to_discard += data->config.encoder_delay;
/* loop offsets are set during decode; force them to stream start so discard works */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
}
#endif
}
void free_atrac9(atrac9_codec_data *data) {
@ -195,16 +224,7 @@ void free_atrac9(atrac9_codec_data *data) {
size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data) {
Atrac9CodecInfo info = {0};
int status;
status = Atrac9GetCodecInfo(data->handle, &info);
if (status < 0) goto fail;
return bytes / info.superframeSize * (info.frameSamples * info.framesInSuperframe);
fail:
return 0;
return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe);
}
#if 0 //not needed (for now)

View File

@ -137,8 +137,8 @@ void decode_nds_procyon(VGMSTREAMCHANNEL * stream, sample * outbuf, int channels
/* l5_555_decoder */
void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* SASSC_decoder */
void decode_SASSC(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* sassc_decoder */
void decode_sassc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* lsf_decode */
void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
@ -170,10 +170,12 @@ void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample);
void free_ea_mt(ea_mt_codec_data *data);
/* hca_decoder */
void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
void reset_hca(VGMSTREAM *vgmstream);
void loop_hca(VGMSTREAM *vgmstream);
hca_codec_data *init_hca(STREAMFILE *streamFile);
void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do);
void reset_hca(hca_codec_data * data);
void loop_hca(hca_codec_data * data);
void free_hca(hca_codec_data * data);
int test_hca_key(hca_codec_data * data, unsigned long long keycode);
#ifdef VGM_USE_VORBIS
/* ogg_vorbis_decoder */

View File

@ -1,110 +1,208 @@
#include "coding.h"
void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) {
/* init a HCA stream; STREAMFILE will be duplicated for internal use. */
hca_codec_data * init_hca(STREAMFILE *streamFile) {
char filename[PATH_LIMIT];
uint8_t header_buffer[0x2000]; /* hca header buffer data (probable max ~0x400) */
hca_codec_data * data = NULL; /* vgmstream HCA context */
int header_size;
int status;
/* test header */
if (read_streamfile(header_buffer, 0x00, 0x08, streamFile) != 0x08)
goto fail;
header_size = clHCA_isOurFile(header_buffer, 0x08);
if (header_size < 0 || header_size > 0x1000)
goto fail;
if (read_streamfile(header_buffer, 0x00, header_size, streamFile) != header_size)
goto fail;
/* init vgmstream context */
data = calloc(1, sizeof(hca_codec_data));
if (!data) goto fail;
/* init library handle */
data->handle = calloc(1, clHCA_sizeof());
clHCA_clear(data->handle);
status = clHCA_DecodeHeader(data->handle, header_buffer, header_size); /* parse header */
if (status < 0) goto fail;
status = clHCA_getInfo(data->handle, &data->info); /* extract header info */
if (status < 0) goto fail;
data->data_buffer = malloc(data->info.blockSize);
if (!data->data_buffer) goto fail;
data->sample_buffer = malloc(sizeof(signed short) * data->info.channelCount * data->info.samplesPerBlock);
if (!data->sample_buffer) goto fail;
/* load streamfile for reads */
get_streamfile_name(streamFile,filename, sizeof(filename));
data->streamfile = open_streamfile(streamFile,filename);
if (!data->streamfile) goto fail;
/* set initial values */
reset_hca(data);
return data;
fail:
free_hca(data);
return NULL;
}
void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do) {
int samples_done = 0;
int32_t samples_remain = clHCA_samplesPerBlock - data->sample_ptr;
void *hca_data = NULL;
clHCA *hca;
if ( data->samples_discard ) {
if ( samples_remain <= data->samples_discard ) {
data->samples_discard -= samples_remain;
samples_remain = 0;
}
else {
samples_remain -= data->samples_discard;
data->sample_ptr += data->samples_discard;
data->samples_discard = 0;
}
}
if ( samples_remain > samples_to_do ) samples_remain = samples_to_do;
memcpy( outbuf, data->sample_buffer + data->sample_ptr * data->info.channelCount, samples_remain * data->info.channelCount * sizeof(sample) );
outbuf += samples_remain * data->info.channelCount;
data->sample_ptr += samples_remain;
samples_done += samples_remain;
hca_data = malloc( data->info.blockSize );
if ( !hca_data ) return;
hca = (clHCA *)(data + 1);
while ( samples_done < samples_to_do ) {
const unsigned int channels = data->info.channelCount;
const unsigned int blockSize = data->info.blockSize;
const unsigned int channelCount = data->info.channelCount;
const unsigned int address = data->info.dataOffset + data->curblock * blockSize;
if (data->curblock >= data->info.blockCount) {
memset(outbuf, 0, (samples_to_do - samples_done) * channelCount * sizeof(sample));
break;
}
if ( read_streamfile((uint8_t*) hca_data, data->start + address, blockSize, data->streamfile) != blockSize )
break;
while (samples_done < samples_to_do) {
if ( clHCA_Decode( hca, hca_data, blockSize, address ) < 0 )
break;
if (data->samples_filled) {
int samples_to_get = data->samples_filled;
++data->curblock;
clHCA_DecodeSamples16( hca, data->sample_buffer );
samples_remain = clHCA_samplesPerBlock;
data->sample_ptr = 0;
if ( data->samples_discard ) {
if ( samples_remain <= data->samples_discard ) {
data->samples_discard -= samples_remain;
samples_remain = 0;
if (data->samples_to_discard) {
/* discard samples for looping */
if (samples_to_get > data->samples_to_discard)
samples_to_get = data->samples_to_discard;
data->samples_to_discard -= samples_to_get;
}
else {
samples_remain -= data->samples_discard;
data->sample_ptr = data->samples_discard;
data->samples_discard = 0;
}
/* get max samples and copy */
if (samples_to_get > samples_to_do - samples_done)
samples_to_get = samples_to_do - samples_done;
memcpy(outbuf + samples_done*channels,
data->sample_buffer + data->samples_consumed*channels,
samples_to_get*channels * sizeof(sample));
samples_done += samples_to_get;
}
if ( samples_remain > samples_to_do - samples_done ) samples_remain = samples_to_do - samples_done;
memcpy( outbuf, data->sample_buffer, samples_remain * channelCount * sizeof(sample) );
samples_done += samples_remain;
outbuf += samples_remain * channelCount;
data->sample_ptr = samples_remain;
/* mark consumed samples */
data->samples_consumed += samples_to_get;
data->samples_filled -= samples_to_get;
}
else {
off_t offset = data->info.headerSize + data->current_block * blockSize;
int status;
size_t bytes;
/* EOF/error */
if (data->current_block >= data->info.blockCount) {
memset(outbuf, 0, (samples_to_do - samples_done) * channels * sizeof(sample));
break;
}
free( hca_data );
/* read frame */
bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile);
if (bytes != blockSize) {
VGM_LOG("HCA: read %x vs expected %x bytes at %lx\n", bytes, blockSize, offset);
break;
}
/* decode frame */
status = clHCA_DecodeBlock(data->handle, (void*)(data->data_buffer), blockSize);
if (status < 0) {
VGM_LOG("HCA: decode fail at %lx\n", offset);
break;
}
/* extract samples */
clHCA_ReadSamples16(data->handle, data->sample_buffer);
data->current_block++;
data->samples_consumed = 0;
data->samples_filled += data->info.samplesPerBlock;
}
}
}
void reset_hca(VGMSTREAM *vgmstream) {
hca_codec_data *data = vgmstream->codec_data;
void reset_hca(hca_codec_data * data) {
if (!data) return;
data->curblock = 0;
data->sample_ptr = clHCA_samplesPerBlock;
data->samples_discard = 0;
data->current_block = 0;
data->samples_filled = 0;
data->samples_consumed = 0;
data->samples_to_discard = data->info.encoderDelay;
}
void loop_hca(VGMSTREAM *vgmstream) {
hca_codec_data *data = (hca_codec_data *)(vgmstream->codec_data);
void loop_hca(hca_codec_data * data) {
if (!data) return;
data->curblock = data->info.loopStart;
data->sample_ptr = clHCA_samplesPerBlock;
data->samples_discard = 0;
data->current_block = data->info.loopStartBlock;
data->samples_filled = 0;
data->samples_consumed = 0;
data->samples_to_discard = data->info.loopStartDelay;
}
void free_hca(hca_codec_data * data) {
if (data) {
clHCA *hca = (clHCA *)(data + 1);
clHCA_done(hca);
if (data->streamfile)
if (!data) return;
close_streamfile(data->streamfile);
clHCA_done(data->handle);
free(data->handle);
free(data->data_buffer);
free(data->sample_buffer);
free(data);
}
}
#define HCA_KEY_MAX_BLANK_FRAMES 15 /* ignored up to N blank frames (not uncommon to have ~10, if more something is off) */
#define HCA_KEY_MAX_TEST_FRAMES 10 /* 5~15 should be enough, but mostly silent or badly mastered files may need more */
#define HCA_KEY_MAX_ACCEPTABLE_SCORE 300 /* unlikely to work correctly, 10~30 may be ok */
/* Test a number of frames if key decrypts correctly.
* Returns score: <0: error/wrong, 0: unknown/silent file, >0: good (the closest to 1 the better) */
int test_hca_key(hca_codec_data * data, unsigned long long keycode) {
size_t test_frame = 0, current_frame = 0, blank_frames = 0;
int total_score = 0;
const unsigned int blockSize = data->info.blockSize;
clHCA_SetKey(data->handle, keycode);
while (test_frame < HCA_KEY_MAX_TEST_FRAMES && current_frame < data->info.blockCount) {
off_t offset = data->info.headerSize + current_frame * blockSize;
int score;
size_t bytes;
/* read frame */
bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile);
if (bytes != blockSize) {
total_score = -1;
break;
}
/* test frame */
score = clHCA_TestBlock(data->handle, (void*)(data->data_buffer), blockSize);
if (score < 0) {
total_score = -1;
break;
}
current_frame++;
/* skip blank block at the beginning */
if (score == 0 && blank_frames < HCA_KEY_MAX_BLANK_FRAMES) {
blank_frames++;
continue;
}
test_frame++;
total_score += score;
/* too far, don't bother checking more frames */
if (total_score > HCA_KEY_MAX_ACCEPTABLE_SCORE)
break;
}
/* signal best possible score */
if (total_score > 0 && total_score <= HCA_KEY_MAX_TEST_FRAMES) {
total_score = 1;
}
return total_score;
}

View File

@ -63,7 +63,7 @@ int32_t SASSC_steps[256] =
-53261, -54797, -56333, -57870, -59406, -60942, -62479, 64015,
};
void decode_SASSC(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
void decode_sassc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i;
int32_t sample_count;
int32_t hist = stream->adpcm_history1_32;

View File

@ -33,6 +33,7 @@ static const char* extension_list[] = {
"afc",
"agsc",
"ahx",
"ahv",
"ai",
//"aif", //common
"aifc", //common?
@ -43,8 +44,8 @@ static const char* extension_list[] = {
"akb",
"al2",
"amts", //fake extension/header id for .stm (to be removed)
"ao", //txth/reserved [Cloudphobia (PC)]
"apc", //txth/reserved [MegaRace 3 (PC)]
"ao",
"apc",
"as4",
"asd",
"asf",
@ -167,10 +168,11 @@ static const char* extension_list[] = {
"int",
"isd",
"isws",
"itl", //txth/reserved [Charinko Hero (GC)]
"itl",
"ivaud",
"ivag",
"ivb",
"ivs", //txth/reserved [Burnout 2 (PS2)]
"joe",
"jstm",
@ -227,7 +229,7 @@ static const char* extension_list[] = {
"msd",
"msf",
"mss",
"msv", //txh/reserved [Fight Club (PS2)]
"msv",
"msvp",
"mta2",
"mtaf",
@ -313,7 +315,7 @@ static const char* extension_list[] = {
"scd",
"sck",
"sd9",
"sdf", //txth/reserved [Gummy Bears Mini Golf (3DS), Agent Hugo - Lemoon Twist (PS2)]
"sdf",
"sdt",
"seg",
"sf0",
@ -357,7 +359,7 @@ static const char* extension_list[] = {
"stx",
"svag",
"svs",
"svg", //txth/reserved [Hunter: The Reckoning - Wayward (PS2)]
"svg",
"swag",
"swav",
"swd",
@ -384,7 +386,7 @@ static const char* extension_list[] = {
"v0",
//"v1", //dual channel with v0
"vag",
"vai", //txth/reserved [Ratatouille (GC)]
"vai",
"vas",
"vawx",
"vb",
@ -395,7 +397,7 @@ static const char* extension_list[] = {
"vgs",
"vgv",
"vig",
"vis", //txth/reserved [AirForce Delta (PS2)]
"vis",
"vms",
"voi",
"vpk",
@ -424,7 +426,7 @@ static const char* extension_list[] = {
"wsd",
"wsi",
"wua",
"wv2", //txth/reserved [Slave Zero (PC)]
"wv2",
"wv6",
"wve",
"wvs",
@ -695,7 +697,7 @@ static const meta_info meta_info_list[] = {
{meta_PS2_RXWS, "Sony RXWS header"},
{meta_PS2_RAW, ".int PCM raw header"},
{meta_PS2_OMU, "Alter Echo OMU Header"},
{meta_DSP_STM, "Nintendo STM header"},
{meta_DSP_STM, "Intelligent Systems STM header"},
{meta_PS2_EXST, "Sony EXST header"},
{meta_PS2_SVAG, "Konami SVAG header"},
{meta_PS_HEADERLESS, "Headerless PS-ADPCM raw header"},
@ -974,7 +976,7 @@ static const meta_info meta_info_list[] = {
{meta_WIIU_BTSND, "Nintendo Wii U Menu Boot Sound"},
{meta_MCA, "Capcom MCA header"},
{meta_XB3D_ADX, "Xenoblade 3D ADX header"},
{meta_HCA, "CRI MiddleWare HCA Header"},
{meta_HCA, "CRI HCA header"},
{meta_PS2_SVAG_SNK, "SNK SVAG header"},
{meta_PS2_VDS_VDM, "Procyon Studio VDS/VDM header"},
{meta_FFMPEG, "FFmpeg supported file format"},
@ -1067,6 +1069,21 @@ static const meta_info meta_info_list[] = {
{meta_HD3_BD3, "Sony HD3+BD3 header"},
{meta_BNK_SONY, "Sony BNK header"},
{meta_SCD_SSCF, "Square-Enix SCD (SSCF) header"},
{meta_DSP_VAG, ".VAG DSP header"},
{meta_DSP_ITL, ".ITL DSP header"},
{meta_A2M, "Artificial Mind & Movement A2M header"},
{meta_AHV, "Amuze AHV header"},
{meta_MSV, "Sony MultiStream MSV header"},
{meta_SDF_PS2, "Beyond Reality PS2 SDF header"},
{meta_SVG, "High Voltage SVG header"},
{meta_VIS, "Konami VIS header"},
{meta_SDF_3DS, "Beyond Reality 3DS SDF header"},
{meta_VAI, "Asobo Studio .VAI header"},
{meta_AIF_ASOBO, "Asobo Studio .AIF header"},
{meta_AO, "AlphaOgg .AO header"},
{meta_APC, "Cryo APC header"},
{meta_WV2, "Infogrames North America WAV2 header"},
{meta_XAU_KONAMI, "Konami XAU header"},
};

View File

@ -275,6 +275,54 @@
<File
RelativePath=".\meta\2dx9.c"
>
</File>
<File
RelativePath=".\meta\ahv.c"
>
</File>
<File
RelativePath=".\meta\a2m.c"
>
</File>
<File
RelativePath=".\meta\msv.c"
>
</File>
<File
RelativePath=".\meta\sdf.c"
>
</File>
<File
RelativePath=".\meta\svg.c"
>
</File>
<File
RelativePath=".\meta\vis.c"
>
</File>
<File
RelativePath=".\meta\vai.c"
>
</File>
<File
RelativePath=".\meta\aif_asobo.c"
>
</File>
<File
RelativePath=".\meta\ao.c"
>
</File>
<File
RelativePath=".\meta\apc.c"
>
</File>
<File
RelativePath=".\meta\wv2.c"
>
</File>
<File
RelativePath=".\meta\xau_konami.c"
>
</File>
<File
RelativePath=".\meta\aax.c"
@ -541,7 +589,11 @@
>
</File>
<File
RelativePath=".\meta\idsp.c"
RelativePath=".\meta\idsp_ie.c"
>
</File>
<File
RelativePath=".\meta\nub_idsp.c"
>
</File>
<File
@ -1751,7 +1803,7 @@
>
</File>
<File
RelativePath=".\coding\SASSC_decoder.c"
RelativePath=".\coding\sassc_decoder.c"
>
</File>
<File

View File

@ -191,6 +191,18 @@
<ClCompile Include="util.c" />
<ClCompile Include="vgmstream.c" />
<ClCompile Include="meta\2dx9.c" />
<ClCompile Include="meta\a2m.c" />
<ClCompile Include="meta\ahv.c" />
<ClCompile Include="meta\msv.c" />
<ClCompile Include="meta\sdf.c" />
<ClCompile Include="meta\svg.c" />
<ClCompile Include="meta\vis.c" />
<ClCompile Include="meta\vai.c" />
<ClCompile Include="meta\aif_asobo.c" />
<ClCompile Include="meta\ao.c" />
<ClCompile Include="meta\apc.c" />
<ClCompile Include="meta\wv2.c" />
<ClCompile Include="meta\xau_konami.c" />
<ClCompile Include="meta\aax.c" />
<ClCompile Include="meta\acm.c" />
<ClCompile Include="meta\ads.c" />
@ -249,7 +261,8 @@
<ClCompile Include="meta\hca.c" />
<ClCompile Include="meta\hd3_bd3.c" />
<ClCompile Include="meta\his.c" />
<ClCompile Include="meta\idsp.c" />
<ClCompile Include="meta\idsp_ie.c" />
<ClCompile Include="meta\nub_idsp.c" />
<ClCompile Include="meta\ish_isd.c" />
<ClCompile Include="meta\ivaud.c" />
<ClCompile Include="meta\ivb.c" />
@ -490,7 +503,7 @@
<ClCompile Include="coding\pcm_decoder.c" />
<ClCompile Include="coding\psv_decoder.c" />
<ClCompile Include="coding\psx_decoder.c" />
<ClCompile Include="coding\SASSC_decoder.c" />
<ClCompile Include="coding\sassc_decoder.c" />
<ClCompile Include="coding\sdx2_decoder.c" />
<ClCompile Include="coding\vorbis_custom_decoder.c" />
<ClCompile Include="coding\vorbis_custom_utils_fsb.c" />

View File

@ -169,6 +169,42 @@
<ClCompile Include="meta\2dx9.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\a2m.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ahv.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\msv.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\sdf.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\svg.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\vis.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\vai.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\aif_asobo.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ao.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\apc.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\wv2.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\xau_konami.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\aax.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -334,7 +370,10 @@
<ClCompile Include="meta\his.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\idsp.c">
<ClCompile Include="meta\nub_idsp.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\idsp_ie.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ish_isd.c">
@ -1033,7 +1072,7 @@
<ClCompile Include="coding\psx_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\SASSC_decoder.c">
<ClCompile Include="coding\sassc_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\sdx2_decoder.c">

45
src/meta/a2m.c Normal file
View File

@ -0,0 +1,45 @@
#include "meta.h"
#include "../coding/coding.h"
/* A2M - from Artificial Mind & Movement games [Scooby-Doo! Unmasked (PS2)] */
VGMSTREAM * init_vgmstream_a2m(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t data_size;
int loop_flag, channel_count;
/* checks */
if ( !check_extensions(streamFile,"int") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x41324D00) /* "A2M\0" */
goto fail;
if (read_32bitBE(0x04,streamFile) != 0x50533200) /* "PS2\0" */
goto fail;
start_offset = 0x30;
data_size = get_streamfile_size(streamFile) - start_offset;
channel_count = 2;
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_A2M;
vgmstream->sample_rate = read_32bitBE(0x10,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x6000;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -140,6 +140,14 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) {
* 0x00 (4): "AINF"; 0x04 (4): size; 0x08 (10): str_id
* 0x18 (2): volume (0=base/max?, negative=reduce)
* 0x1c (2): pan l; 0x1e (2): pan r (0=base, max +-128) */
/* CINF header info (very rare, found after loops) [Sakura Taisen 3 (PS2)]
* 0x00 (4): "CINF"
* 0x04 (4): size
* 0x08 (4): "ASO ", unknown
* 0x28 (4): "SND ", unknown
* 0x48 (-): file name, null terminated
*/
}
else if (version_signature == 0x0500) { /* found in some SFD: Buggy Heat, appears to have no loop */
header_type = meta_ADX_05;

View File

@ -179,6 +179,9 @@ static const adxkey_info adxkey8_list[] = {
/* Shounen Onmyouji: Tsubasa yo Ima, Sora e Kaere [PS2] */
{0x55d9,0x46d3,0x5b01, "SONMYOJI",0},
/* Girls Bravo: Romance 15's [PS2] */
{0x658f,0x4a89,0x5213, "GBRAVO",0},
};
static const adxkey_info adxkey9_list[] = {

48
src/meta/ahv.c Normal file
View File

@ -0,0 +1,48 @@
#include "meta.h"
#include "../coding/coding.h"
/* AHV - from Amuze games [Headhunter (PS2)] */
VGMSTREAM * init_vgmstream_ahv(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t data_size, channel_size, interleave;
int loop_flag, channel_count;
/* checks (.ahv: from names in bigfile) */
if ( !check_extensions(streamFile,"ahv") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x41485600) /* "AHV\0" */
goto fail;
start_offset = 0x800;
data_size = get_streamfile_size(streamFile) - start_offset;
interleave = read_32bitLE(0x10,streamFile);
channel_count = (interleave != 0) ? 2 : 1;
channel_size = read_32bitLE(0x08,streamFile);
loop_flag = 0;
/* VAGp header after 0x14 */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_AHV;
vgmstream->sample_rate = read_32bitLE(0x0c,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(channel_size,1);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
if (interleave)
vgmstream->interleave_last_block_size = (data_size % (interleave*channel_count)) / channel_count;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

49
src/meta/aif_asobo.c Normal file
View File

@ -0,0 +1,49 @@
#include "meta.h"
#include "../coding/coding.h"
/* .AIF - from Asobo Studio games [Ratatouille (PC)] */
VGMSTREAM * init_vgmstream_aif_asobo(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t data_size;
int loop_flag, channel_count;
/* checks */
/* aif: standard, aiffl: for plugins? */
if ( !check_extensions(streamFile,"aif,aiffl") )
goto fail;
if ((uint16_t)read_16bitLE(0x00,streamFile) != 0x69) /* Xbox codec */
goto fail;
channel_count = read_16bitLE(0x02,streamFile); /* assumed */
/* 0x08: ? */
if ((uint16_t)read_16bitLE(0x0c,streamFile) != 0x24*channel_count) /* Xbox block */
goto fail;
if ((uint16_t)read_16bitLE(0x0e,streamFile) != 0x04) /* Xbox bps */
goto fail;
loop_flag = 0;
start_offset = 0x14;
data_size = get_streamfile_size(streamFile) - start_offset;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_AIF_ASOBO;
vgmstream->sample_rate = read_32bitLE(0x04,streamFile);
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size,channel_count);
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

36
src/meta/ao.c Normal file
View File

@ -0,0 +1,36 @@
#include "meta.h"
#include "../coding/coding.h"
/* .AO - from AlphaOgg lib [Cloudphobia (PC)] */
VGMSTREAM * init_vgmstream_ao(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
/* checks */
if ( !check_extensions(streamFile,"ao") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x414C5048) /* "ALPH" */
goto fail;
if (read_32bitBE(0x04,streamFile) != 0x414F4747) /* "AOGG" */
goto fail;
#ifdef VGM_USE_VORBIS
{
ogg_vorbis_meta_info_t ovmi = {0};
ovmi.meta_type = meta_AO;
/* values at 0x08/0x0c/0x10 may be related to looping? */
start_offset = 0xc8;
vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi);
}
#else
goto fail
#endif
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

48
src/meta/apc.c Normal file
View File

@ -0,0 +1,48 @@
#include "meta.h"
#include "../coding/coding.h"
/* APC - from Cryo games [MegaRace 3 (PC)] */
VGMSTREAM * init_vgmstream_apc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t data_size;
int loop_flag, channel_count;
/* checks */
if ( !check_extensions(streamFile,"apc") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4352594F) /* "CRYO" */
goto fail;
if (read_32bitBE(0x04,streamFile) != 0x5F415043) /* "_APC" */
goto fail;
//if (read_32bitBE(0x08,streamFile) != 0x312E3230) /* "1.20" */
// goto fail;
/* 0x14/18: L/R hist sample? */
start_offset = 0x20;
data_size = get_streamfile_size(streamFile) - start_offset;
channel_count = read_32bitLE(0x1c,streamFile) == 0 ? 1 : 2;
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_APC;
vgmstream->sample_rate = read_32bitLE(0x10,streamFile);
vgmstream->num_samples = ima_bytes_to_samples(data_size,channel_count);
vgmstream->coding_type = coding_IMA;
vgmstream->layout_type = layout_none;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,165 +1,133 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* BCSTM - Nintendo 3DS format */
VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
coding_t coding_type;
off_t info_offset = 0, seek_offset = 0, data_offset = 0;
uint16_t temp_id;
int codec_number;
int channel_count, loop_flag;
int i, ima = 0;
off_t start_offset;
int section_count;
off_t info_offset = 0, seek_offset = 0, data_offset = 0;
int channel_count, loop_flag, codec;
int is_camelot_ima = 0;
/* check extension, case insensitive */
/* checks */
if ( !check_extensions(streamFile,"bcstm") )
goto fail;
/* check header */
if ((uint32_t)read_32bitBE(0, streamFile) != 0x4353544D) /* "CSTM" */
/* CSTM header */
if (read_32bitBE(0x00, streamFile) != 0x4353544D) /* "CSTM" */
goto fail;
/* 0x06(2): header size (0x40), 0x08: version (0x00000400), 0x0c: file size (not accurate for Camelot IMA) */
/* check BOM */
if ((uint16_t)read_16bitLE(0x04, streamFile) != 0xFEFF)
goto fail;
if ((uint16_t)read_16bitLE(4, streamFile) != 0xFEFF)
goto fail;
section_count = read_16bitLE(0x10, streamFile);
/* get sections (should always appear in the same order) */
{
int i;
int section_count = read_16bitLE(0x10, streamFile);
for (i = 0; i < section_count; i++) {
temp_id = read_16bitLE(0x14 + i * 0xc, streamFile);
switch(temp_id) {
case 0x4000:
info_offset = read_32bitLE(0x18 + i * 0xc, streamFile);
/* size_t info_size = read_32bitLE(0x1c + i * 0xc, streamFile); */
break;
case 0x4001:
seek_offset = read_32bitLE(0x18 + i * 0xc, streamFile);
/* size_t seek_size = read_32bitLE(0x1c + i * 0xc, streamFile); */
break;
case 0x4002:
data_offset = read_32bitLE(0x18 + i * 0xc, streamFile);
/* size_t data_size = read_32bitLE(0x1c + i * 0xc, streamFile); */
break;
case 0x4003:
/* off_t regn_offset = read_32bitLE(0x18 + i * 0xc, streamFile); */
/* size_t regn_size = read_32bitLE(0x1c + i * 0xc, streamFile); */
break;
case 0x4004:
/* off_t pdat_offset = read_32bitLE(0x18 + i * 0xc, streamFile); */
/* size_t pdat_size = read_32bitLE(0x1c + i * 0xc, streamFile); */
break;
/* 0x00: id, 0x02(2): padding, 0x04(4): offset, 0x08(4): size */
uint16_t section_id = read_16bitLE(0x14 + i*0x0c+0x00, streamFile);
switch(section_id) {
case 0x4000: info_offset = read_32bitLE(0x14 + i*0x0c+0x04, streamFile); break;
case 0x4001: seek_offset = read_32bitLE(0x14 + i*0x0c+0x04, streamFile); break;
case 0x4002: data_offset = read_32bitLE(0x14 + i*0x0c+0x04, streamFile); break;
//case 0x4003: /* off_t regn_offset = read_32bitLE(0x18 + i * 0xc, streamFile); */ /* ? */
//case 0x4004: /* off_t pdat_offset = read_32bitLE(0x18 + i * 0xc, streamFile); */ /* ? */
default:
break;
}
}
}
if (info_offset == 0) goto fail;
if ((uint32_t)read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */
/* INFO section */
if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */
goto fail;
/* check type details */
codec_number = read_8bit(info_offset + 0x20, streamFile);
codec = read_8bit(info_offset + 0x20, streamFile);
loop_flag = read_8bit(info_offset + 0x21, streamFile);
channel_count = read_8bit(info_offset + 0x22, streamFile);
switch (codec_number) {
case 0:
coding_type = coding_PCM8;
break;
case 1:
coding_type = coding_PCM16LE;
break;
case 2:
if (seek_offset == 0) goto fail;
if ((uint32_t)read_32bitBE(seek_offset, streamFile) != 0x5345454B) { /* "SEEK" If this header doesn't exist, assuming that the file is IMA */
ima = 1;
coding_type = coding_3DS_IMA;
}
else
coding_type = coding_NGC_DSP;
break;
default:
goto fail;
}
if (channel_count < 1) goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = read_32bitLE(info_offset + 0x2c, streamFile);
vgmstream->sample_rate = read_32bitLE(info_offset + 0x24, streamFile);
/* channels and loop flag are set by allocate_vgmstream */
if (ima) //Shift the loop points back slightly to avoid stupid pops in some IMA streams due to DC offsetting
{
vgmstream->loop_start_sample = read_32bitLE(info_offset + 0x28, streamFile);
if (vgmstream->loop_start_sample > 10000)
{
vgmstream->loop_start_sample -= 5000;
vgmstream->loop_end_sample = vgmstream->num_samples - 5000;
}
else
vgmstream->loop_end_sample = vgmstream->num_samples;
}
else
{
vgmstream->loop_start_sample = read_32bitLE(info_offset + 0x28, streamFile);
vgmstream->loop_end_sample = vgmstream->num_samples;
}
vgmstream->coding_type = coding_type;
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave;
vgmstream->meta_type = meta_CSTM;
if (ima) {
vgmstream->interleave_block_size = 0x200;
} else {
vgmstream->interleave_block_size = read_32bitLE(info_offset + 0x34, streamFile);
vgmstream->interleave_last_block_size = read_32bitLE(info_offset + 0x44, streamFile);
}
if (vgmstream->coding_type == coding_NGC_DSP) {
off_t coef_offset;
off_t tempoffset = info_offset;
int foundcoef = 0;
int i, j;
int coef_spacing = 0x2E;
while (!(foundcoef))
{
if ((uint32_t)read_32bitLE(tempoffset, streamFile) == 0x00004102)
{
coef_offset = read_32bitLE(tempoffset + 4, streamFile) + tempoffset + (channel_count * 8) - 4 - info_offset;
foundcoef++;
break;
}
tempoffset++;
}
for (j = 0; j<vgmstream->channels; j++) {
for (i = 0; i<16; i++) {
vgmstream->ch[j].adpcm_coef[i] = read_16bitLE(info_offset + coef_offset + j*coef_spacing + i * 2, streamFile);
}
}
}
if (ima) { // No SEEK (ADPC) header, so just start where the SEEK header is supposed to be.
/* Camelot games have some weird codec hijack [Mario Tennis Open (3DS), Mario Golf: World Tour (3DS)] */
if (codec == 0x02 && read_32bitBE(seek_offset, streamFile) != 0x5345454B) { /* "SEEK" */
if (seek_offset == 0) goto fail;
start_offset = seek_offset;
} else {
is_camelot_ima = 1;
}
else {
if (data_offset == 0) goto fail;
start_offset = data_offset + 0x20;
}
/* open the file for reading by each channel */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(info_offset + 0x24, streamFile);
vgmstream->num_samples = read_32bitLE(info_offset + 0x2c, streamFile);
vgmstream->loop_start_sample = read_32bitLE(info_offset + 0x28, streamFile);
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->meta_type = meta_CSTM;
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(info_offset + 0x34, streamFile);
vgmstream->interleave_last_block_size = read_32bitLE(info_offset + 0x44, streamFile);
/* Camelot doesn't follow header values */
if (is_camelot_ima) {
size_t block_samples, last_samples;
size_t data_size = get_streamfile_size(streamFile) - start_offset;
vgmstream->interleave_block_size = 0x200;
vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*channel_count)) / channel_count;
/* align the loop points back to avoid pops in some IMA streams, seems to help some Mario Golf songs but not all */
block_samples = ima_bytes_to_samples(vgmstream->interleave_block_size*channel_count,channel_count); // 5000?
last_samples = ima_bytes_to_samples(vgmstream->interleave_last_block_size*channel_count,channel_count);
if (vgmstream->loop_start_sample > block_samples) {
vgmstream->loop_start_sample -= last_samples;
vgmstream->loop_end_sample -= last_samples;
}
}
switch(codec) {
case 0x00:
vgmstream->coding_type = coding_PCM8;
break;
case 0x01:
vgmstream->coding_type = coding_PCM16LE;
break;
case 0x02:
vgmstream->coding_type = coding_NGC_DSP;
if (is_camelot_ima) {
vgmstream->coding_type = coding_3DS_IMA;
}
else {
int i, c;
off_t channel_indexes, channel_info_offset, coefs_offset;
channel_indexes = info_offset+0x08 + read_32bitLE(info_offset + 0x1C, streamFile);
for (i = 0; i < vgmstream->channels; i++) {
channel_info_offset = channel_indexes + read_32bitLE(channel_indexes+0x04+(i*0x08)+0x04, streamFile);
coefs_offset = channel_info_offset + read_32bitLE(channel_info_offset+0x04, streamFile);
for (c = 0; c < 16; c++) {
vgmstream->ch[i].adpcm_coef[c] = read_16bitLE(coefs_offset + c*2, streamFile);
}
}
}
break;
default: /* 0x03: IMA? */
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:

View File

@ -66,6 +66,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
switch(version) {
case 0x03: /* L@ove Once - Mermaid's Tears (PS3) */
case 0x04: /* Test banks */
case 0x09: /* Puyo Puyo Tetris (PS4) */
section_entries = (uint16_t)read_16bit(sblk_offset+0x16,streamFile); /* entry size: ~0x0c */
material_entries = (uint16_t)read_16bit(sblk_offset+0x18,streamFile); /* entry size: ~0x08 */
@ -155,6 +156,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
/* parse names */
switch(version) {
//case 0x03: /* different format? */
//case 0x04: /* different format? */
case 0x09:
case 0x0d:
/* find if this sound has an assigned name in table1 */
@ -205,6 +207,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
switch(version) {
case 0x03:
case 0x04:
sample_rate = 48000; /* seems ok */
channel_count = 1;
@ -255,7 +258,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
type = read_16bit(start_offset+0x00,streamFile);
if (read_32bit(start_offset+0x04,streamFile) != 0x01) /* type? */
goto fail;
extradata_size = 0x10 + read_32bit(start_offset+0x08,streamFile); /* 0x80 for AT9, 0x10 for PCM */
extradata_size = 0x10 + read_32bit(start_offset+0x08,streamFile); /* 0x80 for AT9, 0x10 for PCM/PS-ADPCM */
/* 0x0c: null? */
switch(type) {
@ -292,6 +295,19 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) {
codec = PCM16;
break;
case 0x00: /* PS-ADPCM (test banks) */
sample_rate = 48000; /* seems ok */
/* 0x10: null? */
channel_count = read_32bit(start_offset+0x14,streamFile);
interleave = 0x02;
loop_start = read_32bit(start_offset+0x18,streamFile);
loop_length = read_32bit(start_offset+0x1c,streamFile);
loop_end = loop_start + loop_length; /* loop_start is -1 if not set */
codec = PSX;
break;
default:
VGM_LOG("BNK: unknown type %x\n", type);
goto fail;

View File

@ -2,181 +2,107 @@
#include "hca_keys.h"
#include "../coding/coding.h"
#define HCA_KEY_MAX_TEST_CLIPS 400 /* hopefully nobody masters files with more that a handful... */
#define HCA_KEY_MAX_TEST_FRAMES 100 /* ~102400 samples */
#define HCA_KEY_MAX_TEST_SAMPLES 10240 /* ~10 frames of non-blank samples */
static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffer, int header_size, unsigned int * out_key1, unsigned int * out_key2);
static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode);
VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
uint8_t buffer[0x8000]; /* hca header buffer data (probably max ~0x400) */
char filename[PATH_LIMIT];
off_t start = 0;
size_t file_size = streamFile->get_size(streamFile);
hca_codec_data * hca_data = NULL;
unsigned long long keycode = 0;
int header_size;
hca_codec_data * hca_data = NULL; /* vgmstream HCA context */
clHCA * hca; /* HCA_Decoder context */
unsigned int ciphKey1, ciphKey2;
/* checks */
if ( !check_extensions(streamFile, "hca"))
return NULL;
if (((uint32_t)read_32bitBE(0x00,streamFile) & 0x7f7f7f7f) != 0x48434100) /* "HCA\0", possibly masked */
goto fail;
/* check extension, case insensitive */
if ( !check_extensions(streamFile, "hca")) return NULL;
/* test/init header (find real header size first) */
if ( file_size < 8 ) goto fail;
if ( read_streamfile(buffer, start, 8, streamFile) != 8 ) goto fail;
header_size = clHCA_isOurFile0(buffer);
if ( header_size < 0 || header_size > 0x8000 ) goto fail;
if ( read_streamfile(buffer, start, header_size, streamFile) != header_size ) goto fail;
if ( clHCA_isOurFile1(buffer, header_size) < 0 ) goto fail;
/* init vgmstream context */
hca_data = (hca_codec_data *) calloc(1, sizeof(hca_codec_data) + clHCA_sizeof());
/* init vgmstream and library's context, will validate the HCA */
hca_data = init_hca(streamFile);
if (!hca_data) goto fail;
//hca_data->size = file_size;
hca_data->start = 0;
hca_data->sample_ptr = clHCA_samplesPerBlock;
/* HCA_Decoder context memory goes right after our codec data (reserved in alloc'ed) */
hca = (clHCA *)(hca_data + 1);
/* pre-load streamfile so the hca_data is ready before key detection */
streamFile->get_name( streamFile, filename, sizeof(filename) );
hca_data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!hca_data->streamfile) goto fail;
/* find decryption key in external file or preloaded list */
{
if (hca_data->info.encryptionEnabled) {
uint8_t keybuf[8];
if (read_key_file(keybuf, 8, streamFile) == 8) {
ciphKey2 = get_32bitBE(keybuf+0);
ciphKey1 = get_32bitBE(keybuf+4);
keycode = (uint64_t)get_64bitBE(keybuf+0x00);
} else {
find_hca_key(hca_data, hca, buffer, header_size, &ciphKey1, &ciphKey2);
}
find_hca_key(hca_data, &keycode);
}
/* init decoder with key */
clHCA_clear(hca, ciphKey1, ciphKey2);
if ( clHCA_Decode(hca, buffer, header_size, 0) < 0 ) goto fail;
if ( clHCA_getInfo(hca, &hca_data->info) < 0 ) goto fail;
clHCA_SetKey(hca_data->handle, keycode); //maybe should be done through hca_decoder.c?
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(hca_data->info.channelCount, hca_data->info.loopEnabled);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_HCA;
vgmstream->sample_rate = hca_data->info.samplingRate;
vgmstream->num_samples = hca_data->info.blockCount * clHCA_samplesPerBlock;
vgmstream->loop_start_sample = hca_data->info.loopStart * clHCA_samplesPerBlock;
vgmstream->loop_end_sample = hca_data->info.loopEnd * clHCA_samplesPerBlock;
vgmstream->num_samples = hca_data->info.blockCount * hca_data->info.samplesPerBlock -
hca_data->info.encoderDelay - hca_data->info.encoderPadding;
vgmstream->loop_start_sample = hca_data->info.loopStartBlock * hca_data->info.samplesPerBlock -
hca_data->info.encoderDelay + hca_data->info.loopStartDelay;
vgmstream->loop_end_sample = hca_data->info.loopEndBlock * hca_data->info.samplesPerBlock -
hca_data->info.encoderDelay + (hca_data->info.samplesPerBlock - hca_data->info.loopEndPadding);
/* After loop end CRI's encoder removes the rest of the original samples and puts some
* garbage in the last frame that should be ignored. Optionally it can encode fully preserving
* the file too, but it isn't detectable, so we'll allow the whole thing just in case */
//if (vgmstream->loop_end_sample && vgmstream->num_samples > vgmstream->loop_end_sample)
// vgmstream->num_samples = vgmstream->loop_end_sample;
vgmstream->coding_type = coding_CRI_HCA;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_HCA;
vgmstream->codec_data = hca_data;
return vgmstream;
fail:
free(hca_data);
free_hca(hca_data);
return NULL;
}
/* Tries to find the decryption key from a list. Simply decodes a few frames and checks if there aren't too many
* clipped samples, as it's common for invalid keys (though possible with valid keys in poorly mastered files). */
static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffer, int header_size, unsigned int * out_key1, unsigned int * out_key2) {
sample *testbuf = NULL, *temp;
int i, j, bufsize = 0, tempsize;
size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
/* Try to find the decryption key from a list. */
static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode) {
const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
unsigned long long best_keycode;
int best_score = -1;
int i;
int min_clip_count = -1;
/* defaults to PSO2 key, most common */
unsigned int best_key2 = 0xCC554639;
unsigned int best_key1 = 0x30DBE1AB;
best_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
/* find a candidate key */
for (i = 0; i < keys_length; i++) {
int clip_count = 0, sample_count = 0;
int f = 0, s;
int score;
unsigned long long keycode = (unsigned long long)hcakey_list[i].key;
unsigned int key1, key2;
uint64_t key = hcakey_list[i].key;
key2 = (key >> 32) & 0xFFFFFFFF;
key1 = (key >> 0) & 0xFFFFFFFF;
score = test_hca_key(hca_data, keycode);
//;VGM_LOG("HCA: test key=%08x%08x, score=%i\n",
// (uint32_t)((keycode >> 32) & 0xFFFFFFFF), (uint32_t)(keycode & 0xFFFFFFFF), score);
/* re-init HCA with the current key as buffer becomes invalid (probably can be simplified) */
hca_data->curblock = 0;
hca_data->sample_ptr = clHCA_samplesPerBlock;
if ( read_streamfile(buffer, hca_data->start, header_size, hca_data->streamfile) != header_size ) continue;
/* wrong key */
if (score < 0)
continue;
clHCA_clear(hca, key1, key2);
if (clHCA_Decode(hca, buffer, header_size, 0) < 0) continue;
if (clHCA_getInfo(hca, &hca_data->info) < 0) continue;
if (hca_data->info.channelCount > 32) continue; /* nonsense don't alloc too much */
tempsize = sizeof(sample) * clHCA_samplesPerBlock * hca_data->info.channelCount;
if (tempsize > bufsize) { /* should happen once */
temp = (sample *)realloc(testbuf, tempsize);
if (!temp) goto end;
testbuf = temp;
bufsize = tempsize;
/* score 0 is not trustable, update too if something better is found */
if (best_score < 0 || score < best_score || (best_score == 0 && score == 1)) {
best_score = score;
best_keycode = keycode;
}
/* test enough frames, but not too many */
while (f < HCA_KEY_MAX_TEST_FRAMES && f < hca_data->info.blockCount) {
j = clHCA_samplesPerBlock;
decode_hca(hca_data, testbuf, j, hca_data->info.channelCount);
j *= hca_data->info.channelCount;
for (s = 0; s < j; s++) {
if (testbuf[s] != 0x0000 && testbuf[s] != 0xFFFF)
sample_count++; /* ignore upper/lower blank samples */
if (testbuf[s] == 0x7FFF || testbuf[s] == 0x8000)
clip_count++; /* upper/lower clip */
/* best possible score */
if (score == 1) {
break;
}
}
if (clip_count > HCA_KEY_MAX_TEST_CLIPS)
break; /* too many, don't bother */
if (sample_count >= HCA_KEY_MAX_TEST_SAMPLES)
break; /* enough non-blank samples tested */
//;VGM_LOG("HCA: best key=%08x%08x (score=%i)\n",
// (uint32_t)((best_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(best_keycode & 0xFFFFFFFF), best_score);
f++;
}
if (min_clip_count < 0 || clip_count < min_clip_count) {
min_clip_count = clip_count;
best_key2 = key2;
best_key1 = key1;
}
if (min_clip_count == 0)
break; /* can't get better than this */
/* a few clips is normal, but some invalid keys may give low numbers too */
//if (clip_count < 10)
// break;
}
/* reset HCA */
hca_data->curblock = 0;
hca_data->sample_ptr = clHCA_samplesPerBlock;
read_streamfile(buffer, hca_data->start, header_size, hca_data->streamfile);
end:
VGM_ASSERT(min_clip_count > 0, "HCA: best key=%08x%08x (clips=%i)\n", best_key2,best_key1, min_clip_count);
*out_key2 = best_key2;
*out_key1 = best_key1;
free(testbuf);//free(temp);
VGM_ASSERT(best_score > 1, "HCA: best key=%08x%08x (score=%i)\n",
(uint32_t)((best_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(best_keycode & 0xFFFFFFFF), best_score);
*out_keycode = best_keycode;
}

View File

@ -1,171 +0,0 @@
#include "meta.h"
//todo cleanup
/* "idsp" - from Namco's Wii NUB archives [Soul Calibur Legends (Wii), Sky Crawlers: Innocent Aces (Wii)] */
VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag;
int channel_count;
int i, j;
off_t start_offset;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("idsp",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x69647370 || /* "idsp" */
read_32bitBE(0xBC,streamFile) != 0x49445350) /* "IDSP" */
goto fail;
loop_flag = read_32bitBE(0x20,streamFile);
channel_count = read_32bitBE(0xC4,streamFile);
if (channel_count > 8)
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
start_offset = (channel_count * 0x60) + 0x100;
vgmstream->sample_rate = read_32bitBE(0xC8,streamFile);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->num_samples = (read_32bitBE(0x14,streamFile))*14/8/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = (read_32bitBE(0xD0,streamFile));
vgmstream->loop_end_sample = (read_32bitBE(0xD4,streamFile));
}
if (channel_count == 1) {
vgmstream->layout_type = layout_none;
}
else if (channel_count > 1) {
if (read_32bitBE(0xD8,streamFile) == 0) {
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = (get_streamfile_size(streamFile)-start_offset)/2;
}
else if (read_32bitBE(0xD8,streamFile) > 0) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitBE(0xD8,streamFile);
}
}
vgmstream->meta_type = meta_NUB_IDSP;
{
if (vgmstream->coding_type == coding_NGC_DSP) {
off_t coef_table[8] = {0x118,0x178,0x1D8,0x238,0x298,0x2F8,0x358,0x3B8};
for (j=0;j<vgmstream->channels;j++) {
for (i=0;i<16;i++) {
vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile);
}
}
}
}
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}
/* IDSP - from Inevitable Entertainment games [Defender (GC)] */
VGMSTREAM * init_vgmstream_idsp_ie(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag = 0;
int channel_count;
off_t start_offset;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("idsp",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */
goto fail;
channel_count = read_32bitBE(0x0C,streamFile);
if (channel_count > 2) // Refuse everything else for now
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
start_offset = 0x70;
vgmstream->sample_rate = read_32bitBE(0x08,streamFile);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->num_samples = read_32bitBE(0x04,streamFile)/channel_count/8*14;
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = read_32bitBE(0x04,streamFile)/channel_count/8*14;
}
if (channel_count == 1) {
vgmstream->layout_type = layout_none;
}
else {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitBE(0x10,streamFile);
}
vgmstream->meta_type = meta_IDSP_IE;
{
int i;
for (i=0;i<16;i++)
vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x14+i*2,streamFile);
if (channel_count == 2) {
for (i=0;i<16;i++)
vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x42+i*2,streamFile);
}
}
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

44
src/meta/idsp_ie.c Normal file
View File

@ -0,0 +1,44 @@
#include "meta.h"
#include "../coding/coding.h"
/* IDSP - from Inevitable Entertainment games [Defender (GC)] */
VGMSTREAM * init_vgmstream_idsp_ie(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
/* checks */
if ( !check_extensions(streamFile,"idsp") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */
goto fail;
loop_flag = 0;
channel_count = read_32bitBE(0x0C,streamFile);
if (channel_count > 2) goto fail;
start_offset = 0x70;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_IDSP_IE;
vgmstream->sample_rate = read_32bitBE(0x08,streamFile);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->num_samples = dsp_bytes_to_samples(read_32bitBE(0x04,streamFile), channel_count);
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitBE(0x10,streamFile);
dsp_read_coefs_be(vgmstream,streamFile,0x14,0x2E);
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -3,8 +3,6 @@
#include "../vgmstream.h"
VGMSTREAM * init_vgmstream_idsp_nus3(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_afc(STREAMFILE *streamFile);
@ -26,14 +24,30 @@ VGMSTREAM * init_vgmstream_nds_strm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_adpdtk(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_mdsp_std(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_stm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_mpdsp(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_idsp_nus3(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_sadf(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_swd(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_ddsp(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_wii_was(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_dsp_xiii(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_dsp_cabelas(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_wii_ndp(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_aaap(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_iadp(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_vag(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile);
@ -133,8 +147,6 @@ VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_sfl_ogg(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_bmdx(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_wsi(STREAMFILE * streamFile);
@ -152,7 +164,6 @@ VGMSTREAM * init_vgmstream_ivb(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_svs(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_riff(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_rifx(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xnb(STREAMFILE * streamFile);
@ -236,8 +247,6 @@ VGMSTREAM * init_vgmstream_aix(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ngc_tydsp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ngc_swd(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_capdsp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xbox_wvs(STREAMFILE *streamFile);
@ -264,8 +273,6 @@ VGMSTREAM * init_vgmstream_dc_idvi(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_rnd(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_kraw(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_omu(STREAMFILE *streamFile);
@ -273,7 +280,7 @@ VGMSTREAM * init_vgmstream_ps2_omu(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_xa2(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_idsp_ie(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ngc_ymf(STREAMFILE * streamFile);
@ -397,10 +404,6 @@ VGMSTREAM * init_vgmstream_RedSpark(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ivaud(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_wii_ndp(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_sps(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_xa2_rrp(STREAMFILE *streamFile);
@ -437,8 +440,6 @@ VGMSTREAM * init_vgmstream_exakt_sc(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_wii_bns(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_wii_was(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_pona_3do(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_pona_psx(STREAMFILE* streamFile);
@ -454,8 +455,6 @@ VGMSTREAM * init_vgmstream_ps2_ast(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_dmsg(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_aaap(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_konami(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_ster(STREAMFILE* streamFile);
@ -480,24 +479,16 @@ VGMSTREAM * init_vgmstream_pc_smp(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ngc_rkv(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_dsp_ddsp(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_p3d(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_mpds(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_b1s(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_wad(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_dsp_xiii(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_dsp_cabelas(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_lpcm(STREAMFILE* streamFile);
@ -512,8 +503,6 @@ VGMSTREAM * init_vgmstream_bar(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ffw(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_dsp_dspw(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_jstm(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_xvag(STREAMFILE* streamFile);
@ -528,8 +517,6 @@ VGMSTREAM * init_vgmstream_baf(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_iadp(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps3_past(STREAMFILE* streamFile);
@ -733,8 +720,6 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_le(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_pcm_sre(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE * streamFile);
@ -752,10 +737,6 @@ VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ubi_bao_pk(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_sadf(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile);
@ -779,4 +760,20 @@ VGMSTREAM * init_vgmstream_nus3bank(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_scd_sscf(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_a2m(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ahv(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_msv(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_sdf_ps2(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_svg(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_vis(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_sdf_3ds(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_vai(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_aif_asobo(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ao(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_apc(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_wv2(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xau_konami(STREAMFILE *streamFile);
#endif /*_META_H*/

43
src/meta/msv.c Normal file
View File

@ -0,0 +1,43 @@
#include "meta.h"
#include "../coding/coding.h"
/* MSV - from Sony MultiStream format [Fight Club (PS2)] */
VGMSTREAM * init_vgmstream_msv(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t channel_size;
int loop_flag, channel_count;
/* checks */
if ( !check_extensions(streamFile,"msv") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4D535670) /* "MSVp" */
goto fail;
start_offset = 0x30;
channel_count = 1;
channel_size = read_32bitBE(0x0c,streamFile);
loop_flag = 0; /* no looping and last 16 bytes (end frame) are removed, from Sony's docs */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_MSV;
vgmstream->sample_rate = read_32bitBE(0x10,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(channel_size,1);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_none;
read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile);
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -176,6 +176,7 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d
}
}
/* all done, must be DSP */
loop_flag = ch_header[0].loop_flag;
@ -629,7 +630,7 @@ fail:
}
/* sadf - Procyon Studio Header Variant [Xenoblade Chronicles 2 (Switch)] (sfx) */
VGMSTREAM * init_vgmstream_dsp_sadf(STREAMFILE *streamFile) {
VGMSTREAM * init_vgmstream_sadf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int channel_count, loop_flag;
off_t start_offset;
@ -779,6 +780,7 @@ VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE *streamFile) {
stream_size -= 0x14; /* remove padding */
stream_size -= dspm.start_offset;
if (dspm.interleave)
dspm.interleave_last = (stream_size / dspm.channel_count) % dspm.interleave;
}
@ -1111,3 +1113,56 @@ VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile) {
fail:
return NULL;
}
/* .vag - from Penny-Punching Princess (Switch) sfx */
VGMSTREAM * init_vgmstream_dsp_vag(STREAMFILE *streamFile) {
dsp_meta dspm = {0};
/* checks */
if (!check_extensions(streamFile, "vag"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x08000000) /* file type? OPUSs had 09 */
goto fail;
if (read_32bitLE(0x08,streamFile) != read_32bitLE(0x24,streamFile)) /* header has various repeated values */
goto fail;
dspm.channel_count = 1;
dspm.max_channels = 1;
dspm.little_endian = 1;
dspm.header_offset = 0x1c;
dspm.header_spacing = 0x60;
dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count;
dspm.interleave = 0;
dspm.fix_loop_start = 1;
dspm.meta_type = meta_DSP_VAG;
return init_vgmstream_dsp_common(streamFile, &dspm);
fail:
return NULL;
}
/* .itl - from Chanrinko Hero (GC) */
VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile) {
dsp_meta dspm = {0};
/* checks */
if (!check_extensions(streamFile, "itl"))
goto fail;
dspm.channel_count = 2;
dspm.max_channels = 2;
dspm.header_offset = 0x00;
dspm.header_spacing = 0x60;
dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count;
dspm.interleave = 0x23C0;
dspm.fix_looping = 1;
dspm.meta_type = meta_DSP_ITL;
return init_vgmstream_dsp_common(streamFile, &dspm);
fail:
return NULL;
}

55
src/meta/nub_idsp.c Normal file
View File

@ -0,0 +1,55 @@
#include "meta.h"
#include "../coding/coding.h"
/* "idsp" - from Namco's Wii NUB archives [Soul Calibur Legends (Wii), Sky Crawlers: Innocent Aces (Wii)] */
VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
/* checks */
if ( !check_extensions(streamFile,"idsp") )
goto fail;
/* actual header starts at "IDSP", while "idsp" is mostly nub bank stuff */
if (read_32bitBE(0x00,streamFile) != 0x69647370) /* "idsp" */
goto fail;
if (read_32bitBE(0xBC,streamFile) != 0x49445350) /* "IDSP" */
goto fail;
loop_flag = read_32bitBE(0x20,streamFile);
channel_count = read_32bitBE(0xC4,streamFile);
if (channel_count > 8) goto fail;
start_offset = 0x100 + (channel_count * 0x60);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_NUB_IDSP;
vgmstream->sample_rate = read_32bitBE(0xC8,streamFile);
vgmstream->num_samples = dsp_bytes_to_samples(read_32bitBE(0x14,streamFile),channel_count);
if (loop_flag) {
vgmstream->loop_start_sample = (read_32bitBE(0xD0,streamFile));
vgmstream->loop_end_sample = (read_32bitBE(0xD4,streamFile));
}
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitBE(0xD8,streamFile);
if (vgmstream->interleave_block_size == 0)
vgmstream->interleave_block_size = (get_streamfile_size(streamFile) - start_offset) / channel_count;
dsp_read_coefs_be(vgmstream,streamFile,0x118,0x60);
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

96
src/meta/sdf.c Normal file
View File

@ -0,0 +1,96 @@
#include "meta.h"
#include "../coding/coding.h"
/* SDF - from Beyond Reality games [Agent Hugo - Lemoon Twist (PS2)] */
VGMSTREAM * init_vgmstream_sdf_ps2(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t data_size;
int loop_flag, channel_count;
/* checks */
if ( !check_extensions(streamFile,"sdf") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x53444600) /* "SDF\0" */
goto fail;
if (read_32bitBE(0x04,streamFile) != 0x03000000) /* version? */
goto fail;
start_offset = 0x18;
data_size = get_streamfile_size(streamFile) - start_offset;
if (read_32bitLE(0x08,streamFile) != data_size)
goto fail;
channel_count = read_32bitLE(0x10,streamFile);
loop_flag = 0; /* all files have loop flags but simply fade out normally and repeat */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_SDF_PS2;
vgmstream->sample_rate = read_32bitLE(0x0c,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile);
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* SDF - from Beyond Reality games [Gummy Bears Mini Golf (3DS)] */
VGMSTREAM * init_vgmstream_sdf_3ds(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t data_size;
int loop_flag, channel_count;
/* checks */
if ( !check_extensions(streamFile,"sdf") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x53444600) /* "SDF\0" */
goto fail;
if (read_32bitBE(0x04,streamFile) != 0x03000000) /* version? */
goto fail;
start_offset = 0x78; /* assumed */
data_size = get_streamfile_size(streamFile) - start_offset;
if (read_32bitLE(0x08,streamFile) != data_size)
goto fail;
channel_count = read_32bitLE(0x14,streamFile);
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_SDF_3DS;
vgmstream->sample_rate = read_32bitLE(0x10,streamFile);
vgmstream->num_samples = dsp_bytes_to_samples(data_size,channel_count);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = data_size / channel_count;
dsp_read_coefs_le(vgmstream,streamFile,0x1c,0x2e);
//todo: there be hist around 0x3c
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

48
src/meta/svg.c Normal file
View File

@ -0,0 +1,48 @@
#include "meta.h"
#include "../coding/coding.h"
/* SVG - from High Voltage games [Hunter: The Reckoning - Wayward (PS2)] */
VGMSTREAM * init_vgmstream_svg(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t data_size, interleave;
int loop_flag, channel_count;
int32_t loop_start = 0, loop_end = 0;
/* checks */
if ( !check_extensions(streamFile,"svg") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x53564770) /* "SVGp" */
goto fail;
start_offset = 0x800;
data_size = read_32bitLE(0x18,streamFile);
interleave = read_32bitLE(0x14,streamFile);
channel_count = 2;
loop_flag = ps_find_loop_offsets(streamFile, start_offset, data_size, channel_count, interleave,&loop_start, &loop_end);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_SVG;
vgmstream->sample_rate = read_32bitBE(0x2c,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count);
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
read_string(vgmstream->stream_name,0x10+1, 0x04,streamFile);
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

45
src/meta/vai.c Normal file
View File

@ -0,0 +1,45 @@
#include "meta.h"
#include "../coding/coding.h"
/* .VAI - from Asobo Studio games [Ratatouille (GC)] */
VGMSTREAM * init_vgmstream_vai(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t data_size;
int loop_flag, channel_count;
/* checks */
if ( !check_extensions(streamFile,"vai") )
goto fail;
start_offset = 0x4060;
data_size = get_streamfile_size(streamFile) - start_offset;
if (read_32bitBE(0x04,streamFile) != data_size)
goto fail;
channel_count = 2;
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_VAI;
vgmstream->sample_rate = read_32bitBE(0x00,streamFile);
vgmstream->num_samples = dsp_bytes_to_samples(data_size,channel_count);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x4000;
dsp_read_coefs_be(vgmstream,streamFile,0x0c,0x20);
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

52
src/meta/vis.c Normal file
View File

@ -0,0 +1,52 @@
#include "meta.h"
#include "../coding/coding.h"
/* VIS - from Konami games [AirForce Delta Strike (PS2) (PS2)] */
VGMSTREAM * init_vgmstream_vis(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t data_size;
int loop_flag, channel_count;
/* checks */
if ( !check_extensions(streamFile,"vis") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x56495341) /* "VISA" */
goto fail;
start_offset = 0x800;
data_size = get_streamfile_size(streamFile) - start_offset;
loop_flag = read_32bitLE(0x18,streamFile);
channel_count = read_32bitLE(0x20,streamFile); /* assumed */
/* 0x1c: always 0x10 */
/* 0x24: always 0x01 */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_VIS;
vgmstream->sample_rate = read_32bitLE(0x08,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count);
vgmstream->loop_start_sample = ps_bytes_to_samples(read_32bitLE(0x0c,streamFile),channel_count);
vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitLE(0x10,streamFile),channel_count);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile); /* usually 0x10 or 0x4000 */
if (vgmstream->interleave_block_size)
vgmstream->interleave_last_block_size =
(data_size % (vgmstream->interleave_block_size*channel_count)) / channel_count;
read_string(vgmstream->stream_name,0x10+1, 0x28,streamFile);
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

43
src/meta/wv2.c Normal file
View File

@ -0,0 +1,43 @@
#include "meta.h"
#include "../coding/coding.h"
/* WAV2 - from Infrogrames North America games [Slave Zero (PC) (PS2)] */
VGMSTREAM * init_vgmstream_wv2(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t data_size;
int loop_flag, channel_count;
/* checks */
if ( !check_extensions(streamFile,"wv2") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x57415632) /* "WAV2" */
goto fail;
start_offset = 0x1c;
data_size = get_streamfile_size(streamFile) - start_offset;
channel_count = read_8bit(0x0c,streamFile);
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_WV2;
vgmstream->sample_rate = read_32bitLE(0x10,streamFile);
vgmstream->num_samples = ima_bytes_to_samples(data_size,channel_count); /* also 0x18 */
vgmstream->coding_type = coding_DVI_IMA_int;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0xFA;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

60
src/meta/xau_konami.c Normal file
View File

@ -0,0 +1,60 @@
#include "meta.h"
#include "../coding/coding.h"
/* XAU - from Konami games [Yu-Gi-Oh - The Dawn of Destiny (Xbox)] */
VGMSTREAM * init_vgmstream_xau_konami(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, header_offset;
size_t stream_size;
int loop_flag, channel_count, sample_rate;
off_t loop_start, loop_end;
/* checks */
if ( !check_extensions(streamFile,"xau") )
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x53465842) /* "SFXB" */
goto fail;
//todo: subsongs used in sfx packs (rare)
if (read_32bitLE(0x54,streamFile) != 1) /* subsong count */
goto fail;
start_offset = 0x60 + read_32bitLE(0x34,streamFile);
header_offset = 0x60 + 0x20 + 0x40*0; /* target subsong */
if (read_32bitBE(header_offset+0x00,streamFile) != 0x52494646) /* "RIFF" */
goto fail;
if (read_16bitLE(header_offset+0x14,streamFile) != 0x01) /* codec */
goto fail;
channel_count = read_16bitLE(header_offset+0x16,streamFile);
sample_rate = read_32bitLE(header_offset+0x18,streamFile);
loop_start = read_32bitLE(header_offset+030,streamFile);
loop_end = read_32bitLE(header_offset+0x34,streamFile);
loop_flag = (loop_end > 0);
stream_size = get_streamfile_size(streamFile) - start_offset;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XAU_KONAMI;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = pcm_bytes_to_samples(stream_size,channel_count,16);
vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start,channel_count,16);
vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end,channel_count,16);
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -408,7 +408,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_opus_ppp,
init_vgmstream_ubi_bao_pk,
init_vgmstream_dsp_switch_audio,
init_vgmstream_dsp_sadf,
init_vgmstream_sadf,
init_vgmstream_h4m,
init_vgmstream_ps2_ads_container,
init_vgmstream_asf,
@ -422,8 +422,24 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_bnk_sony,
init_vgmstream_nus3bank,
init_vgmstream_scd_sscf,
init_vgmstream_dsp_vag,
init_vgmstream_dsp_itl_ch,
init_vgmstream_a2m,
init_vgmstream_ahv,
init_vgmstream_msv,
init_vgmstream_sdf_ps2,
init_vgmstream_svg,
init_vgmstream_vis,
init_vgmstream_sdf_3ds,
init_vgmstream_vai,
init_vgmstream_aif_asobo,
init_vgmstream_ao,
init_vgmstream_apc,
init_vgmstream_wv2,
init_vgmstream_xau_konami,
/* lowest priority metas (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_ps2_int, /* .int raw PS-ADPCM */
init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */
@ -554,7 +570,7 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
#endif
if (vgmstream->coding_type==coding_CRI_HCA) {
reset_hca(vgmstream);
reset_hca(vgmstream->codec_data);
}
if (vgmstream->coding_type==coding_EA_MT) {
@ -1191,7 +1207,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_EA_MT:
return 432;
case coding_CRI_HCA:
return clHCA_samplesPerBlock;
return 0; /* 1024 - delay/padding (which can be bigger than 1024) */
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
case coding_MP4_AAC:
return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame;
@ -1657,7 +1673,7 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
#endif
case coding_CRI_HCA:
decode_hca(vgmstream->codec_data, buffer+samples_written*vgmstream->channels,
samples_to_do,vgmstream->channels);
samples_to_do);
break;
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
@ -1893,7 +1909,7 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
break;
case coding_SASSC:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_SASSC(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
decode_sassc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do);
}
@ -2019,7 +2035,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
/* prepare certain codecs' internal state for looping */
if (vgmstream->coding_type==coding_CRI_HCA) {
loop_hca(vgmstream);
loop_hca(vgmstream->codec_data);
}
if (vgmstream->coding_type==coding_EA_MT) {

View File

@ -691,6 +691,21 @@ typedef enum {
meta_HD3_BD3, /* Sony PS3 bank */
meta_BNK_SONY, /* Sony Scream Tool bank */
meta_SCD_SSCF, /* Square Enix SCD old version */
meta_DSP_VAG, /* Penny-Punching Princess (Switch) sfx */
meta_DSP_ITL, /* Charinko Hero (GC) */
meta_A2M, /* Scooby-Doo! Unmasked (PS2) */
meta_AHV, /* Headhunter (PS2) */
meta_MSV, /* Fight Club (PS2) */
meta_SDF_PS2, /* Agent Hugo - Lemoon Twist (PS2) */
meta_SVG, /* Hunter - The Reckoning - Wayward (PS2) */
meta_VIS, /* AirForce Delta Strike (PS2) */
meta_VAI, /* Ratatouille (GC) */
meta_SDF_3DS, /* Gummy Bears Mini Golf (3DS) */
meta_AIF_ASOBO, /* Ratatouille (PC) */
meta_AO, /* Cloudphobia (PC) */
meta_APC, /* MegaRace 3 (PC) */
meta_WV2, /* Slave Zero (PC) */
meta_XAU_KONAMI, /* Yu-Gi-Oh - The Dawn of Destiny (Xbox) */
} meta_t;
@ -1073,14 +1088,18 @@ typedef struct {
typedef struct {
STREAMFILE *streamfile;
uint64_t start;
//uint64_t size;
clHCA_stInfo info;
unsigned int curblock;
unsigned int sample_ptr;
unsigned int samples_discard;
signed short sample_buffer[clHCA_samplesPerBlock * 16];
//clHCA * hca exists here (pre-alloc'ed)
signed short *sample_buffer;
size_t samples_filled;
size_t samples_consumed;
size_t samples_to_discard;
void* data_buffer;
unsigned int current_block;
void* handle;
} hca_codec_data;
#ifdef VGM_USE_FFMPEG

View File

@ -11,7 +11,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_vgmstream", "winamp\in_v
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ext_libs", "ext_libs\ext_libs.vcproj", "{10E6BFC6-1E5B-46E4-BA42-F04DFBD0ABFF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "cli\test.vcproj", "{AF7D88A0-3CB1-4CD8-BAD1-0305EB996D69}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vgmstream_cli", "cli\vgmstream_cli.vcproj", "{AF7D88A0-3CB1-4CD8-BAD1-0305EB996D69}"
ProjectSection(ProjectDependencies) = postProject
{54A6AD11-5369-4895-A06F-E255ABB99B11} = {54A6AD11-5369-4895-A06F-E255ABB99B11}
{42D86561-8CE4-40F5-86CE-58C986B77502} = {42D86561-8CE4-40F5-86CE-58C986B77502}

View File

@ -13,7 +13,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdk-aac", "dependencies\fdk
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4v2", "dependencies\qaac\vcproject\mp4v2\mp4v2.vcxproj", "{86A064E2-C81B-4EEE-8BE0-A39A2E7C7C76}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "cli\test.vcxproj", "{AF7D88A0-3CB1-4CD8-BAD1-0305EB996D69}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "cli\vgmstream_cli.vcxproj", "{AF7D88A0-3CB1-4CD8-BAD1-0305EB996D69}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_vgmstream", "winamp\in_vgmstream.vcxproj", "{42D86561-8CE4-40F5-86CE-58C986B77502}"
EndProject