diff --git a/cli/test.vcproj b/cli/vgmstream_cli.vcproj similarity index 100% rename from cli/test.vcproj rename to cli/vgmstream_cli.vcproj diff --git a/cli/test.vcxproj b/cli/vgmstream_cli.vcxproj similarity index 99% rename from cli/test.vcxproj rename to cli/vgmstream_cli.vcxproj index 588672ee..e7f835d9 100644 --- a/cli/test.vcxproj +++ b/cli/vgmstream_cli.vcxproj @@ -27,6 +27,7 @@ 8.1 + test diff --git a/cli/test.vcxproj.filters b/cli/vgmstream_cli.vcxproj.filters similarity index 100% rename from cli/test.vcxproj.filters rename to cli/vgmstream_cli.vcxproj.filters diff --git a/doc/BUILD.md b/doc/BUILD.md new file mode 100644 index 00000000..2169f48b --- /dev/null +++ b/doc/BUILD.md @@ -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. diff --git a/BUILD.md b/doc/DEV.md similarity index 58% rename from BUILD.md rename to doc/DEV.md index 72879644..0c35014c 100644 --- a/BUILD.md +++ b/doc/DEV.md @@ -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. +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,20 +112,22 @@ 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. - + If the decoder needs to keep state between calls it may use the VGMSTREAM for common values (like ADPCM history), or alloc a custom data struct. In that case the decoder should provide init/free functions so the meta or vgmstream may use. This is the case with decoders implemented using external libraries (*ext_libs*), as seen in *#ifdef VGM_USE_X ... #endif* sections. Adding a new decoder involves: @@ -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. diff --git a/doc/GENH.md b/doc/GENH.md new file mode 100644 index 00000000..8b980f93 --- /dev/null +++ b/doc/GENH.md @@ -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. diff --git a/doc/TXTH.md b/doc/TXTH.md new file mode 100644 index 00000000..e02dab4b --- /dev/null +++ b/doc/TXTH.md @@ -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) + +``` diff --git a/doc/TXTP.md b/doc/TXTP.md new file mode 100644 index 00000000..7e8763a9 --- /dev/null +++ b/doc/TXTP.md @@ -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 +``` diff --git a/ext_includes/clHCA.h b/ext_includes/clHCA.h index 8961f86d..071e3a2b 100644 --- a/ext_includes/clHCA.h +++ b/ext_includes/clHCA.h @@ -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 diff --git a/ext_libs/clHCA.c b/ext_libs/clHCA.c index c2935d60..c0b30111 100644 --- a/ext_libs/clHCA.c +++ b/ext_libs/clHCA.c @@ -1,1712 +1,1641 @@ +/** + * clHCA DECODER + * + * - Original decompilation and C++ decoder by nyaga + * https://github.com/Nyagamon/HCADecoder + * - Ported to C by kode54 + * https://gist.github.com/kode54/ce2bf799b445002e125f06ed833903c0 + * - Cleaned up by bnnm using Thealexbarney's VGAudio decoder as reference + * https://github.com/Thealexbarney/VGAudio + */ +/* TODO: + * - improve portability on types and float casts, sizeof(int) isn't necessarily sizeof(float) + * - check "packed_noise_level" vs VGAudio (CriHcaPacking.UnpackFrameHeader), weird behaviour + * - check "delta scalefactors" vs VGAudio (CriHcaPacking.DeltaDecode), may be setting wrong values on bad data + * - check "read intensity" vs VGAudio (CriHcaPacking.ReadIntensity), skips intensities if first is 15 + * - simplify DCT4 code + * - add extra validations: encoder_delay/padding < sample_count, bands/totals (max: 128?), track count==1, etc + * - calling clHCA_clear multiple times will not deallocate "comment" correctly + */ //-------------------------------------------------- -// インクルード +// Includes //-------------------------------------------------- #include "clHCA.h" -//#include +#include #include #include -#ifdef _MSC_VER -#define inline __inline -#endif +#define HCA_MASK 0x7F7F7F7F /* chunk obfuscation when the HCA is encrypted (ciph type > 0) */ +#define HCA_SUBFRAMES_PER_FRAME 8 +#define HCA_SAMPLES_PER_SUBFRAME 128 +#define HCA_SAMPLES_PER_FRAME (HCA_SUBFRAMES_PER_FRAME*HCA_SAMPLES_PER_SUBFRAME) +#define HCA_MDCT_BITS 7 /* (1<<7) = 128 */ + +#define HCA_MAX_CHANNELS 16 /* internal max? in practice only 8 can be encoded */ //-------------------------------------------------- -// インライン関数 +// Decoder config/state //-------------------------------------------------- -#if 0 -static inline unsigned short get_le16(unsigned short v_){const unsigned char *v=(const unsigned char *)&v_;unsigned short r=v[1];r<<=8;r|=v[0];return r;} -static inline unsigned short get_be16(unsigned short v_){const unsigned char *v=(const unsigned char *)&v_;unsigned short r=v[0];r<<=8;r|=v[1];return r;} -static inline unsigned int get_be24(unsigned int v_){const unsigned char *v=(const unsigned char *)&v_;unsigned int r=v[0];r<<=8;r|=v[1];r<<=8;r|=v[2];return r;}; -static inline unsigned int get_le32(unsigned int v_){const unsigned char *v=(const unsigned char *)&v_;unsigned int r=v[3];r<<=8;r|=v[2];r<<=8;r|=v[1];r<<=8;r|=v[0];return r;} -static inline unsigned int get_be32(unsigned int v_){const unsigned char *v=(const unsigned char *)&v_;unsigned int r=v[0];r<<=8;r|=v[1];r<<=8;r|=v[2];r<<=8;r|=v[3];return r;} -static inline float get_bef32(float v_){union{float f;unsigned int i;}v;v.f=v_;v.i=get_be32(v.i);return v.f;} +typedef enum { DISCRETE = 0, STEREO_PRIMARY = 1, STEREO_SECONDARY = 2 } channel_type_t; +typedef struct stChannel { + /* HCA channel config */ + int type; /* discrete / stereo-primary / stereo-secondary */ + unsigned int coded_scalefactor_count; /* scalefactors used (depending on channel type) */ + unsigned char *hfr_scales; /* high frequency scales, pointing to higher scalefactors (simplification) */ -static union { unsigned int i; unsigned char c[4]; } g_is_le = {1}; -static inline unsigned short swap_u16(unsigned short v){unsigned short r=v&0xFF;r<<=8;v>>=8;r|=v&0xFF;return r;} -static inline unsigned short swap_u32(unsigned int v){unsigned int r=v&0xFF;r<<=8;v>>=8;r|=v&0xFF;r<<=8;v>>=8;r|=v&0xFF;r<<=8;v>>=8;r|=v&0xFF;return r;} -static inline unsigned short ret_le16(unsigned short v){if (g_is_le.c[0]) return v; else return swap_u16(v);} -static inline unsigned short ret_le32(unsigned int v){if (g_is_le.c[0]) return v; else return swap_u32(v);} -#endif + /* subframe state */ + unsigned char intensity[HCA_SUBFRAMES_PER_FRAME]; /* intensity indexes (value max: 15 / 4b) */ + unsigned char scalefactors[HCA_SAMPLES_PER_SUBFRAME]; /* scale indexes (value max: 64 / 6b)*/ + unsigned char resolution[HCA_SAMPLES_PER_SUBFRAME]; /* resolution indexes (value max: 15 / 4b) */ -static inline unsigned int ceil2(unsigned int a,unsigned int b){return (b>0)?(a/b+((a%b)?1:0)):0;} + float gain[HCA_SAMPLES_PER_SUBFRAME]; /* gain to apply to quantized spectral data */ + float spectra[HCA_SAMPLES_PER_SUBFRAME]; /* resulting dequantized data */ + float temp[HCA_SAMPLES_PER_SUBFRAME]; /* temp for DCT-IV */ + float dct[HCA_SAMPLES_PER_SUBFRAME]; /* result of DCT-IV */ + float imdct_previous[HCA_SAMPLES_PER_SUBFRAME]; /* IMDCT */ -//-------------------------------------------------- -// structure definitions -//-------------------------------------------------- -typedef struct stHeader{//ファイル情報 (必須) - unsigned int hca; // 'HCA' - unsigned short version; // バージョン。v1.3とv2.0の存在を確認 - unsigned short dataOffset; // データオフセット -} stHeader; -typedef struct stFormat{//フォーマット情報 (必須) - unsigned int fmt; // 'fmt' - unsigned int channelCount:8; // チャンネル数 1〜16 - unsigned int samplingRate:24; // サンプリングレート 1〜0x7FFFFF - unsigned int blockCount; // ブロック数 0以上 - unsigned short r01; // 先頭の無音部分(ブロック数*0x400+0x80) - unsigned short r02; // 末尾の無音部分?計算方法不明(0x226) -} stFormat; -typedef struct stCompress{//圧縮情報 (圧縮情報かデコード情報のどちらか一つが必須) - unsigned int comp; // 'comp' - unsigned short blockSize; // ブロックサイズ(CBRのときに有効?) 8〜0xFFFF、0のときはVBR - unsigned char r01; // 不明(1) 0〜r02 v2.0現在1のみ対応 - unsigned char r02; // 不明(15) r01〜0x1F v2.0現在15のみ対応 - unsigned char r03; // 不明(1)(1) - unsigned char r04; // 不明(1)(0) - unsigned char r05; // 不明(0x80)(0x80) - unsigned char r06; // 不明(0x80)(0x20) - unsigned char r07; // 不明(0)(0x20) - unsigned char r08; // 不明(0)(8) - unsigned char reserve1; // 予約 - unsigned char reserve2; // 予約 -} stCompress; -typedef struct stDecode{//デコード情報 (圧縮情報かデコード情報のどちらか一つが必須) - unsigned int dec; // 'dec' - unsigned short blockSize; // ブロックサイズ(CBRのときに有効?) 8〜0xFFFF、0のときはVBR - unsigned char r01; // 不明(1) 0〜r02 v2.0現在1のみ対応 - unsigned char r02; // 不明(15) r01〜0x1F v2.0現在15のみ対応 - unsigned char count1; // type0とtype1の数-1 - unsigned char count2; // type2の数-1 - unsigned char r03:4; // 不明(0) - unsigned char r04:4; // 不明(0) 0は1に修正される - unsigned char enableCount2; // count2を使うフラグ -} stDecode; -typedef struct stVBR{//可変ビットレート情報 (廃止?) - unsigned int vbr; // 'vbr' - unsigned short r01; // 不明 0〜0x1FF - unsigned short r02; // 不明 -} stVBR; -typedef struct stATH{//ATHテーブル情報 (v2.0から廃止?) - unsigned int ath; // 'ath' - unsigned short type; // テーブルの種類(0:全て0 1:テーブル1) -} stATH; -typedef struct stLoop{//ループ情報 - unsigned int loop; // 'loop' - unsigned int loopStart; // ループ開始ブロックインデックス 0〜loopEnd - unsigned int loopEnd; // ループ終了ブロックインデックス loopStart〜(stFormat::blockCount-1) - unsigned short r01; // 不明(0x80)ループフラグ?ループ回数? - unsigned short r02; // 不明(0x226) -} stLoop; -typedef struct stCipher{//暗号テーブル情報 - unsigned int ciph; // 'ciph' - unsigned short type; // 暗号化の種類(0:暗号化なし 1:鍵なし暗号化 0x38:鍵あり暗号化) -} stCipher; -typedef struct stRVA{//相対ボリューム調節情報 - unsigned int rva; // 'rva' - float volume; // ボリューム -} stRVA; -typedef struct stComment{//コメント情報 - unsigned int comm; // 'comm' - unsigned char len; // コメントの長さ? - //char comment[]; -} stComment; -typedef struct stPadding{//パディング - unsigned int pad; // 'pad' -} stPadding; -typedef struct clATH{ - unsigned char _table[0x80]; -} clATH; -typedef struct clCipher{ - unsigned char _table[0x100]; -} clCipher; -typedef struct stChannel{ - float block[0x80]; - float base[0x80]; - char value[0x80]; - char scale[0x80]; - char value2[8]; - int type; - char *value3; - unsigned int count; - float wav1[0x80]; - float wav2[0x80]; - float wav3[0x80]; - float wave[8][0x80]; + /* frame state */ + float wave[HCA_SUBFRAMES_PER_FRAME][HCA_SAMPLES_PER_SUBFRAME]; /* resulting samples */ } stChannel; -typedef struct clHCA{ - unsigned int _validFile; - unsigned int _version; - unsigned int _dataOffset; - unsigned int _channelCount; - unsigned int _samplingRate; - unsigned int _blockCount; - unsigned int _fmt_r01; - unsigned int _fmt_r02; - unsigned int _blockSize; - unsigned int _comp_r01; - unsigned int _comp_r02; - unsigned int _comp_r03; - unsigned int _comp_r04; - unsigned int _comp_r05; - unsigned int _comp_r06; - unsigned int _comp_r07; - unsigned int _comp_r08; - unsigned int _comp_r09; - unsigned int _vbr_r01; - unsigned int _vbr_r02; - unsigned int _ath_type; - unsigned int _loopStart; - unsigned int _loopEnd; - unsigned int _loop_r01; - unsigned int _loop_r02; - unsigned int _loopFlg; - unsigned int _ciph_type; - unsigned int _ciph_key1; - unsigned int _ciph_key2; - float _rva_volume; - unsigned int _comm_len; - char *_comm_comment; - clATH _ath; - clCipher _cipher; - stChannel _channel[0x10]; + +typedef struct clHCA { + /* header config */ + unsigned int is_valid; + /* hca chunk */ + unsigned int version; + unsigned int header_size; + /* fmt chunk */ + unsigned int channels; + unsigned int sample_rate; + unsigned int frame_count; + unsigned int encoder_delay; + unsigned int encoder_padding; + /* comp/dec chunk */ + unsigned int frame_size; + unsigned int min_resolution; + unsigned int max_resolution; + unsigned int track_count; + unsigned int channel_config; + unsigned int stereo_type; + unsigned int total_band_count; + unsigned int base_band_count; + unsigned int stereo_band_count; + unsigned int bands_per_hfr_group; + unsigned int reserved1; + unsigned int reserved2; + /* vbr chunk */ + unsigned int vbr_max_frame_size; + unsigned int vbr_noise_Level; + /* ath chunk */ + unsigned int ath_type; + /* loop chunk */ + unsigned int loop_start_frame; + unsigned int loop_end_frame; + unsigned int loop_start_delay; + unsigned int loop_end_padding; + unsigned int loop_flag; + /* ciph chunk */ + unsigned int ciph_type; + unsigned long long keycode; + /* rva chunk */ + float rva_volume; + /* comm chunk */ + unsigned int comment_len; + char *comment; + + /* initial state */ + unsigned int hfr_group_count; + unsigned char ath_curve[HCA_SAMPLES_PER_SUBFRAME]; + unsigned char cipher_table[256]; + /* variable state */ + stChannel channel[HCA_MAX_CHANNELS]; } clHCA; -typedef struct clData{ - const unsigned char *_data; - int _size; - int _bit; + +typedef struct clData { + const unsigned char *data; + int size; + int bit; } clData; -//-------------------------------------------------- -// コンストラクタ -//-------------------------------------------------- -static void clATH_constructor(clATH *ath); -static void clCipher_constructor(clCipher *cipher); - -static void clHCA_constructor(clHCA *hca,unsigned int ciphKey1,unsigned int ciphKey2){ - memset(hca,0,sizeof(*hca)); - hca->_ciph_key1 = ciphKey1; - hca->_ciph_key2 = ciphKey2; - clATH_constructor(&hca->_ath); - clCipher_constructor(&hca->_cipher); - hca->_validFile = 0; - hca->_comm_comment = 0; -} - -static void clHCA_destructor(clHCA *hca) -{ - if(hca->_comm_comment){free(hca->_comm_comment);hca->_comm_comment=0;} -} //-------------------------------------------------- -// HCAチェック +// Checksum //-------------------------------------------------- -#if 0 -static int clHCA_CheckFile(void *data,unsigned int size){ - return (data&&size>=4&&(get_be32(*(unsigned int *)data)&0x7F7F7F7F)==0x48434100);/*'HCA\0'*/ -} -#endif - -//-------------------------------------------------- -// チェックサム -//-------------------------------------------------- -static const unsigned short clHCA_CheckSum_v[]={ - 0x0000,0x8005,0x800F,0x000A,0x801B,0x001E,0x0014,0x8011,0x8033,0x0036,0x003C,0x8039,0x0028,0x802D,0x8027,0x0022, - 0x8063,0x0066,0x006C,0x8069,0x0078,0x807D,0x8077,0x0072,0x0050,0x8055,0x805F,0x005A,0x804B,0x004E,0x0044,0x8041, - 0x80C3,0x00C6,0x00CC,0x80C9,0x00D8,0x80DD,0x80D7,0x00D2,0x00F0,0x80F5,0x80FF,0x00FA,0x80EB,0x00EE,0x00E4,0x80E1, - 0x00A0,0x80A5,0x80AF,0x00AA,0x80BB,0x00BE,0x00B4,0x80B1,0x8093,0x0096,0x009C,0x8099,0x0088,0x808D,0x8087,0x0082, - 0x8183,0x0186,0x018C,0x8189,0x0198,0x819D,0x8197,0x0192,0x01B0,0x81B5,0x81BF,0x01BA,0x81AB,0x01AE,0x01A4,0x81A1, - 0x01E0,0x81E5,0x81EF,0x01EA,0x81FB,0x01FE,0x01F4,0x81F1,0x81D3,0x01D6,0x01DC,0x81D9,0x01C8,0x81CD,0x81C7,0x01C2, - 0x0140,0x8145,0x814F,0x014A,0x815B,0x015E,0x0154,0x8151,0x8173,0x0176,0x017C,0x8179,0x0168,0x816D,0x8167,0x0162, - 0x8123,0x0126,0x012C,0x8129,0x0138,0x813D,0x8137,0x0132,0x0110,0x8115,0x811F,0x011A,0x810B,0x010E,0x0104,0x8101, - 0x8303,0x0306,0x030C,0x8309,0x0318,0x831D,0x8317,0x0312,0x0330,0x8335,0x833F,0x033A,0x832B,0x032E,0x0324,0x8321, - 0x0360,0x8365,0x836F,0x036A,0x837B,0x037E,0x0374,0x8371,0x8353,0x0356,0x035C,0x8359,0x0348,0x834D,0x8347,0x0342, - 0x03C0,0x83C5,0x83CF,0x03CA,0x83DB,0x03DE,0x03D4,0x83D1,0x83F3,0x03F6,0x03FC,0x83F9,0x03E8,0x83ED,0x83E7,0x03E2, - 0x83A3,0x03A6,0x03AC,0x83A9,0x03B8,0x83BD,0x83B7,0x03B2,0x0390,0x8395,0x839F,0x039A,0x838B,0x038E,0x0384,0x8381, - 0x0280,0x8285,0x828F,0x028A,0x829B,0x029E,0x0294,0x8291,0x82B3,0x02B6,0x02BC,0x82B9,0x02A8,0x82AD,0x82A7,0x02A2, - 0x82E3,0x02E6,0x02EC,0x82E9,0x02F8,0x82FD,0x82F7,0x02F2,0x02D0,0x82D5,0x82DF,0x02DA,0x82CB,0x02CE,0x02C4,0x82C1, - 0x8243,0x0246,0x024C,0x8249,0x0258,0x825D,0x8257,0x0252,0x0270,0x8275,0x827F,0x027A,0x826B,0x026E,0x0264,0x8261, - 0x0220,0x8225,0x822F,0x022A,0x823B,0x023E,0x0234,0x8231,0x8213,0x0216,0x021C,0x8219,0x0208,0x820D,0x8207,0x0202, +static const unsigned short crc16_lookup_table[256] = { + 0x0000,0x8005,0x800F,0x000A,0x801B,0x001E,0x0014,0x8011,0x8033,0x0036,0x003C,0x8039,0x0028,0x802D,0x8027,0x0022, + 0x8063,0x0066,0x006C,0x8069,0x0078,0x807D,0x8077,0x0072,0x0050,0x8055,0x805F,0x005A,0x804B,0x004E,0x0044,0x8041, + 0x80C3,0x00C6,0x00CC,0x80C9,0x00D8,0x80DD,0x80D7,0x00D2,0x00F0,0x80F5,0x80FF,0x00FA,0x80EB,0x00EE,0x00E4,0x80E1, + 0x00A0,0x80A5,0x80AF,0x00AA,0x80BB,0x00BE,0x00B4,0x80B1,0x8093,0x0096,0x009C,0x8099,0x0088,0x808D,0x8087,0x0082, + 0x8183,0x0186,0x018C,0x8189,0x0198,0x819D,0x8197,0x0192,0x01B0,0x81B5,0x81BF,0x01BA,0x81AB,0x01AE,0x01A4,0x81A1, + 0x01E0,0x81E5,0x81EF,0x01EA,0x81FB,0x01FE,0x01F4,0x81F1,0x81D3,0x01D6,0x01DC,0x81D9,0x01C8,0x81CD,0x81C7,0x01C2, + 0x0140,0x8145,0x814F,0x014A,0x815B,0x015E,0x0154,0x8151,0x8173,0x0176,0x017C,0x8179,0x0168,0x816D,0x8167,0x0162, + 0x8123,0x0126,0x012C,0x8129,0x0138,0x813D,0x8137,0x0132,0x0110,0x8115,0x811F,0x011A,0x810B,0x010E,0x0104,0x8101, + 0x8303,0x0306,0x030C,0x8309,0x0318,0x831D,0x8317,0x0312,0x0330,0x8335,0x833F,0x033A,0x832B,0x032E,0x0324,0x8321, + 0x0360,0x8365,0x836F,0x036A,0x837B,0x037E,0x0374,0x8371,0x8353,0x0356,0x035C,0x8359,0x0348,0x834D,0x8347,0x0342, + 0x03C0,0x83C5,0x83CF,0x03CA,0x83DB,0x03DE,0x03D4,0x83D1,0x83F3,0x03F6,0x03FC,0x83F9,0x03E8,0x83ED,0x83E7,0x03E2, + 0x83A3,0x03A6,0x03AC,0x83A9,0x03B8,0x83BD,0x83B7,0x03B2,0x0390,0x8395,0x839F,0x039A,0x838B,0x038E,0x0384,0x8381, + 0x0280,0x8285,0x828F,0x028A,0x829B,0x029E,0x0294,0x8291,0x82B3,0x02B6,0x02BC,0x82B9,0x02A8,0x82AD,0x82A7,0x02A2, + 0x82E3,0x02E6,0x02EC,0x82E9,0x02F8,0x82FD,0x82F7,0x02F2,0x02D0,0x82D5,0x82DF,0x02DA,0x82CB,0x02CE,0x02C4,0x82C1, + 0x8243,0x0246,0x024C,0x8249,0x0258,0x825D,0x8257,0x0252,0x0270,0x8275,0x827F,0x027A,0x826B,0x026E,0x0264,0x8261, + 0x0220,0x8225,0x822F,0x022A,0x823B,0x023E,0x0234,0x8231,0x8213,0x0216,0x021C,0x8219,0x0208,0x820D,0x8207,0x0202, }; -static unsigned short clHCA_CheckSum(const void *data,int size,unsigned short sum){ - const unsigned char *s, *e; - for(s=(const unsigned char *)data,e=s+size;s>8)^*s]; - return sum; +static unsigned short crc16_checksum(const unsigned char *data, unsigned int size) { + unsigned int i; + unsigned short sum = 0; + + /* HCA header/frames should always have checksum 0 (checksum(size-16b) = last 16b) */ + for (i = 0; i < size; i++) { + sum = (sum << 8) ^ crc16_lookup_table[(sum >> 8) ^ data[i]]; + } + return sum; } //-------------------------------------------------- -// データ +// Bitstream reader //-------------------------------------------------- -void clData_constructor(clData *ds,const void *data,int size){ - ds->_data=(const unsigned char *)data; - ds->_size=size*8; - ds->_bit=0; +static void bitreader_init(clData *br, const void *data, int size) { + br->data = data; + br->size = size * 8; + br->bit = 0; } -static unsigned int clData_CheckBit(clData *ds,int bitSize){ - unsigned int v=0; - if(ds->_bit+bitSize<=ds->_size){ - unsigned int bitOffset=bitSize+(ds->_bit&7); - if((ds->_size-ds->_bit)>=32&&bitOffset>=25){ - static const int mask[]={0xFFFFFFFF,0x7FFFFFFF,0x3FFFFFFF,0x1FFFFFFF,0x0FFFFFFF,0x07FFFFFF,0x03FFFFFF,0x01FFFFFF}; - const unsigned int _bit=ds->_bit; - const unsigned char *data=&ds->_data[_bit>>3]; - v=data[0];v=(v<<8)|data[1];v=(v<<8)|data[2];v=(v<<8)|data[3]; - v&=mask[_bit&7]; - v>>=32-(_bit&7)-bitSize; - } - else if((ds->_size-ds->_bit)>=24&&bitOffset>=17){ - static const int mask[]={0xFFFFFF,0x7FFFFF,0x3FFFFF,0x1FFFFF,0x0FFFFF,0x07FFFF,0x03FFFF,0x01FFFF}; - const unsigned int _bit=ds->_bit; - const unsigned char *data=&ds->_data[_bit>>3]; - v=data[0];v=(v<<8)|data[1];v=(v<<8)|data[2]; - v&=mask[_bit&7]; - v>>=24-(_bit&7)-bitSize; - } - else if((ds->_size-ds->_bit)>=16&&bitOffset>=9){ - static const int mask[]={0xFFFF,0x7FFF,0x3FFF,0x1FFF,0x0FFF,0x07FF,0x03FF,0x01FF}; - const unsigned int _bit=ds->_bit; - const unsigned char *data=&ds->_data[_bit>>3]; - v=data[0];v=(v<<8)|data[1]; - v&=mask[_bit&7]; - v>>=16-(_bit&7)-bitSize; - } - else{ - static const int mask[]={0xFF,0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01}; - const unsigned int _bit=ds->_bit; - const unsigned char *data=&ds->_data[_bit>>3]; - v=data[0]; - v&=mask[_bit&7]; - v>>=8-(_bit&7)-bitSize; - } - } - return v; +static unsigned int bitreader_peek(clData *br, int bitsize) { + const unsigned int bit = br->bit; + const unsigned int bit_rem = bit & 7; + const unsigned int size = br->size; + unsigned int v = 0; + unsigned int bit_offset, bit_left; + + if (!(bit + bitsize <= size)) + return v; + + bit_offset = bitsize + bit_rem; + bit_left = size - bit; + if (bit_left >= 32 && bit_offset >= 25) { + static const unsigned int mask[8] = { + 0xFFFFFFFF,0x7FFFFFFF,0x3FFFFFFF,0x1FFFFFFF, + 0x0FFFFFFF,0x07FFFFFF,0x03FFFFFF,0x01FFFFFF + }; + const unsigned char *data = &br->data[bit >> 3]; + v = data[0]; + v = (v << 8) | data[1]; + v = (v << 8) | data[2]; + v = (v << 8) | data[3]; + v &= mask[bit_rem]; + v >>= 32 - bit_rem - bitsize; + } + else if (bit_left >= 24 && bit_offset >= 17) { + static const unsigned int mask[8] = { + 0xFFFFFF,0x7FFFFF,0x3FFFFF,0x1FFFFF, + 0x0FFFFF,0x07FFFF,0x03FFFF,0x01FFFF + }; + const unsigned char *data = &br->data[bit >> 3]; + v = data[0]; + v = (v << 8) | data[1]; + v = (v << 8) | data[2]; + v &= mask[bit_rem]; + v >>= 24 - bit_rem - bitsize; + } + else if (bit_left >= 16 && bit_offset >= 9) { + static const unsigned int mask[8] = { + 0xFFFF,0x7FFF,0x3FFF,0x1FFF,0x0FFF,0x07FF,0x03FF,0x01FF + }; + const unsigned char *data = &br->data[bit >> 3]; + v = data[0]; + v = (v << 8) | data[1]; + v &= mask[bit_rem]; + v >>= 16 - bit_rem - bitsize; + } + else { + static const unsigned int mask[8] = { + 0xFF,0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01 + }; + const unsigned char *data = &br->data[bit >> 3]; + v = data[0]; + v &= mask[bit_rem]; + v >>= 8 - bit_rem - bitsize; + } + return v; } -static unsigned int clData_GetBit(clData *ds,int bitSize){ - unsigned int v=clData_CheckBit(ds,bitSize); - ds->_bit+=bitSize; - return v; +static unsigned int bitreader_read(clData *br, int bitsize) { + unsigned int v = bitreader_peek(br, bitsize); + br->bit += bitsize; + return v; } -static void clData_AddBit(clData *ds,int bitSize){ - ds->_bit+=bitSize; +static void bitreader_skip(clData *br, int bitsize) { + br->bit += bitsize; } //-------------------------------------------------- -// ヘッダ情報をコンソール出力 -//-------------------------------------------------- -#if 0 -static int clHCA_PrintInfo(const char *filenameHCA){ - FILE *fp; - stHeader header; - unsigned char *data; - unsigned int size; - clData d; - - // temporaries, so we don't need a state structure - unsigned int _version; - unsigned int _dataOffset; - unsigned int _channelCount; - unsigned int _samplingRate; - unsigned int _blockCount; - unsigned int _fmt_r01; - unsigned int _fmt_r02; - unsigned int _blockSize; - unsigned int _comp_r01; - unsigned int _comp_r02; - unsigned int _comp_r03; - unsigned int _comp_r04; - unsigned int _comp_r05; - unsigned int _comp_r06; - unsigned int _comp_r07; - unsigned int _comp_r08; - unsigned int _comp_r09; - unsigned int _vbr_r01; - unsigned int _vbr_r02; - unsigned int _ath_type; - unsigned int _loopStart; - unsigned int _loopEnd; - unsigned int _loop_r01; - unsigned int _loop_r02; - unsigned int _loopFlg; - unsigned int _ciph_type; - unsigned int _ciph_key1; - unsigned int _ciph_key2; - float _rva_volume; - unsigned int _comm_len; - char *_comm_comment; - - // チェック - if(!(filenameHCA))return -1; - - // HCAファイルを開く - if((fp = fopen(filenameHCA,"rb")) == NULL){ - printf("Error: ファイルが開けませんでした。\n"); - return -1; - } - - // ヘッダチェック - memset(&header,0,sizeof(header)); - fread(&header,sizeof(header),1,fp); - if(!clHCA_CheckFile(&header,sizeof(header))){ - printf("Error: HCAファイルではありません。\n"); - fclose(fp);return -1; - } - - // ヘッダ解析 - clData_constructor(&d, &header, sizeof(header)); - clData_AddBit(&d, 32); - header.dataOffset=clData_CheckBit(&d, 16); - data=(unsigned char*) malloc(header.dataOffset); - if(!data){ - printf("Error: メモリ不足です。\n"); - fclose(fp);return -1; - } - fseek(fp,0,SEEK_SET); - fread(data,header.dataOffset,1,fp); - - size=header.dataOffset; - - clData_constructor(&d, data, size); - - // サイズチェック - if(size=sizeof(stHeader) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x48434100){/*'HCA\0'*/ - clData_AddBit(&d,32); - _version=clData_GetBit(&d,16); - _dataOffset=clData_GetBit(&d,16); - printf("コーデック: HCA\n"); - printf("バージョン: %d.%d\n",_version>>8,_version&0xFF); - //if(size<_dataOffset)return -1; - if(clHCA_CheckSum(data,_dataOffset,0))printf("※ ヘッダが破損しています。改変してる場合もこの警告が出ます。\n"); - size-=(16+16+32)/8; - }else{ - printf("※ HCAチャンクがありません。再生に必要な情報です。\n"); - } - - // fmt - if(size>=sizeof(stFormat) && (clData_CheckBit(&d, 32)&0x7F7F7F7F)==0x666D7400){/*'fmt\0'*/ - clData_AddBit(&d,32); - _channelCount=clData_GetBit(&d,8); - _samplingRate=clData_GetBit(&d,24); - _blockCount=clData_GetBit(&d,32); - _fmt_r01=clData_GetBit(&d,16); - _fmt_r02=clData_GetBit(&d,16); - switch(_channelCount){ - case 1:printf("チャンネル数: モノラル (1チャンネル)\n");break; - case 2:printf("チャンネル数: ステレオ (2チャンネル)\n");break; - default:printf("チャンネル数: %dチャンネル\n",_channelCount);break; - } - if(!(_channelCount>=1&&_channelCount<=16)){ - printf("※ チャンネル数の範囲は1〜16です。\n"); - } - printf("サンプリングレート: %dHz\n",_samplingRate); - if(!(_samplingRate>=1&&_samplingRate<=0x7FFFFF)){ - printf("※ サンプリングレートの範囲は1〜8388607(0x7FFFFF)です。\n"); - } - printf("ブロック数: %d\n",_blockCount); - printf("先頭無音ブロック数: %d\n",(_fmt_r01-0x80)/0x400); - printf("末尾無音サンプル数?: %d\n",_fmt_r02); - size-=(16+16+32+24+8+32)/8; - }else{ - printf("※ fmtチャンクがありません。再生に必要な情報です。\n"); - } - - // comp - if(size>=sizeof(stCompress) && (clData_CheckBit(&d, 32)&0x7F7F7F7F)==0x636F6D70){/*'comp'*/ - clData_AddBit(&d,32); - _blockSize=clData_GetBit(&d,16); - _comp_r01=clData_GetBit(&d,8); - _comp_r02=clData_GetBit(&d,8); - _comp_r03=clData_GetBit(&d,8); - _comp_r04=clData_GetBit(&d,8); - _comp_r05=clData_GetBit(&d,8); - _comp_r06=clData_GetBit(&d,8); - _comp_r07=clData_GetBit(&d,8); - _comp_r08=clData_GetBit(&d,8); - clData_AddBit(&d,8+8); - printf("ビットレート: CBR (固定ビットレート)\n"); - printf("ブロックサイズ: 0x%04X\n",_blockSize); - if(!(_blockSize>=8&&_blockSize<=0xFFFF)){ - printf("※ ブロックサイズの範囲は8〜65535(0xFFFF)です。v1.3では0でVBRになるようになってましたが、v2.0から廃止されたようです。\n"); - } - printf("comp1: %d\n",_comp_r01); - printf("comp2: %d\n",_comp_r02); - if(!(_comp_r01>=0&&_comp_r01<=_comp_r02&&_comp_r02<=0x1F)){ - printf("※ comp1とcomp2の範囲は0<=comp1<=comp2<=31です。v2.0現在、comp1は1、comp2は15で固定されています。\n"); - } - printf("comp3: %d\n",_comp_r03); - if(!_comp_r03){ - printf("※ comp3は1以上の値です。\n"); - } - printf("comp4: %d\n",_comp_r04); - printf("comp5: %d\n",_comp_r05); - printf("comp6: %d\n",_comp_r06); - printf("comp7: %d\n",_comp_r07); - printf("comp8: %d\n",_comp_r08); - size-=(32+16+8*8)/8; - } - - // dec - else if(size>=sizeof(stDecode) && (clData_CheckBit(&d, 32)&0x7F7F7F7F)==0x64656300){/*'dec\0'*/ - unsigned char count1,count2,enableCount2; - clData_AddBit(&d, 32); - _blockSize=clData_GetBit(&d,16); - _comp_r01=clData_GetBit(&d,8); - _comp_r02=clData_GetBit(&d,8); - count1=clData_GetBit(&d,8); - count2=clData_GetBit(&d,8); - _comp_r03=clData_GetBit(&d,4); - _comp_r04=clData_GetBit(&d,4); - enableCount2=clData_GetBit(&d,8); - _comp_r05=count1+1; - _comp_r06=((enableCount2)?count2:count1)+1; - _comp_r07=_comp_r05-_comp_r06; - _comp_r08=0; - printf("ビットレート: CBR (固定ビットレート)\n"); - printf("ブロックサイズ: 0x%04X\n",_blockSize); - if(!(_blockSize>=8&&_blockSize<=0xFFFF)){ - printf("※ ブロックサイズの範囲は8〜65535(0xFFFF)です。v1.3では0でVBRになるようになってましたが、v2.0から廃止されたようです。\n"); - } - printf("dec1: %d\n",_comp_r01); - printf("dec2: %d\n",_comp_r02); - if(!(_comp_r01>=0&&_comp_r01<=_comp_r02&&_comp_r02<=0x1F)){ - printf("※ dec1とdec2の範囲は0<=dec1<=dec2<=31です。v2.0現在、dec1は1、dec2は15で固定されています。\n"); - } - printf("dec3: %d\n",_comp_r03); - if(!_comp_r03){ - printf("※ dec3は再生時に1以上の値に修正されます。\n"); - } - printf("dec4: %d\n",_comp_r04); - printf("dec5: %d\n",_comp_r05); - printf("dec6: %d\n",_comp_r06); - printf("dec7: %d\n",_comp_r07); - size-=(8+4+4+8+8+8+8+16+32)/8; - }else{ - printf("※ compチャンクまたはdecチャンクがありません。再生に必要な情報です。\n"); - } - - // vbr - if(size>=sizeof(stVBR) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x76627200){/*'vbr\0'*/ - clData_AddBit(&d, 32); - _vbr_r01=clData_GetBit(&d,16); - _vbr_r02=clData_GetBit(&d,16); - printf("ビットレート: VBR (可変ビットレート) ※v2.0で廃止されています。\n"); - if(!(_blockSize==0)){ - printf("※ compまたはdecチャンクですでにCBRが指定されています。\n"); - } - printf("vbr1: %d\n",_vbr_r01); - if(!(_vbr_r01>=0&&_vbr_r01<=0x1FF)){ - printf("※ vbr1の範囲は0〜511(0x1FF)です。\n"); - } - printf("vbr2: %d\n",_vbr_r02); - size-=(16+16+32)/8; - }else{ - _vbr_r01=0; - _vbr_r02=0; - } - - // ath - if(size>=6 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x61746800){/*'ath\0'*/ - clData_AddBit(&d,32); - _ath_type=clData_GetBit(&d,16); - printf("ATHタイプ:%d ※v2.0から廃止されています。\n",_ath_type); - size-=(16+32)/8; - }else{ - if(_version<0x200){ - printf("ATHタイプ:1 ※v2.0から廃止されています。\n"); - } - } - - // loop - if(size>=sizeof(stLoop) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x6C6F6F70){/*'loop'*/ - clData_AddBit(&d, 32); - _loopStart=clData_GetBit(&d,32); - _loopEnd=clData_GetBit(&d,32); - _loop_r01=clData_GetBit(&d,16); - _loop_r02=clData_GetBit(&d,16); - printf("ループ開始ブロック: %d\n",_loopStart); - printf("ループ終了ブロック: %d\n",_loopEnd); - if(!(_loopStart>=0&&_loopStart<=_loopEnd&&_loopEnd<_blockCount)){ - printf("※ ループ開始ブロックとループ終了ブロックの範囲は、0<=ループ開始ブロック<=ループ終了ブロック<ブロック数 です。\n"); - } - printf("ループ情報1: %d\n",_loop_r01); - printf("ループ情報2: %d\n",_loop_r02); - size-=(16+16+32+32+32)/8; - } - - // ciph - if(size>=6 && (clData_CheckBit(&d, 32)&0x7F7F7F7F)==0x63697068){/*'ciph'*/ - clData_AddBit(&d,32); - _ciph_type=clData_GetBit(&d,16); - switch(_ciph_type){ - case 0:printf("暗号化タイプ: なし\n");break; - case 1:printf("暗号化タイプ: 鍵無し暗号化\n");break; - case 0x38:printf("暗号化タイプ: 鍵有り暗号化 ※正しい鍵を使わないと出力波形がおかしくなります。\n");break; - default:printf("暗号化タイプ: %d\n",_ciph_type);break; - } - if(!(_ciph_type==0||_ciph_type==1||_ciph_type==0x38)){ - printf("※ この暗号化タイプは、v2.0現在再生できません。\n"); - } - size-=6; - } - - // rva - if(size>=sizeof(stRVA) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x72766100){/*'rva\0'*/ - union { unsigned int i; float f; } v; - clData_AddBit(&d,32); - v.i=clData_GetBit(&d,32); - _rva_volume=v.f; - printf("相対ボリューム調節: %g倍\n",_rva_volume); - size-=(32+32)/8; - } - - // comm - if(size>=5 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x636F6D6D){/*'comm'*/ - int i; - clData_AddBit(&d,32); - _comm_len=clData_GetBit(&d,8); - _comm_comment=malloc(_comm_len+1); - for(i=0;i<_comm_len;++i)_comm_comment[i]=clData_GetBit(&d,8); - _comm_comment[i]='\0'; - printf("コメント: %s\n",_comm_comment); - free(_comm_comment); - } - - free(data); - - // 閉じる - fclose(fp); - - return 0; -} -#endif - -//-------------------------------------------------- -// デコードしてWAVEファイルに保存 -//-------------------------------------------------- -#if 0 -static int clHCA_DecodeToWavefile_Decode(void *fp1,void *fp2,unsigned int address,unsigned int count,void *data,void *modeFunction); - -static void clHCA_DecodeToWavefile_DecodeModeFloat(float f,void *fp){fwrite(&f,sizeof(f),1,(FILE *)fp);} -static void clHCA_DecodeToWavefile_DecodeMode8bit(float f,void *fp){int v=(int)(f*0x7F)+0x80;fwrite(&v,1,1,(FILE *)fp);} -static void clHCA_DecodeToWavefile_DecodeMode16bit(float f,void *fp){int v=(int)(f*0x7FFF);fwrite(&v,2,1,(FILE *)fp);} -static void clHCA_DecodeToWavefile_DecodeMode24bit(float f,void *fp){int v=(int)(f*0x7FFFFF);fwrite(&v,3,1,(FILE *)fp);} -static void clHCA_DecodeToWavefile_DecodeMode32bit(float f,void *fp){int v=(int)(f*0x7FFFFFFF);fwrite(&v,4,1,(FILE *)fp);} - -static int clHCA_DecodeToWavefile(clHCA *hca,const char *filenameHCA,const char *filenameWAV,float volume,int mode,int loop){ - FILE *fp1; - FILE *fp2; - stHeader header; - unsigned char *data1; - unsigned char *data2; - struct stWAVEHeader{ - char riff[4]; - unsigned int riffSize; - char wave[4]; - char fmt[4]; - unsigned int fmtSize; - unsigned short fmtType; - unsigned short fmtChannelCount; - unsigned int fmtSamplingRate; - unsigned int fmtSamplesPerSec; - unsigned short fmtSamplingSize; - unsigned short fmtBitCount; - }wavRiff={'R','I','F','F',0,'W','A','V','E','f','m','t',' ',ret_le32(0x10),0,0,0,0,0,0}; - struct stWAVEsmpl{ - char smpl[4]; - unsigned int smplSize; - unsigned int manufacturer; - unsigned int product; - unsigned int samplePeriod; - unsigned int MIDIUnityNote; - unsigned int MIDIPitchFraction; - unsigned int SMPTEFormat; - unsigned int SMPTEOffset; - unsigned int sampleLoops; - unsigned int samplerData; - unsigned int loop_Identifier; - unsigned int loop_Type; - unsigned int loop_Start; - unsigned int loop_End; - unsigned int loop_Fraction; - unsigned int loop_PlayCount; - }wavSmpl={'s','m','p','l',ret_le32(0x3C),0,0,0,ret_le32(0x3C),0,0,0,ret_le32(1),ret_le32(0x18),0,0,0,0,0,0}; - struct stWAVEnote{ - char note[4]; - unsigned int noteSize; - unsigned int dwName; - }wavNote={'n','o','t','e',0,0}; - struct stWAVEdata{ - char data[4]; - unsigned int dataSize; - }wavData={'d','a','t','a',0}; - - // チェック - if(!(filenameHCA&&filenameWAV&&(mode==0||mode==8||mode==16||mode==24||mode==32)&&loop>=0))return -1; - - // HCAファイルを開く - if((fp1 = fopen(filenameHCA,"rb")) == NULL)return -1; - - // ヘッダチェック - memset(&header,0,sizeof(header)); - fread(&header,sizeof(header),1,fp1); - if(!CheckFile(&header,sizeof(header))){fclose(fp1);return -1;} - - // ヘッダ解析 - header.dataOffset=get_be16(header.dataOffset); - data1=(unsigned char*) malloc(header.dataOffset); - if(!data1){fclose(fp1);return -1;} - fseek(fp1,0,SEEK_SET); - fread(data1,header.dataOffset,1,fp1); - if(clHCA_Decode(cl,data1,header.dataOffset,0) < 0){free(data1);fclose(fp1);return -1;} - - // WAVEファイルを開く - if((fp2 = fopen(filenameWAV,"wb")) == NULL){free(data1);fclose(fp1);return -1;} - - // WAVEヘッダを書き込み - wavRiff.fmtType=ret_le16((mode>0)?1:3); - wavRiff.fmtChannelCount=ret_le16(hca->_channelCount); - wavRiff.fmtBitCount=ret_le16((mode>0)?mode:32); - wavRiff.fmtSamplingRate=ret_le32(hca->_samplingRate); - wavRiff.fmtSamplingSize=ret_le16(wavRiff.fmtBitCount/8*wavRiff.fmtChannelCount); - wavRiff.fmtSamplesPerSec=ret_le32(wavRiff.fmtSamplingRate*wavRiff.fmtSamplingSize); - if(hca->_loopFlg){ - wavSmpl.samplePeriod=ret_le32((unsigned int)(1/(double)wavRiff.fmtSamplingRate*1000000000)); - wavSmpl.loop_Start=ret_le32(hca->_loopStart*0x80*8*wavRiff.fmtSamplingSize); - wavSmpl.loop_End=ret_le32(hca->_loopEnd*0x80*8*wavRiff.fmtSamplingSize); - wavSmpl.loop_PlayCount=ret_le32((hca->_loop_r01==0x80)?0:hca->_loop_r01); - }else if(loop){ - wavSmpl.loop_Start=0; - wavSmpl.loop_End=ret_le32(hca->_blockCount*0x80*8*wavRiff.fmtSamplingSize); - hca->_loopStart=0; - hca->_loopEnd=hca->_blockCount; - } - if(_comm_comment){ - unsigned int noteSize=4+hca->_comm_len+1; - if(noteSize&3)noteSize+=4-(noteSize&3); - waveNote.noteSize=ret_le32(noteSize); - } - wavData.dataSize=ret_le32(hca->_blockCount*0x80*8*wavRiff.fmtSamplingSize+(wavSmpl.loop_End-wavSmpl.loop_Start)*loop); - wavRiff.riffSize=ret_le32(0x1C+((hca->_loopFlg&&!loop)?sizeof(wavSmpl):0)+(hca->_comm_comment?8+wavNote.noteSize:0)+sizeof(wavData)+wavData.dataSize); - fwrite(&wavRiff,sizeof(wavRiff),1,fp2); - if(hca->_loopFlg&&!loop)fwrite(&wavSmpl,sizeof(wavSmpl),1,fp2); - if(hca->_comm_comment){ - int address=ftell(fp2); - fwrite(&wavNote,sizeof(wavNote),1,fp2); - fputs(hca->_comm_comment,fp2); - fseek(fp2,address+8+wavNote.noteSize,SEEK_SET); - } - fwrite(&wavData,sizeof(wavData),1,fp2); - - // 相対ボリュームを調節 - hca->_rva_volume*=volume; - - // デコード - void *modeFunction; - switch(mode){ - case 0:modeFunction=(void *)clHCA_DecodeToWavefile_DecodeModeFloat;break; - case 8:modeFunction=(void *)clHCA_DecodeToWavefile_DecodeMode8bit;break; - case 16:modeFunction=(void *)clHCA_DecodeToWavefile_DecodeMode16bit;break; - case 24:modeFunction=(void *)clHCA_DecodeToWavefile_DecodeMode24bit;break; - case 32:modeFunction=(void *)clHCA_DecodeToWavefile_DecodeMode32bit;break; - } - data2=(unsigned char*) malloc(_blockSize); - if(!data2){free(data1);fclose(fp2);fclose(fp1);return -1;} - if(!loop){ - if(clHCA_DecodeToWavefile_Decode(cl,fp1,fp2,_dataOffset,_blockCount,data2,modeFunction) < 0){free(data2);free(data1);fclose(fp2);fclose(fp1);return -1;} - }else{ - unsigned int loopBlockOffset=hca->_dataOffset+hca->_loopStart*hca->_blockSize; - unsigned int loopBlockCount=hca->_loopEnd-hca->_loopStart; - int i; - if(clHCA_DecodeToWavefile_Decode(cl,fp1,fp2,_dataOffset,_loopEnd,data2,modeFunction) < 0){free(data2);free(data1);fclose(fp2);fclose(fp1);return -1;} - for(i=1;i_blockSize,1,(FILE *)fp1); - if(clHCA_Decode(cl,data,hca->_blockSize,address) < 0)return -1; - for(i=0;i<8;i++){ - for(j=0;j<0x80;j++){ - for(k=0,m=hca->_channelCount;k_rva_volume; - if(f>1){f=1;}else if(f<-1){f=-1;} - ((void (*)(float,void *))modeFunction)(f,fp2); - } - } - } - } - return 0; -} -#endif - -//-------------------------------------------------- -// Decoder utilities +// API/Utilities //-------------------------------------------------- -int clHCA_isOurFile0(const void *data){ - clData d; - clData_constructor(&d, data, 8); - if((clData_CheckBit(&d,32)&0x7F7F7F7F)==0x48434100){/*'HCA\0'*/ - clData_AddBit(&d,32+16); - return clData_CheckBit(&d,16); - } - return -1; +int clHCA_isOurFile(const void *data, unsigned int size) { + clData br; + unsigned int header_size = 0; + + if (!data || size < 0x08) + return -1; + + bitreader_init(&br, data, 8); + if ((bitreader_peek(&br, 32) & HCA_MASK) == 0x48434100) {/*'HCA\0'*/ + bitreader_skip(&br, 32 + 16); + header_size = bitreader_read(&br, 16); + } + + if (header_size == 0) + return -1; + return header_size; } -int clHCA_isOurFile1(const void *data, unsigned int size){ - int minsize; - if (size<8)return -1; - minsize = clHCA_isOurFile0(data); - if (minsize < 0 || (unsigned int)minsize > size)return -1; - if (clHCA_CheckSum(data, minsize, 0))return -1; - return 0; +int clHCA_getInfo(clHCA *hca, clHCA_stInfo *info) { + if (!hca || !info || !hca->is_valid) + return -1; + + info->version = hca->version; + info->headerSize = hca->header_size; + info->samplingRate = hca->sample_rate; + info->channelCount = hca->channels; + info->blockSize = hca->frame_size; + info->blockCount = hca->frame_count; + info->encoderDelay = hca->encoder_delay; + info->encoderPadding = hca->encoder_padding; + info->loopEnabled = hca->loop_flag; + info->loopStartBlock = hca->loop_start_frame; + info->loopEndBlock = hca->loop_end_frame; + info->loopStartDelay = hca->loop_start_delay; + info->loopEndPadding = hca->loop_end_padding; + info->samplesPerBlock = HCA_SAMPLES_PER_FRAME; + info->comment = hca->comment; + info->encryptionEnabled = hca->ciph_type == 56; /* keycode encryption */ + return 0; } -int clHCA_getInfo(clHCA *hca, clHCA_stInfo *info){ - if (!hca->_validFile)return -1; - info->version=hca->_version; - info->dataOffset=hca->_dataOffset; - info->samplingRate=hca->_samplingRate; - info->channelCount=hca->_channelCount; - info->blockSize=hca->_blockSize; - info->blockCount=hca->_blockCount; - info->loopEnabled=hca->_loopFlg; - info->loopStart=hca->_loopStart; - info->loopEnd=hca->_loopEnd; - info->comment=hca->_comm_comment; - return 0; +void clHCA_ReadSamples16(clHCA *hca, signed short *samples) { + const float scale = 32768.0f; + float f; + signed int s; + unsigned int i, j, k; + + for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) { + for (j = 0; j < HCA_SAMPLES_PER_SUBFRAME; j++) { + for (k = 0; k < hca->channels; k++) { + f = hca->channel[k].wave[i][j]; + //f = f * hca->rva_volume; /* rare, won't apply for now */ + if (f > 1) { + f = 1; + } else if (f < -1) { + f = -1; + } + s = (signed int) (f * scale); + if ((unsigned) (s + 0x8000) & 0xFFFF0000) + s = (s >> 31) ^ 0x7FFF; + *samples++ = (signed short) s; + } + } + } } -void clHCA_DecodeSamples16(clHCA *hca,signed short *samples){ - const float scale = 32768.0f; - float f; - signed int s; - int i, j; - unsigned int k, l; - //const float _rva_volume=hca->_rva_volume; - for(i=0;i<8;i++){ - for(j=0;j<0x80;j++){ - for(k=0,l=hca->_channelCount;k_channel[k].wave[i][j]/**_rva_volume*/; - if(f>1){f=1;}else if(f<-1){f=-1;} - s=(signed int)(f*scale); - if ((unsigned)(s+0x8000)&0xFFFF0000)s=(s>>31)^0x7FFF; - *samples++=(signed short)s; - } - } - } -} //-------------------------------------------------- // Allocation and creation //-------------------------------------------------- - -int clHCA_sizeof(){ - return sizeof(clHCA); +static void clHCA_constructor(clHCA *hca) { + if (!hca) + return; + memset(hca, 0, sizeof(*hca)); + hca->is_valid = 0; + hca->comment = 0; } -void clHCA_clear(clHCA *hca,unsigned int ciphKey1,unsigned int ciphKey2){ - clHCA_constructor(hca,ciphKey1,ciphKey2); +static void clHCA_destructor(clHCA *hca) { + if (!hca) + return; + free(hca->comment); + hca->comment = 0; } -void clHCA_done(clHCA *hca) -{ - clHCA_destructor(hca); +int clHCA_sizeof() { + return sizeof(clHCA); } -clHCA * clHCA_new(unsigned int ciphKey1, unsigned int ciphKey2){ - clHCA *hca = (clHCA *) malloc(clHCA_sizeof()); - if (hca){ - clHCA_constructor(hca,ciphKey1,ciphKey2); - } - return hca; +void clHCA_clear(clHCA *hca) { + clHCA_constructor(hca); } -void clHCA_delete(clHCA *hca){ - if (hca){ - clHCA_destructor(hca); - free(hca); - } +void clHCA_done(clHCA *hca) { + clHCA_destructor(hca); +} + +clHCA * clHCA_new() { + clHCA *hca = (clHCA *) malloc(clHCA_sizeof()); + if (hca) { + clHCA_constructor(hca); + } + return hca; +} + +void clHCA_delete(clHCA *hca) { + clHCA_destructor(hca); + free(hca); } //-------------------------------------------------- // ATH //-------------------------------------------------- -static void clATH_Init0(clATH *ath); -static void clATH_Init1(clATH *ath,unsigned int key); - -void clATH_constructor(clATH *ath){ - memset(ath,0,sizeof(*ath)); - clATH_Init0(ath); -} - -static int clATH_Init(clATH *ath,int type,unsigned int key){ - switch(type){ - case 0:clATH_Init0(ath);break; - case 1:clATH_Init1(ath,key);break; - default:return -1; - } - return 0; -} - -static const unsigned char * clATH_GetTable(clATH *ath){ - return ath->_table; -} - -void clATH_Init0(clATH *ath){ - memset(ath->_table,0,sizeof(ath->_table)); -} - -static const unsigned char clATH_list[]={ - 0x78,0x5F,0x56,0x51,0x4E,0x4C,0x4B,0x49,0x48,0x48,0x47,0x46,0x46,0x45,0x45,0x45, - 0x44,0x44,0x44,0x44,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x42,0x42,0x42,0x42,0x42, - 0x42,0x42,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x40,0x40,0x40,0x40, - 0x40,0x40,0x40,0x40,0x40,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, - 0x3F,0x3F,0x3F,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, - 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, - 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, - 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, - 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3F, - 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, - 0x3F,0x3F,0x3F,0x3F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, - 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x41,0x41,0x41,0x41,0x41,0x41,0x41, - 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, - 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, - 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x43,0x43,0x43, - 0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x44,0x44, - 0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x45,0x45,0x45,0x45, - 0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46, - 0x46,0x46,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x48,0x48,0x48,0x48, - 0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4A,0x4A,0x4A,0x4A, - 0x4A,0x4A,0x4A,0x4A,0x4B,0x4B,0x4B,0x4B,0x4B,0x4B,0x4B,0x4C,0x4C,0x4C,0x4C,0x4C, - 0x4C,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4E,0x4E,0x4E,0x4E,0x4E,0x4E,0x4F,0x4F,0x4F, - 0x4F,0x4F,0x4F,0x50,0x50,0x50,0x50,0x50,0x51,0x51,0x51,0x51,0x51,0x52,0x52,0x52, - 0x52,0x52,0x53,0x53,0x53,0x53,0x54,0x54,0x54,0x54,0x54,0x55,0x55,0x55,0x55,0x56, - 0x56,0x56,0x56,0x57,0x57,0x57,0x57,0x57,0x58,0x58,0x58,0x59,0x59,0x59,0x59,0x5A, - 0x5A,0x5A,0x5A,0x5B,0x5B,0x5B,0x5B,0x5C,0x5C,0x5C,0x5D,0x5D,0x5D,0x5D,0x5E,0x5E, - 0x5E,0x5F,0x5F,0x5F,0x60,0x60,0x60,0x61,0x61,0x61,0x61,0x62,0x62,0x62,0x63,0x63, - 0x63,0x64,0x64,0x64,0x65,0x65,0x66,0x66,0x66,0x67,0x67,0x67,0x68,0x68,0x68,0x69, - 0x69,0x6A,0x6A,0x6A,0x6B,0x6B,0x6B,0x6C,0x6C,0x6D,0x6D,0x6D,0x6E,0x6E,0x6F,0x6F, - 0x70,0x70,0x70,0x71,0x71,0x72,0x72,0x73,0x73,0x73,0x74,0x74,0x75,0x75,0x76,0x76, - 0x77,0x77,0x78,0x78,0x78,0x79,0x79,0x7A,0x7A,0x7B,0x7B,0x7C,0x7C,0x7D,0x7D,0x7E, - 0x7E,0x7F,0x7F,0x80,0x80,0x81,0x81,0x82,0x83,0x83,0x84,0x84,0x85,0x85,0x86,0x86, - 0x87,0x88,0x88,0x89,0x89,0x8A,0x8A,0x8B,0x8C,0x8C,0x8D,0x8D,0x8E,0x8F,0x8F,0x90, - 0x90,0x91,0x92,0x92,0x93,0x94,0x94,0x95,0x95,0x96,0x97,0x97,0x98,0x99,0x99,0x9A, - 0x9B,0x9B,0x9C,0x9D,0x9D,0x9E,0x9F,0xA0,0xA0,0xA1,0xA2,0xA2,0xA3,0xA4,0xA5,0xA5, - 0xA6,0xA7,0xA7,0xA8,0xA9,0xAA,0xAA,0xAB,0xAC,0xAD,0xAE,0xAE,0xAF,0xB0,0xB1,0xB1, - 0xB2,0xB3,0xB4,0xB5,0xB6,0xB6,0xB7,0xB8,0xB9,0xBA,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, - 0xC0,0xC1,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xC9,0xCA,0xCB,0xCC,0xCD, - 0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD, - 0xDE,0xDF,0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xED,0xEE, - 0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFF,0xFF, +/* Base ATH (Absolute Threshold of Hearing) curve (for 41856hz). + * May be a slight modification of the standard Painter & Spanias ATH curve formula. */ +static const unsigned char ath_base_curve[656] = { + 0x78,0x5F,0x56,0x51,0x4E,0x4C,0x4B,0x49,0x48,0x48,0x47,0x46,0x46,0x45,0x45,0x45, + 0x44,0x44,0x44,0x44,0x43,0x43,0x43,0x43,0x43,0x43,0x42,0x42,0x42,0x42,0x42,0x42, + 0x42,0x42,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x40,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, + 0x3F,0x3F,0x3F,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D, + 0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, + 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B, + 0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3B,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C,0x3C, + 0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3D,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3E,0x3F, + 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, + 0x3F,0x3F,0x3F,0x3F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, + 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x43,0x43,0x43, + 0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x43,0x44,0x44, + 0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x45,0x45,0x45,0x45, + 0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x46,0x46,0x46,0x46,0x46,0x46,0x46,0x46, + 0x46,0x46,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x47,0x48,0x48,0x48,0x48, + 0x48,0x48,0x48,0x48,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x4A,0x4A,0x4A,0x4A, + 0x4A,0x4A,0x4A,0x4A,0x4B,0x4B,0x4B,0x4B,0x4B,0x4B,0x4B,0x4C,0x4C,0x4C,0x4C,0x4C, + 0x4C,0x4D,0x4D,0x4D,0x4D,0x4D,0x4D,0x4E,0x4E,0x4E,0x4E,0x4E,0x4E,0x4F,0x4F,0x4F, + 0x4F,0x4F,0x4F,0x50,0x50,0x50,0x50,0x50,0x51,0x51,0x51,0x51,0x51,0x52,0x52,0x52, + 0x52,0x52,0x53,0x53,0x53,0x53,0x54,0x54,0x54,0x54,0x54,0x55,0x55,0x55,0x55,0x56, + 0x56,0x56,0x56,0x57,0x57,0x57,0x57,0x57,0x58,0x58,0x58,0x59,0x59,0x59,0x59,0x5A, + 0x5A,0x5A,0x5A,0x5B,0x5B,0x5B,0x5B,0x5C,0x5C,0x5C,0x5D,0x5D,0x5D,0x5D,0x5E,0x5E, + 0x5E,0x5F,0x5F,0x5F,0x60,0x60,0x60,0x61,0x61,0x61,0x61,0x62,0x62,0x62,0x63,0x63, + 0x63,0x64,0x64,0x64,0x65,0x65,0x66,0x66,0x66,0x67,0x67,0x67,0x68,0x68,0x68,0x69, + 0x69,0x6A,0x6A,0x6A,0x6B,0x6B,0x6B,0x6C,0x6C,0x6D,0x6D,0x6D,0x6E,0x6E,0x6F,0x6F, + 0x70,0x70,0x70,0x71,0x71,0x72,0x72,0x73,0x73,0x73,0x74,0x74,0x75,0x75,0x76,0x76, + 0x77,0x77,0x78,0x78,0x78,0x79,0x79,0x7A,0x7A,0x7B,0x7B,0x7C,0x7C,0x7D,0x7D,0x7E, + 0x7E,0x7F,0x7F,0x80,0x80,0x81,0x81,0x82,0x83,0x83,0x84,0x84,0x85,0x85,0x86,0x86, + 0x87,0x88,0x88,0x89,0x89,0x8A,0x8A,0x8B,0x8C,0x8C,0x8D,0x8D,0x8E,0x8F,0x8F,0x90, + 0x90,0x91,0x92,0x92,0x93,0x94,0x94,0x95,0x95,0x96,0x97,0x97,0x98,0x99,0x99,0x9A, + 0x9B,0x9B,0x9C,0x9D,0x9D,0x9E,0x9F,0xA0,0xA0,0xA1,0xA2,0xA2,0xA3,0xA4,0xA5,0xA5, + 0xA6,0xA7,0xA7,0xA8,0xA9,0xAA,0xAA,0xAB,0xAC,0xAD,0xAE,0xAE,0xAF,0xB0,0xB1,0xB1, + 0xB2,0xB3,0xB4,0xB5,0xB6,0xB6,0xB7,0xB8,0xB9,0xBA,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xC9,0xCA,0xCB,0xCC,0xCD, + 0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD, + 0xDE,0xDF,0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xED,0xEE, + 0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFF,0xFF, }; -void clATH_Init1(clATH *ath, unsigned int key){ - unsigned int i, v; - unsigned char *_table=ath->_table; - for(i=0,v=0;i<0x80;i++,v+=key){ - unsigned int index=v>>13; - if(index>=0x28E){ - memset(&_table[i],0xFF,0x80-i); - break; - } - _table[i]=clATH_list[index]; - } +static void ath_init0(unsigned char *ath_curve) { + /* disable curve */ + memset(ath_curve, 0, sizeof(ath_curve[0]) * HCA_SAMPLES_PER_SUBFRAME); +} + +static void ath_init1(unsigned char *ath_curve, unsigned int sample_rate) { + unsigned int i, index; + unsigned int acc = 0; + + /* scale ATH curve depending on frequency */ + for (i = 0; i < HCA_SAMPLES_PER_SUBFRAME; i++) { + acc += sample_rate; + index = acc >> 13; + + if (index >= 654) { + memset(ath_curve+i, 0xFF, sizeof(ath_curve[0]) * (HCA_SAMPLES_PER_SUBFRAME - i)); + break; + } + ath_curve[i] = ath_base_curve[index]; + } +} + +static int ath_init(unsigned char *ath_curve, int type, unsigned int sample_rate) { + switch (type) { + case 0: + ath_init0(ath_curve); + break; + case 1: + ath_init1(ath_curve, sample_rate); + break; + default: + return -1; + } + return 0; +} + + +//-------------------------------------------------- +// Encryption +//-------------------------------------------------- +static void cipher_decrypt(unsigned char *cipher_table, unsigned char *data, int size) { + unsigned int i; + + for (i = 0; i < size; i++) { + data[i] = cipher_table[data[i]]; + } +} + +static void cipher_init0(unsigned char *cipher_table) { + unsigned int i; + + /* no encryption */ + for (i = 0; i < 256; i++) { + cipher_table[i] = i; + } +} + +static void cipher_init1(unsigned char *cipher_table) { + const int mul = 13; + const int add = 11; + unsigned int i, v = 0; + + /* keyless encryption (unused?) */ + for (i = 1; i < 256 - 1; i++) { + v = (v * mul + add) & 0xFF; + if (v == 0 || v == 0xFF) + v = (v * mul + add) & 0xFF; + cipher_table[i] = v; + } + cipher_table[0] = 0; + cipher_table[0xFF] = 0xFF; +} + +static void cipher_init56_create_table(unsigned char *r, unsigned char key) { + const int mul = ((key & 1) << 3) | 5; + const int add = (key & 0xE) | 1; + unsigned int i; + + key >>= 4; + for (i = 0; i < 16; i++) { + key = (key * mul + add) & 0xF; + r[i] = key; + } +} + +static void cipher_init56(unsigned char *cipher_table, unsigned long long keycode) { + unsigned char kc[8]; + unsigned char seed[16]; + unsigned char base[256], base_r[16], base_c[16]; + unsigned int r, c; + + /* 56bit keycode encryption (given as a uint64_t number, but upper 8b aren't used) */ + + /* keycode = keycode - 1 */ + if (keycode != 0) + keycode--; + + /* init keycode table */ + for (r = 0; r < (8-1); r++) { + kc[r] = keycode & 0xFF; + keycode = keycode >> 8; + } + + /* init seed table */ + seed[0x00] = kc[1]; + seed[0x01] = kc[1] ^ kc[6]; + seed[0x02] = kc[2] ^ kc[3]; + seed[0x03] = kc[2]; + seed[0x04] = kc[2] ^ kc[1]; + seed[0x05] = kc[3] ^ kc[4]; + seed[0x06] = kc[3]; + seed[0x07] = kc[3] ^ kc[2]; + seed[0x08] = kc[4] ^ kc[5]; + seed[0x09] = kc[4]; + seed[0x0A] = kc[4] ^ kc[3]; + seed[0x0B] = kc[5] ^ kc[6]; + seed[0x0C] = kc[5]; + seed[0x0D] = kc[5] ^ kc[4]; + seed[0x0E] = kc[6] ^ kc[1]; + seed[0x0F] = kc[6]; + + /* init base table */ + cipher_init56_create_table(base_r, kc[0]); + for (r = 0; r < 16; r++) { + unsigned char nb; + cipher_init56_create_table(base_c, seed[r]); + nb = base_r[r] << 4; + for (c = 0; c < 16; c++) { + base[r*16 + c] = nb | base_c[c]; /* combine nibbles */ + } + } + + /* final shuffle table */ + { + unsigned int i; + unsigned int x = 0; + unsigned int pos = 1; + + for (i = 0; i < 256; i++) { + x = (x + 17) & 0xFF; + if (base[x] != 0 && base[x] != 0xFF) + cipher_table[pos++] = base[x]; + } + cipher_table[0] = 0; + cipher_table[0xFF] = 0xFF; + } +} + +static int cipher_init(unsigned char *cipher_table, int type, unsigned long long keycode) { + if (!(keycode)) + type = 0; + + switch (type) { + case 0: + cipher_init0(cipher_table); + break; + case 1: + cipher_init1(cipher_table); + break; + case 56: + cipher_init56(cipher_table, keycode); + break; + default: + return -1; + } + return 0; } //-------------------------------------------------- -// 暗号化テーブル +// Parse //-------------------------------------------------- -static void clCipher_Init0(clCipher *cipher); -static void clCipher_Init1(clCipher *cipher); -static void clCipher_Init56(clCipher *cipher,unsigned int key1,unsigned int key2); - -void clCipher_constructor(clCipher *cipher){ - memset(cipher,0,sizeof(*cipher)); - clCipher_Init0(cipher); +static unsigned int header_ceil2(unsigned int a, unsigned int b) { + return (b > 0) ? (a / b + ((a % b) ? 1 : 0)) : 0; } -static int clCipher_Init(clCipher *cipher,int type,unsigned int key1,unsigned int key2){ - if(!(key1|key2))type=0; - switch(type){ - case 0:clCipher_Init0(cipher);break; - case 1:clCipher_Init1(cipher);break; - case 56:clCipher_Init56(cipher,key1,key2);break; - default:return -1; - } - return 0; +int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) { + clData br; + + if (!hca || !data ) + return -1; + + hca->is_valid = 0; + + if (size < 0x08) + return -1; + + bitreader_init(&br, data, size); + + /* read header chunks */ + + /* HCA base header */ + if ((bitreader_peek(&br, 32) & HCA_MASK) == 0x48434100) { /* "HCA\0" */ + bitreader_skip(&br, 32); + hca->version = bitreader_read(&br, 16); + hca->header_size = bitreader_read(&br, 16); + +#if 0 // play unknown versions anyway (confirmed to exist: v1.1/v1.2/v1.3/v2.0) + if (hca->version != 0x0101 && + hca->version != 0x0102 && + hca->version != 0x0103 && + hca->version != 0x0200) + return -1; +#endif + if (size < hca->header_size) + return -1; + + if (crc16_checksum(data,hca->header_size)) + return -1; + + size -= 0x08; + } + else { + return -1; + } + + /* format info */ + if (size >= 0x10 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x666D7400) { /* "fmt\0" */ + bitreader_skip(&br, 32); + hca->channels = bitreader_read(&br, 8); + hca->sample_rate = bitreader_read(&br, 24); + hca->frame_count = bitreader_read(&br, 32); + hca->encoder_delay = bitreader_read(&br, 16); + hca->encoder_padding = bitreader_read(&br, 16); + + if (!(hca->channels >= 1 && hca->channels <= HCA_MAX_CHANNELS)) + return -1; + + if (hca->frame_count == 0) + return -1; + + if (!(hca->sample_rate >= 1 && hca->sample_rate <= 0x7FFFFF)) /* encoder max seems 48000 */ + return -1; + + size -= 0x10; + } + else { + return -1; + } + + /* compression (v2.0) or decode (v1.x) info */ + if (size >= 0x10 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x636F6D70) { /* "comp" */ + bitreader_skip(&br, 32); + hca->frame_size = bitreader_read(&br, 16); + hca->min_resolution = bitreader_read(&br, 8); + hca->max_resolution = bitreader_read(&br, 8); + hca->track_count = bitreader_read(&br, 8); + hca->channel_config = bitreader_read(&br, 8); + hca->total_band_count = bitreader_read(&br, 8); + hca->base_band_count = bitreader_read(&br, 8); + hca->stereo_band_count = bitreader_read(&br, 8); + hca->bands_per_hfr_group = bitreader_read(&br, 8); + hca->reserved1 = bitreader_read(&br, 8); + hca->reserved2 = bitreader_read(&br, 8); + + size -= 0x10; + } + else if (size >= 0x0c && (bitreader_peek(&br, 32) & HCA_MASK) == 0x64656300) { /* "dec\0" */ + bitreader_skip(&br, 32); + hca->frame_size = bitreader_read(&br, 16); + hca->min_resolution = bitreader_read(&br, 8); + hca->max_resolution = bitreader_read(&br, 8); + hca->total_band_count = bitreader_read(&br, 8) + 1; + hca->base_band_count = bitreader_read(&br, 8) + 1; + hca->track_count = bitreader_read(&br, 4); + hca->channel_config = bitreader_read(&br, 4); + hca->stereo_type = bitreader_read(&br, 8); + + if (hca->stereo_type == 0) + hca->base_band_count = hca->total_band_count; + hca->stereo_band_count = hca->total_band_count - hca->base_band_count; + hca->bands_per_hfr_group = 0; + + size -= 0x0c; + } + else { + return -1; + } + + /* VBR (variable bit rate) info */ + if (size >= 0x08 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x76627200) { /* "vbr\0" */ + bitreader_skip(&br, 32); + hca->vbr_max_frame_size = bitreader_read(&br, 16); + hca->vbr_noise_Level = bitreader_read(&br, 16); + + if (!(hca->frame_size == 0 && hca->vbr_max_frame_size > 8 && hca->vbr_max_frame_size <= 0x1FF)) + return -1; + + size -= 0x08; + } + else { + /* removed in v2.0, probably unused in v1.x */ + hca->vbr_max_frame_size = 0; + hca->vbr_noise_Level = 0; + } + + /* ATH (Absolute Threshold of Hearing) info */ + if (size >= 0x06 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x61746800) { /* "ath\0" */ + bitreader_skip(&br, 32); + hca->ath_type = bitreader_read(&br, 16); + } + else { + /* removed in v2.0, default in v1.x (maybe only used in v1.1, as v1.2/v1.3 set ath_type = 0) */ + hca->ath_type = (hca->version < 0x200) ? 1 : 0; + } + + /* loop info */ + if (size >= 0x10 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x6C6F6F70) { /* "loop" */ + bitreader_skip(&br, 32); + hca->loop_start_frame = bitreader_read(&br, 32); + hca->loop_end_frame = bitreader_read(&br, 32); + hca->loop_start_delay = bitreader_read(&br, 16); + hca->loop_end_padding = bitreader_read(&br, 16); + + hca->loop_flag = 1; + + if (!(hca->loop_start_frame >= 0 && hca->loop_start_frame <= hca->loop_end_frame + && hca->loop_end_frame < hca->frame_count)) + return -1; + + size -= 0x10; + } + else { + hca->loop_start_frame = 0; + hca->loop_end_frame = 0; + hca->loop_start_delay = 0; + hca->loop_end_padding = 0; + + hca->loop_flag = 0; + } + + /* cipher/encryption info */ + if (size >= 0x06 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x63697068) { /* "ciph" */ + bitreader_skip(&br, 32); + hca->ciph_type = bitreader_read(&br, 16); + + if (!(hca->ciph_type == 0 || hca->ciph_type == 1 || hca->ciph_type == 56)) + return -1; + + size -= 0x06; + } + else { + hca->ciph_type = 0; + } + + /* RVA (relative volume adjustment) info */ + if (size >= 0x08 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x72766100) { /* "rva\0" */ + union { + unsigned int i; + float f; + } rva_volume_cast; + bitreader_skip(&br, 32); + rva_volume_cast.i = bitreader_read(&br, 32); + hca->rva_volume = rva_volume_cast.f; + + size -= 0x08; + } else { + hca->rva_volume = 1; + } + + /* comment */ + if (size >= 0x05 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x636F6D6D) {/* "comm" */ + unsigned int i; + char *temp; + bitreader_skip(&br, 32); + hca->comment_len = bitreader_read(&br, 8); + + if (hca->comment_len > size) + return -1; + + temp = realloc(hca->comment, hca->comment_len + 1); + if (!temp) + return -1; + hca->comment = temp; + for (i = 0; i < hca->comment_len; ++i) + hca->comment[i] = bitreader_read(&br, 8); + hca->comment[i] = '\0'; /* should be null terminated but make sure */ + + size -= 0x05 + hca->comment_len; + } + else { + hca->comment_len = 0; + hca->comment = NULL; + } + + /* padding info */ + if (size >= 0x04 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x70616400) { /* "pad\0" */ + size -= (size - 0x02); /* fills up to header_size, sans checksum */ + } + + /* should be fully read, but allow data buffer may be bigger than header_size */ + //if (size != 0x02) + // return -1; + + + /* extra validations */ + if (!(hca->frame_size >= 0x08 && hca->frame_size <= 0xFFFF)) /* actual max seems 0x155*channels */ + return -1; /* theoretically can be 0 if VBR (not seen) */ + + if (!(hca->min_resolution == 1 && hca->max_resolution == 15)) + return -1; + + + /* inits state */ + if (hca->track_count == 0) + hca->track_count = 1; /* default to avoid division by zero */ + + hca->hfr_group_count = header_ceil2( + hca->total_band_count - hca->base_band_count - hca->stereo_band_count, + hca->bands_per_hfr_group); + + if (ath_init(hca->ath_curve, hca->ath_type, hca->sample_rate) < 0) + return -1; + if (cipher_init(hca->cipher_table, hca->ciph_type, hca->keycode) < 0) + return -1; + + + /* init channels */ + { + int channel_types[HCA_MAX_CHANNELS] = {0}; + unsigned int i, channels_per_track; + + channels_per_track = hca->channels / hca->track_count; + if (hca->stereo_band_count && channels_per_track > 1) { + int *ct = channel_types; + for (i = 0; i < hca->track_count; i++, ct += channels_per_track) { + switch (channels_per_track) { + case 2: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + break; + case 3: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + break; + case 4: + ct[0] = STEREO_PRIMARY; + ct[1] = 2; + if (hca->channel_config == 0) { + ct[2] = STEREO_PRIMARY; + ct[3] = STEREO_SECONDARY; + } else { + ct[2] = DISCRETE; + ct[3] = DISCRETE; + } + break; + case 5: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + if (hca->channel_config <= 2) { + ct[3] = STEREO_PRIMARY; + ct[4] = STEREO_SECONDARY; + } else { + ct[3] = DISCRETE; + ct[4] = DISCRETE; + } + break; + case 6: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + ct[3] = DISCRETE; + ct[4] = STEREO_PRIMARY; + ct[5] = STEREO_SECONDARY; + break; + case 7: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + ct[3] = DISCRETE; + ct[4] = STEREO_PRIMARY; + ct[5] = STEREO_SECONDARY; + ct[6] = DISCRETE; + break; + case 8: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + ct[3] = DISCRETE; + ct[4] = STEREO_PRIMARY; + ct[5] = STEREO_SECONDARY; + ct[6] = STEREO_PRIMARY; + ct[7] = STEREO_SECONDARY; + break; + } + } + } + + memset(hca->channel, 0, sizeof(hca->channel)); + for (i = 0; i < hca->channels; i++) { + hca->channel[i].type = channel_types[i]; + hca->channel[i].coded_scalefactor_count = (channel_types[i] != 2) ? + hca->base_band_count + hca->stereo_band_count : + hca->base_band_count; + hca->channel[i].hfr_scales = &hca->channel[i].scalefactors[hca->base_band_count + hca->stereo_band_count]; + } + } + + + /* clHCA is correctly initialized and decoder state reset + * (keycode is not changed between calls) */ + hca->is_valid = 1; + + return 0; } -static void clCipher_Mask(clCipher *cipher,void *data,int size){ - unsigned char *_table=cipher->_table; - unsigned char *d; - for(d=(unsigned char *)data;size>0;d++,size--)*d=_table[*d]; +void clHCA_SetKey(clHCA *hca, unsigned long long keycode) { + if (!hca) + return; + hca->keycode = keycode; + + /* May be called even if clHCA is not valid (header not parsed), as the + * key will be used during DecodeHeader ciph init. If header was already + * parsed reinitializes the decryption table using the new key. */ + if (hca->is_valid) { + /* ignore error since it can't really fail */ + cipher_init(hca->cipher_table, hca->ciph_type, hca->keycode); + } } -void clCipher_Init0(clCipher *cipher){ - unsigned char *_table=cipher->_table; - int i; - for(i=0;i<0x100;i++)_table[i]=i; +int clHCA_TestBlock(clHCA *hca, void *data, unsigned int size) { + const float scale = 32768.0f; + unsigned int ch, sf, s; + int status; + int clips = 0, blanks = 0; + float fsample; + signed int psample; + + /* return if decode fails (happens sometimes with wrong keys) */ + status = clHCA_DecodeBlock(hca, data, size); + if (status < 0) + return -1; + + /* check decode results */ + for (ch = 0; ch < hca->channels; ch++) { + for (sf = 0; sf < HCA_SUBFRAMES_PER_FRAME; sf++) { + for (s = 0; s < HCA_SAMPLES_PER_SUBFRAME; s++) { + fsample = hca->channel[ch].wave[sf][s]; + psample = (signed int) (fsample * scale); + if (fsample > 1.0f || fsample < -1.0f) + clips++; + else if (psample == 0 || psample == -1) + blanks++; + } + } + } + + /* the more clips the less likely block was correctly decrypted */ + if (clips > 0) + return clips; + /* if block is silent result is not useful */ + if (blanks == hca->channels * HCA_SUBFRAMES_PER_FRAME * HCA_SAMPLES_PER_SUBFRAME) + return 0; + + /* block may be correct (but wrong keys can get this too) */ + return 1; } -void clCipher_Init1(clCipher *cipher){ - unsigned char *_table=cipher->_table; - int i, v; - for(i=1,v=0;i<0xFF;i++){ - v=(v*13+11)&0xFF; - if(v==0||v==0xFF)v=(v*13+11)&0xFF; - _table[i]=v; - } - _table[0]=0; - _table[0xFF]=0xFF; + +#if 0 +// it'd seem like resetting IMDCT (others get overwritten) would matter when restarting the +// stream from 0, but doesn't seem any different, maybe because the first frame acts as setup/empty +void clHCA_DecodeReset(clHCA * hca) { + unsigned int i; + + if (!hca || !hca->is_valid) + return; + + for (i = 0; i < hca->channels; i++) { + stChannel *ch = &hca->channel[i]; + + memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES_PER_FRAME); + memset(ch->scalefactors, 0, sizeof(ch->scalefactors[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->resolution, 0, sizeof(ch->resolution[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->gain, 0, sizeof(ch->gain[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->spectra, 0, sizeof(ch->spectra[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->temp, 0, sizeof(ch->temp[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->dct, 0, sizeof(ch->dct[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->imdct_previous, 0, sizeof(ch->imdct_previous[0]) * HCA_SAMPLES_PER_SUBFRAME); + memset(ch->wave, 0, sizeof(ch->wave[0][0]) * HCA_SUBFRAMES_PER_FRAME * HCA_SUBFRAMES_PER_FRAME); + } } +#endif -static void clCipher_Init56_CreateTable(unsigned char *r,unsigned char key); +//-------------------------------------------------- +// Decode +//-------------------------------------------------- +static int decode1_unpack_channel(stChannel *ch, clData *br, + unsigned int hfr_group_count, unsigned int packed_noise_level, const unsigned char *ath_curve); -void clCipher_Init56(clCipher *cipher,unsigned int key1,unsigned int key2){ - unsigned char *_table=cipher->_table; +static void decode2_dequantize_coefficients(stChannel *ch, clData *br); - // テーブル1を生成 - unsigned char t1[8]; - unsigned char t2[0x10]; - unsigned char t3[0x100],t31[0x10],t32[0x10],*t; +static void decode3_reconstruct_high_frequency(stChannel *ch, + unsigned int hfr_group_count, unsigned int bands_per_hfr_group, + unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count); - int i, j, v; +static void decode4_apply_intensity_stereo(stChannel *ch, int subframe, + unsigned int usable_band_count, unsigned int base_band_count, unsigned int stereo_band_count); - if(!key1)key2--; - key1--; - for(i=0;i<7;i++){ - t1[i]=key1; - key1=(key1>>8)|(key2<<24); - key2>>=8; - } +static void decoder5_run_imdct(stChannel *ch, int subframe); - // テーブル2 - t2[0x00]=t1[1]; t2[0x01]=t1[1]^t1[6]; - t2[0x02]=t1[2]^t1[3]; t2[0x03]=t1[2]; - t2[0x04]=t1[2]^t1[1]; t2[0x05]=t1[3]^t1[4]; - t2[0x06]=t1[3]; t2[0x07]=t1[3]^t1[2]; - t2[0x08]=t1[4]^t1[5]; t2[0x09]=t1[4]; - t2[0x0A]=t1[4]^t1[3]; t2[0x0B]=t1[5]^t1[6]; - t2[0x0C]=t1[5]; t2[0x0D]=t1[5]^t1[4]; - t2[0x0E]=t1[6]^t1[1]; t2[0x0F]=t1[6]; - // テーブル3 - t=t3; - clCipher_Init56_CreateTable(t31,t1[0]); - for(i=0;i<0x10;i++){ - unsigned char v; - clCipher_Init56_CreateTable(t32,t2[i]); - v=t31[i]<<4; - for(j=0;j<0x10;j++){ - *(t++)=v|t32[j]; - } - } +int clHCA_DecodeBlock(clHCA *hca, void *data, unsigned int size) { + clData br; + unsigned short sync; + unsigned int subframe, ch; - // CIPHテーブル - t=&_table[1]; - for(i=0,v=0;i<0x100;i++){ - unsigned char a; - v=(v+0x11)&0xFF; - a=t3[v]; - if(a!=0&&a!=0xFF)*(t++)=a; - } - _table[0]=0; - _table[0xFF]=0xFF; + if (!data || !hca || !hca->is_valid) + return -1; + if (size < hca->frame_size) + return -1; -} + bitreader_init(&br, data, hca->frame_size); -void clCipher_Init56_CreateTable(unsigned char *r,unsigned char key){ - int mul=((key&1)<<3)|5; - int add=(key&0xE)|1; - int i; - key>>=4; - for(i=0;i<0x10;i++){ - key=(key*mul+add)&0xF; - *(r++)=key; - } + /* test sync (not encrypted) */ + sync = bitreader_read(&br, 16); + if (sync != 0xFFFF) + return -1; + + if (crc16_checksum(data, hca->frame_size)) + return -1; + + cipher_decrypt(hca->cipher_table, data, hca->frame_size); + + + /* unpack frame values */ + { + unsigned int frame_acceptable_noise_level = bitreader_read(&br, 9); + unsigned int frame_evaluation_boundary = bitreader_read(&br, 7); + unsigned int packed_noise_level = (frame_acceptable_noise_level << 8) - frame_evaluation_boundary; + + for (ch = 0; ch < hca->channels; ch++) { + int unpack = decode1_unpack_channel(&hca->channel[ch], &br, + hca->hfr_group_count, packed_noise_level, hca->ath_curve); + if (unpack < 0) + return -1; + } + } + + for (subframe = 0; subframe < HCA_SUBFRAMES_PER_FRAME; subframe++) { + + /* unpack channel data and get dequantized spectra */ + for (ch = 0; ch < hca->channels; ch++){ + decode2_dequantize_coefficients(&hca->channel[ch], &br); + } + + /* restore missing bands from spectra 1 */ + for (ch = 0; ch < hca->channels; ch++) { + decode3_reconstruct_high_frequency(&hca->channel[ch], + hca->hfr_group_count, hca->bands_per_hfr_group, + hca->stereo_band_count, hca->base_band_count, hca->total_band_count); + } + + /* restore missing bands from spectra 2 */ + for (ch = 0; ch < hca->channels - 1; ch++) { + decode4_apply_intensity_stereo(&hca->channel[ch], subframe, + hca->total_band_count, hca->base_band_count, hca->stereo_band_count); + } + + /* apply imdct */ + for (ch = 0; ch < hca->channels; ch++) { + decoder5_run_imdct(&hca->channel[ch], subframe); + } + } + + /* should read all frame sans checksum at most */ + if (br.bit > br.size - 16) { + return -1; + } + + return 0; } //-------------------------------------------------- -// デコード +// Decode 1st step //-------------------------------------------------- -static void stChannel_Decode1(stChannel *ch,clData *data,unsigned int a,int b,const unsigned char *ath); -static void stChannel_Decode2(stChannel *ch,clData *data); -static void stChannel_Decode3(stChannel *ch,unsigned int a,unsigned int b,unsigned int c,unsigned int d); -static void stChannel_Decode4(stChannel *ch,int index,unsigned int a,unsigned int b,unsigned int c); -static void stChannel_Decode5(stChannel *ch,int index); - -int clHCA_Decode(clHCA *hca,void *data,unsigned int size,unsigned int address){ - - // チェック - if(!(data))return -1; - - // ヘッダ - if(address==0){ - char r[0x10]; - unsigned int i, b; - clData d; - - hca->_validFile = 0; - - clData_constructor(&d, data, size); - - // サイズチェック - if(size_version=clData_GetBit(&d,16); - hca->_dataOffset=clData_GetBit(&d,16); - //if(!(_version<=0x200&&_version>0x101))return -1; // バージョンチェック(無効) - if(size_dataOffset)return -1; - //if(clHCA_CheckSum(hca,_dataOffset,0))return -1; // ヘッダの破損チェック(ヘッダ改変を有効にするため破損チェック無効) - size-=sizeof(stHeader); - }else{ - return -1; - } - - // fmt - if(size>=sizeof(stFormat) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x666D7400){/*'fmt\0'*/ - clData_AddBit(&d,32); - hca->_channelCount=clData_GetBit(&d,8); - hca->_samplingRate=clData_GetBit(&d,24); - hca->_blockCount=clData_GetBit(&d,32); - hca->_fmt_r01=clData_GetBit(&d,16); - hca->_fmt_r02=clData_GetBit(&d,16); - if(!(hca->_channelCount>=1&&hca->_channelCount<=16))return -1; - if(!(hca->_samplingRate>=1&&hca->_samplingRate<=0x7FFFFF))return -1; - size-=sizeof(stFormat); - }else{ - return -1; - } - - // comp - if(size>=sizeof(stCompress) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x636F6D70){/*'comp'*/ - clData_AddBit(&d,32); - hca->_blockSize=clData_GetBit(&d,16); - hca->_comp_r01=clData_GetBit(&d,8); - hca->_comp_r02=clData_GetBit(&d,8); - hca->_comp_r03=clData_GetBit(&d,8); - hca->_comp_r04=clData_GetBit(&d,8); - hca->_comp_r05=clData_GetBit(&d,8); - hca->_comp_r06=clData_GetBit(&d,8); - hca->_comp_r07=clData_GetBit(&d,8); - hca->_comp_r08=clData_GetBit(&d,8); - clData_AddBit(&d,8+8); - if(!((hca->_blockSize>=8&&hca->_blockSize<=0xFFFF)||(hca->_blockSize==0)))return -1; - if(!(/*hca->_comp_r01>=0&&*/hca->_comp_r01<=hca->_comp_r02&&hca->_comp_r02<=0x1F))return -1; - size-=sizeof(stCompress); - } - - // dec - else if(size>=sizeof(stDecode) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x64656300){/*'dec\0'*/ - unsigned char count1,count2,enableCount2; - clData_AddBit(&d,32); - hca->_blockSize=clData_GetBit(&d,16); - hca->_comp_r01=clData_GetBit(&d,8); - hca->_comp_r02=clData_GetBit(&d,8); - count1=clData_GetBit(&d,8); - count2=clData_GetBit(&d,8); - hca->_comp_r03=clData_GetBit(&d,4); - hca->_comp_r04=clData_GetBit(&d,4); - enableCount2=clData_GetBit(&d,8); - hca->_comp_r05=count1+1; - hca->_comp_r06=((enableCount2)?count2:count1)+1; - hca->_comp_r07=hca->_comp_r05-hca->_comp_r06; - hca->_comp_r08=0; - if(!((hca->_blockSize>=8&&hca->_blockSize<=0xFFFF)||(hca->_blockSize==0)))return -1; - if(!(/*hca->_comp_r01>=0&&*/hca->_comp_r01<=hca->_comp_r02&&hca->_comp_r02<=0x1F))return -1; - if(!hca->_comp_r03)hca->_comp_r03=1; - size-=sizeof(stDecode); - }else{ - return -1; - } - - // vbr - if(size>=sizeof(stVBR) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x76627200){/*'vbr\0'*/ - clData_AddBit(&d,32); - hca->_vbr_r01=clData_GetBit(&d,16); - hca->_vbr_r02=clData_GetBit(&d,16); - if(!(hca->_blockSize==0&&/*hca->_vbr_r01>=0&&*/hca->_vbr_r01<=0x1FF))return -1; - }else{ - hca->_vbr_r01=0; - hca->_vbr_r02=0; - } - - // ath - if(size>=6 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x61746800){/*'ath\0'*/ - clData_AddBit(&d,32); - hca->_ath_type=clData_GetBit(&d,16); - }else{ - hca->_ath_type=(hca->_version<0x200)?1:0;//v1.3ではデフォルト値が1になってたが、v2.0からATHテーブルが廃止されてるみたいなので0に - } - - // loop - if(size>=sizeof(stLoop) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x6C6F6F70){/*'loop'*/ - clData_AddBit(&d,32); - hca->_loopStart=clData_GetBit(&d,32); - hca->_loopEnd=clData_GetBit(&d,32); - hca->_loop_r01=clData_GetBit(&d,16); - hca->_loop_r02=clData_GetBit(&d,16); - hca->_loopFlg=1; - if(!(/*hca->_loopStart>=0&&*/hca->_loopStart<=hca->_loopEnd&&hca->_loopEnd_blockCount))return -1; - size-=sizeof(stLoop); - }else{ - hca->_loopStart=0; - hca->_loopEnd=0; - hca->_loop_r01=0; - hca->_loop_r02=0x400; - hca->_loopFlg=0; - } - - // ciph - if(size>=6 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x63697068){/*'ciph'*/ - clData_AddBit(&d,32); - hca->_ciph_type=clData_GetBit(&d,16); - if(!(hca->_ciph_type==0||hca->_ciph_type==1||hca->_ciph_type==0x38))return -1; - size-=6; - }else{ - hca->_ciph_type=0; - } - - // rva - if(size>=sizeof(stRVA) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x72766100){/*'rva\0'*/ - union { unsigned int i; float f; } v; - clData_AddBit(&d,32); - v.i=clData_GetBit(&d,32); - hca->_rva_volume=v.f; - size-=sizeof(stRVA); - }else{ - hca->_rva_volume=1; - } - - // comm - if(size>=5 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x636F6D6D){/*'comm'*/ - void * newmem; - unsigned int i; - clData_AddBit(&d,32); - hca->_comm_len=clData_GetBit(&d,8); - if(hca->_comm_len>size)return -1; - newmem=realloc(hca->_comm_comment,hca->_comm_len+1); - if(!newmem)return -1; - hca->_comm_comment=(char *)newmem; - for(i=0;i_comm_len;++i) - ((char *)newmem)[i]=clData_GetBit(&d,8); - ((char *)newmem)[i]='\0'; - size-=5+hca->_comm_len; - }else{ - hca->_comm_len=0; - hca->_comm_comment=NULL; - } - - // 初期化 - if(clATH_Init(&hca->_ath,hca->_ath_type,hca->_samplingRate) < 0)return -1; - if(clCipher_Init(&hca->_cipher,hca->_ciph_type,hca->_ciph_key1,hca->_ciph_key2) < 0)return -1; - - // 値チェック(ヘッダの改変ミスによるエラーを回避するため) - if(!hca->_comp_r03)hca->_comp_r03=1;//0での除算を防ぐため - - // デコード準備 - memset(hca->_channel,0,sizeof(hca->_channel)); - - if(!(hca->_comp_r01==1&&hca->_comp_r02==15))return -1; - - hca->_comp_r09=ceil2(hca->_comp_r05-(hca->_comp_r06+hca->_comp_r07),hca->_comp_r08); - - memset(r,0,sizeof(r)); - b=hca->_channelCount/hca->_comp_r03; - if(hca->_comp_r07&&b>1){ - char *c=r; - for(i=0;i_comp_r03;i++,c+=b){ - switch(b){ - case 2:c[0]=1;c[1]=2;break; - case 3:c[0]=1;c[1]=2;break; - case 4:c[0]=1;c[1]=2;if(hca->_comp_r04==0){c[2]=1;c[3]=2;}break; - case 5:c[0]=1;c[1]=2;if(hca->_comp_r04<=2){c[3]=1;c[4]=2;}break; - case 6:c[0]=1;c[1]=2;c[4]=1;c[5]=2;break; - case 7:c[0]=1;c[1]=2;c[4]=1;c[5]=2;break; - case 8:c[0]=1;c[1]=2;c[4]=1;c[5]=2;c[6]=1;c[7]=2;break; - } - } - } - for(i=0;i_channelCount;i++){ - hca->_channel[i].type=r[i]; - hca->_channel[i].value3=&hca->_channel[i].value[hca->_comp_r06+hca->_comp_r07]; - hca->_channel[i].count=hca->_comp_r06+((r[i]!=2)?hca->_comp_r07:0); - } - - hca->_validFile = 1; - } - - // ブロックデータ - else if(address>=hca->_dataOffset){ - clData d; - int magic; - unsigned int i, j; - if(!hca->_validFile)return -1; - if(size_blockSize)return -1; - if(clHCA_CheckSum(data,hca->_blockSize,0))return -1; - clCipher_Mask(&hca->_cipher,data,hca->_blockSize); - clData_constructor(&d,data,hca->_blockSize); - magic=clData_GetBit(&d,16);//0xFFFF固定 - if(magic==0xFFFF){ - const unsigned int _channelCount=hca->_channelCount; - int a=(clData_GetBit(&d,9)<<8)-clData_GetBit(&d,7); - for(i=0;i<_channelCount;i++)stChannel_Decode1(&hca->_channel[i],&d,hca->_comp_r09,a,clATH_GetTable(&hca->_ath)); - for(i=0;i<8;i++){ - for(j=0;j<_channelCount;j++)stChannel_Decode2(&hca->_channel[j],&d); - for(j=0;j<_channelCount;j++)stChannel_Decode3(&hca->_channel[j],hca->_comp_r09,hca->_comp_r08,hca->_comp_r07+hca->_comp_r06,hca->_comp_r05); - for(j=0;j<_channelCount-1;j++)stChannel_Decode4(&hca->_channel[j],i,hca->_comp_r05-hca->_comp_r06,hca->_comp_r06,hca->_comp_r07); - for(j=0;j<_channelCount;j++)stChannel_Decode5(&hca->_channel[j],i); - } - } - } - - return 0; -} - -//-------------------------------------------------- -// デコード第一段階 -// ベースデータの読み込み -//-------------------------------------------------- -static const unsigned char stChannel_Decode1_scalelist[]={ - // v2.0 - 0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0D,0x0D, - 0x0D,0x0D,0x0D,0x0D,0x0C,0x0C,0x0C,0x0C, - 0x0C,0x0C,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, - 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x09, - 0x09,0x09,0x09,0x09,0x09,0x08,0x08,0x08, - 0x08,0x08,0x08,0x07,0x06,0x06,0x05,0x04, - 0x04,0x04,0x03,0x03,0x03,0x02,0x02,0x02, - 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - // v1.3 - //0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0D,0x0D, - //0x0D,0x0D,0x0D,0x0D,0x0C,0x0C,0x0C,0x0C, - //0x0C,0x0C,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, - //0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x09, - //0x09,0x09,0x09,0x09,0x09,0x08,0x08,0x08, - //0x08,0x08,0x08,0x07,0x06,0x06,0x05,0x04, - //0x04,0x04,0x03,0x03,0x03,0x02,0x02,0x02, - //0x02,0x01,0x01,0x01,0x01,0x01,0x01,0x01, -}; -static const unsigned int stChannel_Decode1_valueInt[]={ - 0x342A8D26,0x34633F89,0x3497657D,0x34C9B9BE,0x35066491,0x353311C4,0x356E9910,0x359EF532, - 0x35D3CCF1,0x360D1ADF,0x363C034A,0x367A83B3,0x36A6E595,0x36DE60F5,0x371426FF,0x3745672A, - 0x37838359,0x37AF3B79,0x37E97C38,0x381B8D3A,0x384F4319,0x388A14D5,0x38B7FBF0,0x38F5257D, - 0x3923520F,0x39599D16,0x3990FA4D,0x39C12C4D,0x3A00B1ED,0x3A2B7A3A,0x3A647B6D,0x3A9837F0, - 0x3ACAD226,0x3B071F62,0x3B340AAF,0x3B6FE4BA,0x3B9FD228,0x3BD4F35B,0x3C0DDF04,0x3C3D08A4, - 0x3C7BDFED,0x3CA7CD94,0x3CDF9613,0x3D14F4F0,0x3D467991,0x3D843A29,0x3DB02F0E,0x3DEAC0C7, - 0x3E1C6573,0x3E506334,0x3E8AD4C6,0x3EB8FBAF,0x3EF67A41,0x3F243516,0x3F5ACB94,0x3F91C3D3, - 0x3FC238D2,0x400164D2,0x402C6897,0x4065B907,0x40990B88,0x40CBEC15,0x4107DB35,0x413504F3, -}; -static const unsigned int stChannel_Decode1_scaleInt[]={ - 0x00000000,0x3F2AAAAB,0x3ECCCCCD,0x3E924925,0x3E638E39,0x3E3A2E8C,0x3E1D89D9,0x3E088889, - 0x3D842108,0x3D020821,0x3C810204,0x3C008081,0x3B804020,0x3B002008,0x3A801002,0x3A000801, -}; -static const float *stChannel_Decode1_valueFloat=(const float *)stChannel_Decode1_valueInt; -static const float *stChannel_Decode1_scaleFloat=(const float *)stChannel_Decode1_scaleInt; - -void stChannel_Decode1(stChannel *ch,clData *data,unsigned int a,int b,const unsigned char *ath){ - unsigned int i; - const unsigned int count=ch->count; - char *value=ch->value; - char *value2=ch->value2; - char *value3=ch->value3; - char *scale=ch->scale; - float *base=ch->base; - int v=clData_GetBit(data,3); - if(v>=6){ - for(i=0;i>1,v4; - value[0]=v1; - for(i=1;itype==2){ - v=clData_CheckBit(data,4);value2[0]=v; - if(v<15)for(i=0;i<8;i++)value2[i]=clData_GetBit(data,4); - }else{ - for(i=0;i>8)-((v*5)>>1)+1; - if(v<0)v=15; - else if(v>=0x39)v=1; - else v=stChannel_Decode1_scalelist[v]; - } - scale[i]=v; - } - memset(&scale[count],0,0x80-count); - for(i=0;icount; - const char *scale=ch->scale; - const float *base=ch->base; - float *block=ch->block; - for(i=0;i>1); - if(!v)clData_AddBit(data,-1); - f=(float)v; - } - block[i]=base[i]*f; - } - memset(&block[count],0,sizeof(float)*(0x80-count)); +/* scalefactor-to-scaling table, generated from sqrt(128) * (2^(53/128))^(scale_factor - 63) */ +static const unsigned int decode1_dequantizer_scaling_table_int[64] = { + 0x342A8D26,0x34633F89,0x3497657D,0x34C9B9BE,0x35066491,0x353311C4,0x356E9910,0x359EF532, + 0x35D3CCF1,0x360D1ADF,0x363C034A,0x367A83B3,0x36A6E595,0x36DE60F5,0x371426FF,0x3745672A, + 0x37838359,0x37AF3B79,0x37E97C38,0x381B8D3A,0x384F4319,0x388A14D5,0x38B7FBF0,0x38F5257D, + 0x3923520F,0x39599D16,0x3990FA4D,0x39C12C4D,0x3A00B1ED,0x3A2B7A3A,0x3A647B6D,0x3A9837F0, + 0x3ACAD226,0x3B071F62,0x3B340AAF,0x3B6FE4BA,0x3B9FD228,0x3BD4F35B,0x3C0DDF04,0x3C3D08A4, + 0x3C7BDFED,0x3CA7CD94,0x3CDF9613,0x3D14F4F0,0x3D467991,0x3D843A29,0x3DB02F0E,0x3DEAC0C7, + 0x3E1C6573,0x3E506334,0x3E8AD4C6,0x3EB8FBAF,0x3EF67A41,0x3F243516,0x3F5ACB94,0x3F91C3D3, + 0x3FC238D2,0x400164D2,0x402C6897,0x4065B907,0x40990B88,0x40CBEC15,0x4107DB35,0x413504F3, +}; +static const float *decode1_dequantizer_scaling_table = (const float *)decode1_dequantizer_scaling_table_int; + +static const unsigned int decode1_quantizer_step_size_int[16] = { + 0x00000000,0x3F2AAAAB,0x3ECCCCCD,0x3E924925,0x3E638E39,0x3E3A2E8C,0x3E1D89D9,0x3E088889, + 0x3D842108,0x3D020821,0x3C810204,0x3C008081,0x3B804020,0x3B002008,0x3A801002,0x3A000801, +}; +static const float *decode1_quantizer_step_size = (const float *)decode1_quantizer_step_size_int; + +static int decode1_unpack_channel(stChannel *ch, clData *br, + unsigned int hfr_group_count, unsigned int packed_noise_level, const unsigned char *ath_curve) { + unsigned int i; + const unsigned int csf_count = ch->coded_scalefactor_count; + + + /* read scalefactors */ + { + /* scale indexes to normalize dequantized coefficients */ + unsigned char scalefactor_delta_bits = bitreader_read(br, 3); + if (scalefactor_delta_bits >= 6) { + /* normal scalefactors */ + for (i = 0; i < csf_count; i++) { + ch->scalefactors[i] = bitreader_read(br, 6); + } + } + else if (scalefactor_delta_bits > 0) { + /* delta scalefactors */ + const unsigned char expected_delta = (1 << scalefactor_delta_bits) - 1; + const unsigned char extra_delta = expected_delta >> 1; + unsigned char scalefactor_prev = bitreader_read(br, 6); + + ch->scalefactors[0] = scalefactor_prev; + for (i = 1; i < csf_count; i++) { + unsigned char delta = bitreader_read(br, scalefactor_delta_bits); + + if (delta != expected_delta) { + /* may happen with bad keycodes, scalefactors must be 6b indexes */ + int scalefactor_test = (int)scalefactor_prev + ((int)delta - (int)extra_delta); + if (scalefactor_test < 0 || scalefactor_test > 64) { + return -1; + } + + scalefactor_prev += delta - extra_delta; + } else { + scalefactor_prev = bitreader_read(br, 6); + } + ch->scalefactors[i] = scalefactor_prev; + } + } + else { + /* no scalefactors */ + memset(ch->scalefactors, 0, sizeof(ch->scalefactors[0]) * HCA_SAMPLES_PER_SUBFRAME); + } + } + + if (ch->type == STEREO_SECONDARY) { + /* read intensity */ + unsigned char intensity_value = bitreader_peek(br, 4); + + ch->intensity[0] = intensity_value; + if (intensity_value < 15) { + for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) { + ch->intensity[i] = bitreader_read(br, 4); + } + } + /* 15 may be an invalid value? */ + //else { + // return -1; + //} + } + else { + /* read hfr scalefactors */ + for (i = 0; i < hfr_group_count; i++) { + ch->hfr_scales[i] = bitreader_read(br, 6); + } + } + + /* calculate resolutions */ + { + /* resolution determines the range of values per encoded spectra, + * using codebooks for lower resolutions during dequantization */ + for (i = 0; i < csf_count; i++) { + unsigned char new_resolution = 0; + unsigned char scalefactor = ch->scalefactors[i]; + if (scalefactor > 0) { + int noise_level = ath_curve[i] + ((packed_noise_level + i) >> 8); + int curve_position = noise_level - ((5 * scalefactor) >> 1) + 1; + + /* curve values can be simplified by clamping position to (0,58) and making + * scale_table[0] = 15, table[58] = 1 (like VGAudio does) */ + if (curve_position < 0) + new_resolution = 15; + else if (curve_position >= 57) + new_resolution = 1; + else + new_resolution = decode1_scale_to_resolution_curve[curve_position]; + } + ch->resolution[i] = new_resolution; + } + memset(&ch->resolution[csf_count], 0, sizeof(ch->resolution[0]) * (HCA_SAMPLES_PER_SUBFRAME - csf_count)); + } + + /* calculate gain */ + { + /* get actual scales to dequantize */ + for (i = 0; i < csf_count; i++) { + float scalefactor_scale = decode1_dequantizer_scaling_table[ ch->scalefactors[i] ]; + float resolution_scale = decode1_quantizer_step_size[ ch->resolution[i] ]; + ch->gain[i] = scalefactor_scale * resolution_scale; + } + } + + return 0; } //-------------------------------------------------- -// デコード第三段階 -// ブロックデータ修正その1 ※v2.0から追加 +// Decode 2nd step //-------------------------------------------------- -static const unsigned int stChannel_Decode3_listInt[2][0x40]={ - { - 0x00000000,0x00000000,0x32A0B051,0x32D61B5E,0x330EA43A,0x333E0F68,0x337D3E0C,0x33A8B6D5, - 0x33E0CCDF,0x3415C3FF,0x34478D75,0x3484F1F6,0x34B123F6,0x34EC0719,0x351D3EDA,0x355184DF, - 0x358B95C2,0x35B9FCD2,0x35F7D0DF,0x36251958,0x365BFBB8,0x36928E72,0x36C346CD,0x370218AF, - 0x372D583F,0x3766F85B,0x3799E046,0x37CD078C,0x3808980F,0x38360094,0x38728177,0x38A18FAF, - 0x38D744FD,0x390F6A81,0x393F179A,0x397E9E11,0x39A9A15B,0x39E2055B,0x3A16942D,0x3A48A2D8, - 0x3A85AAC3,0x3AB21A32,0x3AED4F30,0x3B1E196E,0x3B52A81E,0x3B8C57CA,0x3BBAFF5B,0x3BF9295A, - 0x3C25FED7,0x3C5D2D82,0x3C935A2B,0x3CC4563F,0x3D02CD87,0x3D2E4934,0x3D68396A,0x3D9AB62B, - 0x3DCE248C,0x3E0955EE,0x3E36FD92,0x3E73D290,0x3EA27043,0x3ED87039,0x3F1031DC,0x3F40213B, - },{ - 0x3F800000,0x3FAA8D26,0x3FE33F89,0x4017657D,0x4049B9BE,0x40866491,0x40B311C4,0x40EE9910, - 0x411EF532,0x4153CCF1,0x418D1ADF,0x41BC034A,0x41FA83B3,0x4226E595,0x425E60F5,0x429426FF, - 0x42C5672A,0x43038359,0x432F3B79,0x43697C38,0x439B8D3A,0x43CF4319,0x440A14D5,0x4437FBF0, - 0x4475257D,0x44A3520F,0x44D99D16,0x4510FA4D,0x45412C4D,0x4580B1ED,0x45AB7A3A,0x45E47B6D, - 0x461837F0,0x464AD226,0x46871F62,0x46B40AAF,0x46EFE4BA,0x471FD228,0x4754F35B,0x478DDF04, - 0x47BD08A4,0x47FBDFED,0x4827CD94,0x485F9613,0x4894F4F0,0x48C67991,0x49043A29,0x49302F0E, - 0x496AC0C7,0x499C6573,0x49D06334,0x4A0AD4C6,0x4A38FBAF,0x4A767A41,0x4AA43516,0x4ADACB94, - 0x4B11C3D3,0x4B4238D2,0x4B8164D2,0x4BAC6897,0x4BE5B907,0x4C190B88,0x4C4BEC15,0x00000000, - } +static const unsigned char decode2_quantized_spectrum_max_bits[16] = { + 0,2,3,3,4,4,4,4,5,6,7,8,9,10,11,12 +}; +static const unsigned char decode2_quantized_spectrum_bits[128] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,3,3,0,0,0,0,0,0,0,0, + 2,2,3,3,3,3,3,3,0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4, + 3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4, + 3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4, + 3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +}; +static const float decode2_quantized_spectrum_value[128] = { + +0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0, + +0,+0,+1,-1,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0, + +0,+0,+1,+1,-1,-1,+2,-2,+0,+0,+0,+0,+0,+0,+0,+0, + +0,+0,+1,-1,+2,-2,+3,-3,+0,+0,+0,+0,+0,+0,+0,+0, + +0,+0,+1,+1,-1,-1,+2,+2,-2,-2,+3,+3,-3,-3,+4,-4, + +0,+0,+1,+1,-1,-1,+2,+2,-2,-2,+3,-3,+4,-4,+5,-5, + +0,+0,+1,+1,-1,-1,+2,-2,+3,-3,+4,-4,+5,-5,+6,-6, + +0,+0,+1,-1,+2,-2,+3,-3,+4,-4,+5,-5,+6,-6,+7,-7, }; -static const float *stChannel_Decode3_listFloat=(float *)stChannel_Decode3_listInt[1]; -void stChannel_Decode3(stChannel *ch,unsigned int a,unsigned int b,unsigned int c,unsigned int d){ - if(ch->type!=2&&b){ - unsigned int i, j, k, l; - const char *value=ch->value; - const char *value3=ch->value3; - float *block=ch->block; - for(i=0,k=c,l=c-1;icoded_scalefactor_count; + + + for (i = 0; i < csf_count; i++) { + float qc; + unsigned char resolution = ch->resolution[i]; + unsigned char bits = decode2_quantized_spectrum_max_bits[resolution]; + unsigned int code = bitreader_read(br, bits); + + /* read spectral coefficients */ + if (resolution < 8) { + /* use prefix codebooks for lower resolutions */ + code += resolution << 4; + bitreader_skip(br, decode2_quantized_spectrum_bits[code] - bits); + qc = decode2_quantized_spectrum_value[code]; + } + else { + /* parse values in sign-magnitude form (lowest bit = sign) */ + int signed_code = (1 - ((code & 1) << 1)) * (code >> 1); /* move sign */ + if (signed_code == 0) + bitreader_skip(br, -1); /* zero uses one less bit since it has no sign */ + qc = (float)signed_code; + } + + /* dequantize coef with gain */ + ch->spectra[i] = ch->gain[i] * qc; + } + + /* clean rest of spectra */ + memset(&ch->spectra[csf_count], 0, sizeof(ch->spectra[0]) * (HCA_SAMPLES_PER_SUBFRAME - csf_count)); } //-------------------------------------------------- -// デコード第四段階 -// ブロックデータ修正その2 +// Decode 3rd step //-------------------------------------------------- -static const unsigned int stChannel_Decode4_listInt[]={ - // v2.0 - 0x40000000,0x3FEDB6DB,0x3FDB6DB7,0x3FC92492,0x3FB6DB6E,0x3FA49249,0x3F924925,0x3F800000, - 0x3F5B6DB7,0x3F36DB6E,0x3F124925,0x3EDB6DB7,0x3E924925,0x3E124925,0x00000000,0x00000000, - 0x00000000,0x32A0B051,0x32D61B5E,0x330EA43A,0x333E0F68,0x337D3E0C,0x33A8B6D5,0x33E0CCDF, - 0x3415C3FF,0x34478D75,0x3484F1F6,0x34B123F6,0x34EC0719,0x351D3EDA,0x355184DF,0x358B95C2, - 0x35B9FCD2,0x35F7D0DF,0x36251958,0x365BFBB8,0x36928E72,0x36C346CD,0x370218AF,0x372D583F, - 0x3766F85B,0x3799E046,0x37CD078C,0x3808980F,0x38360094,0x38728177,0x38A18FAF,0x38D744FD, - 0x390F6A81,0x393F179A,0x397E9E11,0x39A9A15B,0x39E2055B,0x3A16942D,0x3A48A2D8,0x3A85AAC3, - 0x3AB21A32,0x3AED4F30,0x3B1E196E,0x3B52A81E,0x3B8C57CA,0x3BBAFF5B,0x3BF9295A,0x3C25FED7, - //↓この2行要らない? - 0x3C5D2D82,0x3C935A2B,0x3CC4563F,0x3D02CD87,0x3D2E4934,0x3D68396A,0x3D9AB62B,0x3DCE248C, - 0x3E0955EE,0x3E36FD92,0x3E73D290,0x3EA27043,0x3ED87039,0x3F1031DC,0x3F40213B,0x00000000, - // v1.3 - //0x40000000,0x3FEDB6DB,0x3FDB6DB7,0x3FC92492,0x3FB6DB6E,0x3FA49249,0x3F924925,0x3F800000, - //0x3F5B6DB7,0x3F36DB6E,0x3F124925,0x3EDB6DB7,0x3E924925,0x3E124925,0x00000000,0x00000000, -}; +static const unsigned int decode3_scale_conversion_table_int[128] = { + 0x00000000,0x00000000,0x32A0B051,0x32D61B5E,0x330EA43A,0x333E0F68,0x337D3E0C,0x33A8B6D5, + 0x33E0CCDF,0x3415C3FF,0x34478D75,0x3484F1F6,0x34B123F6,0x34EC0719,0x351D3EDA,0x355184DF, + 0x358B95C2,0x35B9FCD2,0x35F7D0DF,0x36251958,0x365BFBB8,0x36928E72,0x36C346CD,0x370218AF, + 0x372D583F,0x3766F85B,0x3799E046,0x37CD078C,0x3808980F,0x38360094,0x38728177,0x38A18FAF, + 0x38D744FD,0x390F6A81,0x393F179A,0x397E9E11,0x39A9A15B,0x39E2055B,0x3A16942D,0x3A48A2D8, + 0x3A85AAC3,0x3AB21A32,0x3AED4F30,0x3B1E196E,0x3B52A81E,0x3B8C57CA,0x3BBAFF5B,0x3BF9295A, + 0x3C25FED7,0x3C5D2D82,0x3C935A2B,0x3CC4563F,0x3D02CD87,0x3D2E4934,0x3D68396A,0x3D9AB62B, + 0x3DCE248C,0x3E0955EE,0x3E36FD92,0x3E73D290,0x3EA27043,0x3ED87039,0x3F1031DC,0x3F40213B, -void stChannel_Decode4(stChannel *ch,int index,unsigned int a,unsigned int b,unsigned int c){ - if(ch->type==1&&c){ - float f1=((float *)stChannel_Decode4_listInt)[ch[1].value2[index]]; - float f2=f1-2.0f; - float *s=&ch->block[b]; - float *d=&ch[1].block[b]; - unsigned int i; - for(i=0;itype == STEREO_SECONDARY) + return; + if (bands_per_hfr_group == 0) /* not in v1.x */ + return; + + { + unsigned int group, i; + unsigned int start_band = stereo_band_count + base_band_count; + unsigned int highband = start_band; + unsigned int lowband = start_band - 1; + + for (group = 0; group < hfr_group_count; group++) { + for (i = 0; i < bands_per_hfr_group && highband < total_band_count; i++) { + unsigned int sc_index = ch->hfr_scales[group] - ch->scalefactors[lowband] + 64; + ch->spectra[highband] = decode3_scale_conversion_table[sc_index] * ch->spectra[lowband]; + highband++; + lowband--; + } + } + + ch->spectra[HCA_SAMPLES_PER_SUBFRAME - 1] = 0; /* last spectral coefficient should be 0 */ + } } //-------------------------------------------------- -// デコード第五段階 -// 波形データを生成 +// Decode 4th step //-------------------------------------------------- -static const unsigned int stChannel_Decode5_list1Int[7][0x40]={ - { - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, - },{ - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, - },{ - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, - },{ - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, - },{ - 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, - 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, - 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, - 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, - 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, - 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, - 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, - 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, - },{ - 0x3F7FFB11,0x3F7FD397,0x3F7F84AB,0x3F7F0E58,0x3F7E70B0,0x3F7DABCC,0x3F7CBFC9,0x3F7BACCD, - 0x3F7A7302,0x3F791298,0x3F778BC5,0x3F75DEC6,0x3F740BDD,0x3F721352,0x3F6FF573,0x3F6DB293, - 0x3F6B4B0C,0x3F68BF3C,0x3F660F88,0x3F633C5A,0x3F604621,0x3F5D2D53,0x3F59F26A,0x3F5695E5, - 0x3F531849,0x3F4F7A1F,0x3F4BBBF8,0x3F47DE65,0x3F43E200,0x3F3FC767,0x3F3B8F3B,0x3F373A23, - 0x3F7FFB11,0x3F7FD397,0x3F7F84AB,0x3F7F0E58,0x3F7E70B0,0x3F7DABCC,0x3F7CBFC9,0x3F7BACCD, - 0x3F7A7302,0x3F791298,0x3F778BC5,0x3F75DEC6,0x3F740BDD,0x3F721352,0x3F6FF573,0x3F6DB293, - 0x3F6B4B0C,0x3F68BF3C,0x3F660F88,0x3F633C5A,0x3F604621,0x3F5D2D53,0x3F59F26A,0x3F5695E5, - 0x3F531849,0x3F4F7A1F,0x3F4BBBF8,0x3F47DE65,0x3F43E200,0x3F3FC767,0x3F3B8F3B,0x3F373A23, - },{ - 0x3F7FFEC4,0x3F7FF4E6,0x3F7FE129,0x3F7FC38F,0x3F7F9C18,0x3F7F6AC7,0x3F7F2F9D,0x3F7EEA9D, - 0x3F7E9BC9,0x3F7E4323,0x3F7DE0B1,0x3F7D7474,0x3F7CFE73,0x3F7C7EB0,0x3F7BF531,0x3F7B61FC, - 0x3F7AC516,0x3F7A1E84,0x3F796E4E,0x3F78B47B,0x3F77F110,0x3F772417,0x3F764D97,0x3F756D97, - 0x3F748422,0x3F73913F,0x3F7294F8,0x3F718F57,0x3F708066,0x3F6F6830,0x3F6E46BE,0x3F6D1C1D, - 0x3F6BE858,0x3F6AAB7B,0x3F696591,0x3F6816A8,0x3F66BECC,0x3F655E0B,0x3F63F473,0x3F628210, - 0x3F6106F2,0x3F5F8327,0x3F5DF6BE,0x3F5C61C7,0x3F5AC450,0x3F591E6A,0x3F577026,0x3F55B993, - 0x3F53FAC3,0x3F5233C6,0x3F5064AF,0x3F4E8D90,0x3F4CAE79,0x3F4AC77F,0x3F48D8B3,0x3F46E22A, - 0x3F44E3F5,0x3F42DE29,0x3F40D0DA,0x3F3EBC1B,0x3F3CA003,0x3F3A7CA4,0x3F385216,0x3F36206C, - } +static const unsigned int decode4_intensity_ratio_table_int[80] = { + 0x40000000,0x3FEDB6DB,0x3FDB6DB7,0x3FC92492,0x3FB6DB6E,0x3FA49249,0x3F924925,0x3F800000, + 0x3F5B6DB7,0x3F36DB6E,0x3F124925,0x3EDB6DB7,0x3E924925,0x3E124925,0x00000000,0x00000000, + /* v2.0 seems to define indexes over 15, but intensity is packed in 4b thus unused */ + 0x00000000,0x32A0B051,0x32D61B5E,0x330EA43A,0x333E0F68,0x337D3E0C,0x33A8B6D5,0x33E0CCDF, + 0x3415C3FF,0x34478D75,0x3484F1F6,0x34B123F6,0x34EC0719,0x351D3EDA,0x355184DF,0x358B95C2, + 0x35B9FCD2,0x35F7D0DF,0x36251958,0x365BFBB8,0x36928E72,0x36C346CD,0x370218AF,0x372D583F, + 0x3766F85B,0x3799E046,0x37CD078C,0x3808980F,0x38360094,0x38728177,0x38A18FAF,0x38D744FD, + 0x390F6A81,0x393F179A,0x397E9E11,0x39A9A15B,0x39E2055B,0x3A16942D,0x3A48A2D8,0x3A85AAC3, + 0x3AB21A32,0x3AED4F30,0x3B1E196E,0x3B52A81E,0x3B8C57CA,0x3BBAFF5B,0x3BF9295A,0x3C25FED7, + 0x3C5D2D82,0x3C935A2B,0x3CC4563F,0x3D02CD87,0x3D2E4934,0x3D68396A,0x3D9AB62B,0x3DCE248C, + 0x3E0955EE,0x3E36FD92,0x3E73D290,0x3EA27043,0x3ED87039,0x3F1031DC,0x3F40213B,0x00000000, }; -static const unsigned int stChannel_Decode5_list2Int[7][0x40]={ - { - 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, - 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, - 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, - 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, - 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, - 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, - 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, - 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, - },{ - 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, - 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, - 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, - 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, - 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, - 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, - 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, - 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, - },{ - 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, - 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, - 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, - 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, - 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, - 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, - 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, - 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, - },{ - 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, - 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, - 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, - 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, - 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, - 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, - 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, - 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, - },{ - 0xBCC90AB0,0xBD96A905,0xBDFAB273,0xBE2F10A2,0xBE605C13,0xBE888E93,0xBEA09AE5,0xBEB8442A, - 0xBECF7BCA,0xBEE63375,0xBEFC5D27,0xBF08F59B,0xBF13682A,0xBF1D7FD1,0xBF273656,0xBF3085BB, - 0x3CC90AB0,0x3D96A905,0x3DFAB273,0x3E2F10A2,0x3E605C13,0x3E888E93,0x3EA09AE5,0x3EB8442A, - 0x3ECF7BCA,0x3EE63375,0x3EFC5D27,0x3F08F59B,0x3F13682A,0x3F1D7FD1,0x3F273656,0x3F3085BB, - 0x3CC90AB0,0x3D96A905,0x3DFAB273,0x3E2F10A2,0x3E605C13,0x3E888E93,0x3EA09AE5,0x3EB8442A, - 0x3ECF7BCA,0x3EE63375,0x3EFC5D27,0x3F08F59B,0x3F13682A,0x3F1D7FD1,0x3F273656,0x3F3085BB, - 0xBCC90AB0,0xBD96A905,0xBDFAB273,0xBE2F10A2,0xBE605C13,0xBE888E93,0xBEA09AE5,0xBEB8442A, - 0xBECF7BCA,0xBEE63375,0xBEFC5D27,0xBF08F59B,0xBF13682A,0xBF1D7FD1,0xBF273656,0xBF3085BB, - },{ - 0xBC490E90,0xBD16C32C,0xBD7B2B74,0xBDAFB680,0xBDE1BC2E,0xBE09CF86,0xBE22ABB6,0xBE3B6ECF, - 0xBE541501,0xBE6C9A7F,0xBE827DC0,0xBE8E9A22,0xBE9AA086,0xBEA68F12,0xBEB263EF,0xBEBE1D4A, - 0xBEC9B953,0xBED53641,0xBEE0924F,0xBEEBCBBB,0xBEF6E0CB,0xBF00E7E4,0xBF064B82,0xBF0B9A6B, - 0xBF10D3CD,0xBF15F6D9,0xBF1B02C6,0xBF1FF6CB,0xBF24D225,0xBF299415,0xBF2E3BDE,0xBF32C8C9, - 0x3C490E90,0x3D16C32C,0x3D7B2B74,0x3DAFB680,0x3DE1BC2E,0x3E09CF86,0x3E22ABB6,0x3E3B6ECF, - 0x3E541501,0x3E6C9A7F,0x3E827DC0,0x3E8E9A22,0x3E9AA086,0x3EA68F12,0x3EB263EF,0x3EBE1D4A, - 0x3EC9B953,0x3ED53641,0x3EE0924F,0x3EEBCBBB,0x3EF6E0CB,0x3F00E7E4,0x3F064B82,0x3F0B9A6B, - 0x3F10D3CD,0x3F15F6D9,0x3F1B02C6,0x3F1FF6CB,0x3F24D225,0x3F299415,0x3F2E3BDE,0x3F32C8C9, - },{ - 0xBBC90F88,0xBC96C9B6,0xBCFB49BA,0xBD2FE007,0xBD621469,0xBD8A200A,0xBDA3308C,0xBDBC3AC3, - 0xBDD53DB9,0xBDEE3876,0xBE039502,0xBE1008B7,0xBE1C76DE,0xBE28DEFC,0xBE354098,0xBE419B37, - 0xBE4DEE60,0xBE5A3997,0xBE667C66,0xBE72B651,0xBE7EE6E1,0xBE8586CE,0xBE8B9507,0xBE919DDD, - 0xBE97A117,0xBE9D9E78,0xBEA395C5,0xBEA986C4,0xBEAF713A,0xBEB554EC,0xBEBB31A0,0xBEC1071E, - 0xBEC6D529,0xBECC9B8B,0xBED25A09,0xBED8106B,0xBEDDBE79,0xBEE363FA,0xBEE900B7,0xBEEE9479, - 0xBEF41F07,0xBEF9A02D,0xBEFF17B2,0xBF0242B1,0xBF04F484,0xBF07A136,0xBF0A48AD,0xBF0CEAD0, - 0xBF0F8784,0xBF121EB0,0xBF14B039,0xBF173C07,0xBF19C200,0xBF1C420C,0xBF1EBC12,0xBF212FF9, - 0xBF239DA9,0xBF26050A,0xBF286605,0xBF2AC082,0xBF2D1469,0xBF2F61A5,0xBF31A81D,0xBF33E7BC, - } -}; -static const unsigned int stChannel_Decode5_list3Int[2][0x40]={ - { - 0x3A3504F0,0x3B0183B8,0x3B70C538,0x3BBB9268,0x3C04A809,0x3C308200,0x3C61284C,0x3C8B3F17, - 0x3CA83992,0x3CC77FBD,0x3CE91110,0x3D0677CD,0x3D198FC4,0x3D2DD35C,0x3D434643,0x3D59ECC1, - 0x3D71CBA8,0x3D85741E,0x3D92A413,0x3DA078B4,0x3DAEF522,0x3DBE1C9E,0x3DCDF27B,0x3DDE7A1D, - 0x3DEFB6ED,0x3E00D62B,0x3E0A2EDA,0x3E13E72A,0x3E1E00B1,0x3E287CF2,0x3E335D55,0x3E3EA321, - 0x3E4A4F75,0x3E56633F,0x3E62DF37,0x3E6FC3D1,0x3E7D1138,0x3E8563A2,0x3E8C72B7,0x3E93B561, - 0x3E9B2AEF,0x3EA2D26F,0x3EAAAAAB,0x3EB2B222,0x3EBAE706,0x3EC34737,0x3ECBD03D,0x3ED47F46, - 0x3EDD5128,0x3EE6425C,0x3EEF4EFF,0x3EF872D7,0x3F00D4A9,0x3F0576CA,0x3F0A1D3B,0x3F0EC548, - 0x3F136C25,0x3F180EF2,0x3F1CAAC2,0x3F213CA2,0x3F25C1A5,0x3F2A36E7,0x3F2E9998,0x3F32E705, - },{ - 0xBF371C9E,0xBF3B37FE,0xBF3F36F2,0xBF431780,0xBF46D7E6,0xBF4A76A4,0xBF4DF27C,0xBF514A6F, - 0xBF547DC5,0xBF578C03,0xBF5A74EE,0xBF5D3887,0xBF5FD707,0xBF6250DA,0xBF64A699,0xBF66D908, - 0xBF68E90E,0xBF6AD7B1,0xBF6CA611,0xBF6E5562,0xBF6FE6E7,0xBF715BEF,0xBF72B5D1,0xBF73F5E6, - 0xBF751D89,0xBF762E13,0xBF7728D7,0xBF780F20,0xBF78E234,0xBF79A34C,0xBF7A5397,0xBF7AF439, - 0xBF7B8648,0xBF7C0ACE,0xBF7C82C8,0xBF7CEF26,0xBF7D50CB,0xBF7DA88E,0xBF7DF737,0xBF7E3D86, - 0xBF7E7C2A,0xBF7EB3CC,0xBF7EE507,0xBF7F106C,0xBF7F3683,0xBF7F57CA,0xBF7F74B6,0xBF7F8DB6, - 0xBF7FA32E,0xBF7FB57B,0xBF7FC4F6,0xBF7FD1ED,0xBF7FDCAD,0xBF7FE579,0xBF7FEC90,0xBF7FF22E, - 0xBF7FF688,0xBF7FF9D0,0xBF7FFC32,0xBF7FFDDA,0xBF7FFEED,0xBF7FFF8F,0xBF7FFFDF,0xBF7FFFFC, - } +static const float *decode4_intensity_ratio_table = (const float *)decode4_intensity_ratio_table_int; + +static void decode4_apply_intensity_stereo(stChannel *ch_pair, int subframe, + unsigned int total_band_count, unsigned int base_band_count, unsigned int stereo_band_count) { + if (ch_pair[0].type != STEREO_PRIMARY) + return; + if (stereo_band_count == 0) + return; + + { + float ratio_l = decode4_intensity_ratio_table[ ch_pair[1].intensity[subframe] ]; + float ratio_r = ratio_l - 2.0f; + float *sp_l = ch_pair[0].spectra; + float *sp_r = ch_pair[1].spectra; + unsigned int band; + + for (band = base_band_count; band < total_band_count; band++) { + sp_r[band] = sp_l[band] * ratio_r; + sp_l[band] = sp_l[band] * ratio_l; + } + } +} + +//-------------------------------------------------- +// Decode 5th step +//-------------------------------------------------- +static const unsigned int decode5_sin_tables_int[7][64] = { + { + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, + },{ + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + 0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31,0x3F7B14BE,0x3F54DB31, + },{ + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + 0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403,0x3F7EC46D,0x3F74FA0B,0x3F61C598,0x3F45E403, + },{ + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + 0x3F7FB10F,0x3F7D3AAC,0x3F7853F8,0x3F710908,0x3F676BD8,0x3F5B941A,0x3F4D9F02,0x3F3DAEF9, + },{ + 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, + 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, + 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, + 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, + 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, + 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, + 0x3F7FEC43,0x3F7F4E6D,0x3F7E1324,0x3F7C3B28,0x3F79C79D,0x3F76BA07,0x3F731447,0x3F6ED89E, + 0x3F6A09A7,0x3F64AA59,0x3F5EBE05,0x3F584853,0x3F514D3D,0x3F49D112,0x3F41D870,0x3F396842, + },{ + 0x3F7FFB11,0x3F7FD397,0x3F7F84AB,0x3F7F0E58,0x3F7E70B0,0x3F7DABCC,0x3F7CBFC9,0x3F7BACCD, + 0x3F7A7302,0x3F791298,0x3F778BC5,0x3F75DEC6,0x3F740BDD,0x3F721352,0x3F6FF573,0x3F6DB293, + 0x3F6B4B0C,0x3F68BF3C,0x3F660F88,0x3F633C5A,0x3F604621,0x3F5D2D53,0x3F59F26A,0x3F5695E5, + 0x3F531849,0x3F4F7A1F,0x3F4BBBF8,0x3F47DE65,0x3F43E200,0x3F3FC767,0x3F3B8F3B,0x3F373A23, + 0x3F7FFB11,0x3F7FD397,0x3F7F84AB,0x3F7F0E58,0x3F7E70B0,0x3F7DABCC,0x3F7CBFC9,0x3F7BACCD, + 0x3F7A7302,0x3F791298,0x3F778BC5,0x3F75DEC6,0x3F740BDD,0x3F721352,0x3F6FF573,0x3F6DB293, + 0x3F6B4B0C,0x3F68BF3C,0x3F660F88,0x3F633C5A,0x3F604621,0x3F5D2D53,0x3F59F26A,0x3F5695E5, + 0x3F531849,0x3F4F7A1F,0x3F4BBBF8,0x3F47DE65,0x3F43E200,0x3F3FC767,0x3F3B8F3B,0x3F373A23, + },{ + 0x3F7FFEC4,0x3F7FF4E6,0x3F7FE129,0x3F7FC38F,0x3F7F9C18,0x3F7F6AC7,0x3F7F2F9D,0x3F7EEA9D, + 0x3F7E9BC9,0x3F7E4323,0x3F7DE0B1,0x3F7D7474,0x3F7CFE73,0x3F7C7EB0,0x3F7BF531,0x3F7B61FC, + 0x3F7AC516,0x3F7A1E84,0x3F796E4E,0x3F78B47B,0x3F77F110,0x3F772417,0x3F764D97,0x3F756D97, + 0x3F748422,0x3F73913F,0x3F7294F8,0x3F718F57,0x3F708066,0x3F6F6830,0x3F6E46BE,0x3F6D1C1D, + 0x3F6BE858,0x3F6AAB7B,0x3F696591,0x3F6816A8,0x3F66BECC,0x3F655E0B,0x3F63F473,0x3F628210, + 0x3F6106F2,0x3F5F8327,0x3F5DF6BE,0x3F5C61C7,0x3F5AC450,0x3F591E6A,0x3F577026,0x3F55B993, + 0x3F53FAC3,0x3F5233C6,0x3F5064AF,0x3F4E8D90,0x3F4CAE79,0x3F4AC77F,0x3F48D8B3,0x3F46E22A, + 0x3F44E3F5,0x3F42DE29,0x3F40D0DA,0x3F3EBC1B,0x3F3CA003,0x3F3A7CA4,0x3F385216,0x3F36206C, + } }; -void stChannel_Decode5(stChannel *ch,int index){ - const float *s, *s1, *s2; - float *d; - unsigned int i, count1, count2, j, k; - s=ch->block;d=ch->wav1; - for(i=0,count1=1,count2=0x40;i<7;i++,count1<<=1,count2>>=1){ - float *d1=d; - float *d2=&d[count2]; - float *w; - for(j=0;jwav1;d=ch->block; - for(i=0,count1=0x40,count2=1;i<7;i++,count1>>=1,count2<<=1){ - const float *list1Float=(const float *)stChannel_Decode5_list1Int[i]; - const float *list2Float=(const float *)stChannel_Decode5_list2Int[i]; - float *d1, *d2, *w; - s1=s; - s2=&s1[count2]; - d1=d; - d2=&d1[count2*2-1]; - for(j=0;jwav2; - for(i=0;i<0x80;i++)*(d++)=*(s++); - s=(const float *)stChannel_Decode5_list3Int;d=ch->wave[index]; - s1=&ch->wav2[0x40];s2=ch->wav3; - for(i=0;i<0x40;i++)*(d++)=*(s1++)**(s++)+*(s2++); - for(i=0;i<0x40;i++)*(d++)=*(s++)**(--s1)-*(s2++); - s1=&ch->wav2[0x40-1];d=ch->wav3; - for(i=0;i<0x40;i++)*(d++)=*(s1--)**(--s); - for(i=0;i<0x40;i++)*(d++)=*(--s)**(++s1); +static const unsigned int decode5_cos_tables_int[7][64]={ + { + 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, + 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, + 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, + 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, + 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, + 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, + 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, + 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, + },{ + 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, + 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, + 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, + 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, + 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, + 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, + 0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA,0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA, + 0x3E47C5C2,0x3F0E39DA,0xBE47C5C2,0xBF0E39DA,0xBE47C5C2,0xBF0E39DA,0x3E47C5C2,0x3F0E39DA, + },{ + 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, + 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, + 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, + 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, + 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, + 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, + 0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799,0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799, + 0x3DC8BD36,0x3E94A031,0x3EF15AEA,0x3F226799,0xBDC8BD36,0xBE94A031,0xBEF15AEA,0xBF226799, + },{ + 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, + 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, + 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, + 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, + 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, + 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, + 0xBD48FB30,0xBE164083,0xBE78CFCC,0xBEAC7CD4,0xBEDAE880,0xBF039C3D,0xBF187FC0,0xBF2BEB4A, + 0x3D48FB30,0x3E164083,0x3E78CFCC,0x3EAC7CD4,0x3EDAE880,0x3F039C3D,0x3F187FC0,0x3F2BEB4A, + },{ + 0xBCC90AB0,0xBD96A905,0xBDFAB273,0xBE2F10A2,0xBE605C13,0xBE888E93,0xBEA09AE5,0xBEB8442A, + 0xBECF7BCA,0xBEE63375,0xBEFC5D27,0xBF08F59B,0xBF13682A,0xBF1D7FD1,0xBF273656,0xBF3085BB, + 0x3CC90AB0,0x3D96A905,0x3DFAB273,0x3E2F10A2,0x3E605C13,0x3E888E93,0x3EA09AE5,0x3EB8442A, + 0x3ECF7BCA,0x3EE63375,0x3EFC5D27,0x3F08F59B,0x3F13682A,0x3F1D7FD1,0x3F273656,0x3F3085BB, + 0x3CC90AB0,0x3D96A905,0x3DFAB273,0x3E2F10A2,0x3E605C13,0x3E888E93,0x3EA09AE5,0x3EB8442A, + 0x3ECF7BCA,0x3EE63375,0x3EFC5D27,0x3F08F59B,0x3F13682A,0x3F1D7FD1,0x3F273656,0x3F3085BB, + 0xBCC90AB0,0xBD96A905,0xBDFAB273,0xBE2F10A2,0xBE605C13,0xBE888E93,0xBEA09AE5,0xBEB8442A, + 0xBECF7BCA,0xBEE63375,0xBEFC5D27,0xBF08F59B,0xBF13682A,0xBF1D7FD1,0xBF273656,0xBF3085BB, + },{ + 0xBC490E90,0xBD16C32C,0xBD7B2B74,0xBDAFB680,0xBDE1BC2E,0xBE09CF86,0xBE22ABB6,0xBE3B6ECF, + 0xBE541501,0xBE6C9A7F,0xBE827DC0,0xBE8E9A22,0xBE9AA086,0xBEA68F12,0xBEB263EF,0xBEBE1D4A, + 0xBEC9B953,0xBED53641,0xBEE0924F,0xBEEBCBBB,0xBEF6E0CB,0xBF00E7E4,0xBF064B82,0xBF0B9A6B, + 0xBF10D3CD,0xBF15F6D9,0xBF1B02C6,0xBF1FF6CB,0xBF24D225,0xBF299415,0xBF2E3BDE,0xBF32C8C9, + 0x3C490E90,0x3D16C32C,0x3D7B2B74,0x3DAFB680,0x3DE1BC2E,0x3E09CF86,0x3E22ABB6,0x3E3B6ECF, + 0x3E541501,0x3E6C9A7F,0x3E827DC0,0x3E8E9A22,0x3E9AA086,0x3EA68F12,0x3EB263EF,0x3EBE1D4A, + 0x3EC9B953,0x3ED53641,0x3EE0924F,0x3EEBCBBB,0x3EF6E0CB,0x3F00E7E4,0x3F064B82,0x3F0B9A6B, + 0x3F10D3CD,0x3F15F6D9,0x3F1B02C6,0x3F1FF6CB,0x3F24D225,0x3F299415,0x3F2E3BDE,0x3F32C8C9, + },{ + 0xBBC90F88,0xBC96C9B6,0xBCFB49BA,0xBD2FE007,0xBD621469,0xBD8A200A,0xBDA3308C,0xBDBC3AC3, + 0xBDD53DB9,0xBDEE3876,0xBE039502,0xBE1008B7,0xBE1C76DE,0xBE28DEFC,0xBE354098,0xBE419B37, + 0xBE4DEE60,0xBE5A3997,0xBE667C66,0xBE72B651,0xBE7EE6E1,0xBE8586CE,0xBE8B9507,0xBE919DDD, + 0xBE97A117,0xBE9D9E78,0xBEA395C5,0xBEA986C4,0xBEAF713A,0xBEB554EC,0xBEBB31A0,0xBEC1071E, + 0xBEC6D529,0xBECC9B8B,0xBED25A09,0xBED8106B,0xBEDDBE79,0xBEE363FA,0xBEE900B7,0xBEEE9479, + 0xBEF41F07,0xBEF9A02D,0xBEFF17B2,0xBF0242B1,0xBF04F484,0xBF07A136,0xBF0A48AD,0xBF0CEAD0, + 0xBF0F8784,0xBF121EB0,0xBF14B039,0xBF173C07,0xBF19C200,0xBF1C420C,0xBF1EBC12,0xBF212FF9, + 0xBF239DA9,0xBF26050A,0xBF286605,0xBF2AC082,0xBF2D1469,0xBF2F61A5,0xBF31A81D,0xBF33E7BC, + } +}; + +/* HCA window function, close to a KBD window with an alpha of around 3.82 (similar to AAC/Vorbis) */ +static const unsigned int decode5_imdct_window_int[128] = { + 0x3A3504F0,0x3B0183B8,0x3B70C538,0x3BBB9268,0x3C04A809,0x3C308200,0x3C61284C,0x3C8B3F17, + 0x3CA83992,0x3CC77FBD,0x3CE91110,0x3D0677CD,0x3D198FC4,0x3D2DD35C,0x3D434643,0x3D59ECC1, + 0x3D71CBA8,0x3D85741E,0x3D92A413,0x3DA078B4,0x3DAEF522,0x3DBE1C9E,0x3DCDF27B,0x3DDE7A1D, + 0x3DEFB6ED,0x3E00D62B,0x3E0A2EDA,0x3E13E72A,0x3E1E00B1,0x3E287CF2,0x3E335D55,0x3E3EA321, + 0x3E4A4F75,0x3E56633F,0x3E62DF37,0x3E6FC3D1,0x3E7D1138,0x3E8563A2,0x3E8C72B7,0x3E93B561, + 0x3E9B2AEF,0x3EA2D26F,0x3EAAAAAB,0x3EB2B222,0x3EBAE706,0x3EC34737,0x3ECBD03D,0x3ED47F46, + 0x3EDD5128,0x3EE6425C,0x3EEF4EFF,0x3EF872D7,0x3F00D4A9,0x3F0576CA,0x3F0A1D3B,0x3F0EC548, + 0x3F136C25,0x3F180EF2,0x3F1CAAC2,0x3F213CA2,0x3F25C1A5,0x3F2A36E7,0x3F2E9998,0x3F32E705, + + 0xBF371C9E,0xBF3B37FE,0xBF3F36F2,0xBF431780,0xBF46D7E6,0xBF4A76A4,0xBF4DF27C,0xBF514A6F, + 0xBF547DC5,0xBF578C03,0xBF5A74EE,0xBF5D3887,0xBF5FD707,0xBF6250DA,0xBF64A699,0xBF66D908, + 0xBF68E90E,0xBF6AD7B1,0xBF6CA611,0xBF6E5562,0xBF6FE6E7,0xBF715BEF,0xBF72B5D1,0xBF73F5E6, + 0xBF751D89,0xBF762E13,0xBF7728D7,0xBF780F20,0xBF78E234,0xBF79A34C,0xBF7A5397,0xBF7AF439, + 0xBF7B8648,0xBF7C0ACE,0xBF7C82C8,0xBF7CEF26,0xBF7D50CB,0xBF7DA88E,0xBF7DF737,0xBF7E3D86, + 0xBF7E7C2A,0xBF7EB3CC,0xBF7EE507,0xBF7F106C,0xBF7F3683,0xBF7F57CA,0xBF7F74B6,0xBF7F8DB6, + 0xBF7FA32E,0xBF7FB57B,0xBF7FC4F6,0xBF7FD1ED,0xBF7FDCAD,0xBF7FE579,0xBF7FEC90,0xBF7FF22E, + 0xBF7FF688,0xBF7FF9D0,0xBF7FFC32,0xBF7FFDDA,0xBF7FFEED,0xBF7FFF8F,0xBF7FFFDF,0xBF7FFFFC, +}; +static const float *decode5_imdct_window = (const float *)decode5_imdct_window_int; + +static void decoder5_run_imdct(stChannel *ch, int subframe) { + const static unsigned int size = HCA_SAMPLES_PER_SUBFRAME; + const static unsigned int half = HCA_SAMPLES_PER_SUBFRAME / 2; + const static unsigned int mdct_bits = HCA_MDCT_BITS; + + + /* apply DCT-IV to dequantized spectra */ + { + unsigned int i, j, k; + unsigned int count1a, count2a, count1b, count2b; + const float *temp1a, *temp1b; + float *temp2a, *temp2b; + + /* this is all too crafty for me to simplify, see VGAudio (Mdct.Dct4) */ + + temp1a = ch->spectra; + temp2a = ch->temp; + count1a = 1; + count2a = half; + for (i = 0; i < mdct_bits; i++) { + float *swap; + float *d1 = &temp2a[0]; + float *d2 = &temp2a[count2a]; + + for (j = 0; j < count1a; j++) { + for (k = 0; k < count2a; k++) { + float a = *(temp1a++); + float b = *(temp1a++); + *(d1++) = b + a; + *(d2++) = a - b; + } + d1 += count2a; + d2 += count2a; + } + swap = (float*) temp1a - HCA_SAMPLES_PER_SUBFRAME; /* move spectra/temp to beginning */ + temp1a = temp2a; + temp2a = swap; + + count1a = count1a << 1; + count2a = count2a >> 1; + } + + temp1b = ch->temp; + temp2b = ch->spectra; + count1b = half; + count2b = 1; + for (i = 0; i < mdct_bits; i++) { + const float *sin_table = (const float *) decode5_sin_tables_int[i]; + const float *cos_table = (const float *) decode5_cos_tables_int[i]; + float *swap; + float *d1 = temp2b; + float *d2 = &temp2b[count2b * 2 - 1]; + const float *s1 = &temp1b[0]; + const float *s2 = &temp1b[count2b]; + + for (j = 0; j < count1b; j++) { + for (k = 0; k < count2b; k++) { + float a = *(s1++); + float b = *(s2++); + float sin = *(sin_table++); + float cos = *(cos_table++); + *(d1++) = a * sin - b * cos; + *(d2--) = a * cos + b * sin; + } + s1 += count2b; + s2 += count2b; + d1 += count2b; + d2 += count2b * 3; + } + swap = (float*) temp1b; + temp1b = temp2b; + temp2b = swap; + + count1b = count1b >> 1; + count2b = count2b << 1; + } + + /* copy dct */ + /* (with the above optimization spectra is already modified, so this is redundant) */ + for (i = 0; i < size; i++) { + ch->dct[i] = ch->spectra[i]; + } + } + + /* update output/imdct */ + { + unsigned int i; + + for (i = 0; i < half; i++) { + ch->wave[subframe][i] = decode5_imdct_window[i] * ch->dct[i + half] + ch->imdct_previous[i]; + ch->wave[subframe][i + half] = decode5_imdct_window[i + half] * ch->dct[size - 1 - i] - ch->imdct_previous[i + half]; + ch->imdct_previous[i] = decode5_imdct_window[size - 1 - i] * ch->dct[half - i - 1]; + ch->imdct_previous[i + half] = decode5_imdct_window[half - i - 1] * ch->dct[i]; + } +#if 0 + /* over-optimized IMDCT (for reference), barely noticeable even when decoding hundred of files */ + const float *imdct_window = decode5_imdct_window; + const float *dct; + float *imdct_previous; + float *wave = ch->wave[subframe]; + + dct = &ch->dct[half]; + imdct_previous = ch->imdct_previous; + for (i = 0; i < half; i++) { + *(wave++) = *(dct++) * *(imdct_window++) + *(imdct_previous++); + } + for (i = 0; i < half; i++) { + *(wave++) = *(imdct_window++) * *(--dct) - *(imdct_previous++); + } + /* implicit: imdct_window pointer is now at end */ + dct = &ch->dct[half - 1]; + imdct_previous = ch->imdct_previous; + for (i = 0; i < half; i++) { + *(imdct_previous++) = *(--imdct_window) * *(dct--); + } + for (i = 0; i < half; i++) { + *(imdct_previous++) = *(--imdct_window) * *(++dct) ; + } +#endif + } } diff --git a/src/coding/atrac9_decoder.c b/src/coding/atrac9_decoder.c index 16f11e46..39aa066e 100644 --- a/src/coding/atrac9_decoder.c +++ b/src/coding/atrac9_decoder.c @@ -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); - data->samples_to_discard = num_sample; - data->samples_to_discard += data->config.encoder_delay; + /* 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 - /* 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; } 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) diff --git a/src/coding/coding.h b/src/coding/coding.h index 3fa15733..22c08405 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -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 */ diff --git a/src/coding/hca_decoder.c b/src/coding/hca_decoder.c index 8288229f..cb4d30d8 100644 --- a/src/coding/hca_decoder.c +++ b/src/coding/hca_decoder.c @@ -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; + const unsigned int channels = data->info.channelCount; + const unsigned int blockSize = data->info.blockSize; - 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; + while (samples_done < 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); + if (data->samples_filled) { + int samples_to_get = data->samples_filled; - while ( samples_done < samples_to_do ) { - 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 (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 { + /* 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; + } + + /* mark consumed samples */ + data->samples_consumed += samples_to_get; + data->samples_filled -= samples_to_get; } - - if ( read_streamfile((uint8_t*) hca_data, data->start + address, blockSize, data->streamfile) != blockSize ) - break; - - if ( clHCA_Decode( hca, hca_data, blockSize, address ) < 0 ) - break; - - ++data->curblock; + else { + off_t offset = data->info.headerSize + data->current_block * blockSize; + int status; + size_t bytes; - 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; - } - else { - samples_remain -= data->samples_discard; - data->sample_ptr = data->samples_discard; - data->samples_discard = 0; - } - } + /* EOF/error */ + if (data->current_block >= data->info.blockCount) { + memset(outbuf, 0, (samples_to_do - samples_done) * channels * sizeof(sample)); + break; + } - 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; - } - - 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) - close_streamfile(data->streamfile); - free(data); - } + 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; } diff --git a/src/coding/SASSC_decoder.c b/src/coding/sassc_decoder.c similarity index 98% rename from src/coding/SASSC_decoder.c rename to src/coding/sassc_decoder.c index f3912286..59917bfd 100644 --- a/src/coding/SASSC_decoder.c +++ b/src/coding/sassc_decoder.c @@ -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; diff --git a/src/formats.c b/src/formats.c index ea243db4..f518e935 100644 --- a/src/formats.c +++ b/src/formats.c @@ -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"}, }; diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index e345af4e..3af9348f 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -272,10 +272,58 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -540,10 +588,14 @@ RelativePath=".\meta\his.c" > - - + + + + @@ -1751,7 +1803,7 @@ > + + + + + + + + + + + + @@ -249,7 +261,8 @@ - + + @@ -490,7 +503,7 @@ - + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index ea8d8e16..f4649354 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -169,6 +169,42 @@ meta\Source Files + + meta\Source Files + + + meta\Source Files + + + meta\Source Files + + + meta\Source Files + + + meta\Source Files + + + meta\Source Files + + + meta\Source Files + + + meta\Source Files + + + meta\Source Files + + + meta\Source Files + + + meta\Source Files + + + meta\Source Files + meta\Source Files @@ -334,7 +370,10 @@ meta\Source Files - + + meta\Source Files + + meta\Source Files @@ -1033,7 +1072,7 @@ coding\Source Files - + coding\Source Files diff --git a/src/meta/a2m.c b/src/meta/a2m.c new file mode 100644 index 00000000..93d41832 --- /dev/null +++ b/src/meta/a2m.c @@ -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; +} diff --git a/src/meta/adx.c b/src/meta/adx.c index 0dc09918..43ddd7fc 100644 --- a/src/meta/adx.c +++ b/src/meta/adx.c @@ -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; diff --git a/src/meta/adx_keys.h b/src/meta/adx_keys.h index d3556486..5f7603da 100644 --- a/src/meta/adx_keys.h +++ b/src/meta/adx_keys.h @@ -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[] = { diff --git a/src/meta/ahv.c b/src/meta/ahv.c new file mode 100644 index 00000000..6de9ad70 --- /dev/null +++ b/src/meta/ahv.c @@ -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; +} diff --git a/src/meta/aif_asobo.c b/src/meta/aif_asobo.c new file mode 100644 index 00000000..bccff3fd --- /dev/null +++ b/src/meta/aif_asobo.c @@ -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; +} diff --git a/src/meta/ao.c b/src/meta/ao.c new file mode 100644 index 00000000..eef74589 --- /dev/null +++ b/src/meta/ao.c @@ -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; +} diff --git a/src/meta/apc.c b/src/meta/apc.c new file mode 100644 index 00000000..25239be8 --- /dev/null +++ b/src/meta/apc.c @@ -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; +} diff --git a/src/meta/bcstm.c b/src/meta/bcstm.c index 0b5d89ef..87603d29 100644 --- a/src/meta/bcstm.c +++ b/src/meta/bcstm.c @@ -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); - 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; - default: - break; + /* 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++) { + /* 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; jchannels; 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: diff --git a/src/meta/bnk_sony.c b/src/meta/bnk_sony.c index f6c7a595..10e8f7a6 100644 --- a/src/meta/bnk_sony.c +++ b/src/meta/bnk_sony.c @@ -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; diff --git a/src/meta/hca.c b/src/meta/hca.c index fcd599be..90efd4f9 100644 --- a/src/meta/hca.c +++ b/src/meta/hca.c @@ -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 */ - } - - 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 */ - - f++; + /* best possible score */ + if (score == 1) { + break; } - - 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); + //;VGM_LOG("HCA: best key=%08x%08x (score=%i)\n", + // (uint32_t)((best_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(best_keycode & 0xFFFFFFFF), best_score); -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; } diff --git a/src/meta/idsp.c b/src/meta/idsp.c deleted file mode 100644 index c1fdca92..00000000 --- a/src/meta/idsp.c +++ /dev/null @@ -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;jchannels;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;ich[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;ich[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; -} diff --git a/src/meta/idsp_ie.c b/src/meta/idsp_ie.c new file mode 100644 index 00000000..30fccc32 --- /dev/null +++ b/src/meta/idsp_ie.c @@ -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; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index ab24b8f9..61eaa8e1 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -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*/ diff --git a/src/meta/msv.c b/src/meta/msv.c new file mode 100644 index 00000000..ff844b81 --- /dev/null +++ b/src/meta/msv.c @@ -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; +} diff --git a/src/meta/ngc_dsp_std.c b/src/meta/ngc_dsp_std.c index 7f1cdcdd..ddf4d34e 100644 --- a/src/meta/ngc_dsp_std.c +++ b/src/meta/ngc_dsp_std.c @@ -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,7 +780,8 @@ VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE *streamFile) { stream_size -= 0x14; /* remove padding */ stream_size -= dspm.start_offset; - dspm.interleave_last = (stream_size / dspm.channel_count) % dspm.interleave; + if (dspm.interleave) + dspm.interleave_last = (stream_size / dspm.channel_count) % dspm.interleave; } dspm.fix_looping = 1; @@ -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; +} diff --git a/src/meta/nub_idsp.c b/src/meta/nub_idsp.c new file mode 100644 index 00000000..8ece72d2 --- /dev/null +++ b/src/meta/nub_idsp.c @@ -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; +} diff --git a/src/meta/sdf.c b/src/meta/sdf.c new file mode 100644 index 00000000..1b298015 --- /dev/null +++ b/src/meta/sdf.c @@ -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; +} diff --git a/src/meta/svg.c b/src/meta/svg.c new file mode 100644 index 00000000..99aad344 --- /dev/null +++ b/src/meta/svg.c @@ -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; +} diff --git a/src/meta/vai.c b/src/meta/vai.c new file mode 100644 index 00000000..7539d7f3 --- /dev/null +++ b/src/meta/vai.c @@ -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; +} diff --git a/src/meta/vis.c b/src/meta/vis.c new file mode 100644 index 00000000..3ce35b71 --- /dev/null +++ b/src/meta/vis.c @@ -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; +} diff --git a/src/meta/wv2.c b/src/meta/wv2.c new file mode 100644 index 00000000..c9592ff6 --- /dev/null +++ b/src/meta/wv2.c @@ -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; +} diff --git a/src/meta/xau_konami.c b/src/meta/xau_konami.c new file mode 100644 index 00000000..b8c6eb67 --- /dev/null +++ b/src/meta/xau_konami.c @@ -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; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index 89e8558f..55604cfc 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -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) { diff --git a/src/vgmstream.h b/src/vgmstream.h index 8f179508..97eadaab 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -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 diff --git a/vgmstream.sln b/vgmstream.sln index 81710c7d..6d114c1e 100644 --- a/vgmstream.sln +++ b/vgmstream.sln @@ -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} diff --git a/vgmstream_full.sln b/vgmstream_full.sln index e013d2cb..f3e11274 100644 --- a/vgmstream_full.sln +++ b/vgmstream_full.sln @@ -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