Merge pull request #286 from bnnm/celt-dll

CELT DLLs
This commit is contained in:
Christopher Snowhill 2018-08-12 14:34:48 -07:00 committed by GitHub
commit 6ba3b684fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 2140 additions and 653 deletions

View File

@ -26,10 +26,18 @@ In the case of the foobar2000 component they are all bundled for convenience,
or you can get them here: https://github.com/kode54/vgmstream/tree/master/ext_libs
(bundled here: https://f.losno.co/vgmstream-win32-deps.zip, may not be latest).
Put ```libvorbis.dll```, ```libmpg123-0.dll```, ```libg7221_decode.dll```, ```libg719_decode.dll```,
```avcodec-vgmstream-58.dll```, ```avformat-vgmstream-58.dll```, ```avutil-vgmstream-56.dll```, ```swresample-vgmstream-3.dll```
and ```libatrac9.dll``` somewhere Windows can
find them.
Put the following files somewhere Windows can find them:
- `libvorbis.dll`
- `libmpg123-0.dll`
- `libg7221_decode.dll`
- `libg719_decode.dll`
- `avcodec-vgmstream-58.dll`
- `avformat-vgmstream-58.dll`
- `avutil-vgmstream-56.dll`
- `swresample-vgmstream-3.dll`
- `libatrac9.dll`
- `libcelt-0061.dll`
- `libcelt-0110.dll`
For Winamp/XMPlay/command line this means in the directory with the main .exe,
or in a system directory, or any other directory in the PATH variable.
@ -48,7 +56,7 @@ Options:
-c: loop forever (continuously)
-m: print metadata only, don't decode
-x: decode and print adxencd command line to encode as ADX
-g: decode and print oggenc command line to encode as OGG
2 -g: decode and print oggenc command line to encode as OGG
-b: decode and print batch variable commands
-L: append a smpl chunk and create a looping wav
-e: force end-to-end looping
@ -124,6 +132,7 @@ vgmstream (mainly to get looping in some cases).
- .ac3 to .lac3
- .aif to .aiffl or .aifcl
- .asf to .sng (EA formats)
- .flac to .lflac
- .mp4 to .lmp4
- .ogg to .logg
- .opus to .lopus
@ -192,16 +201,16 @@ are used in few games.
- Nintendo AFC ADPCM
- ITU-T G.721
- CD-ROM XA ADPCM
- Sony PSX ADPCM a.k.a VAG (standard, badflags, bmdx, configurable)
- Sony PSX ADPCM a.k.a VAG (standard, badflags, configurable)
- Sony HEVAG
- Electronic Arts EA-XA (stereo, mono, Maxis)
- Electronic Arts EA-XAS
- DVI/IMA ADPCM (stereo/mono + high/low nibble, 3DS, Omikron, SNDS, etc)
- Microsoft MS IMA ADPCM (standard, Xbox, NDS, Radical, Wwise, FSB, etc)
- Microsoft MS ADPCM
- Westwood VBR ADPCM
- Yamaha AICA ADPCM
- Procyon Studio ADPCM
- Microsoft MS IMA ADPCM (standard, Xbox, NDS, Radical, Wwise, FSB, WV6, etc)
- Microsoft MS ADPCM (standard, Cricket Audio)
- Westwood VBR ADPCM
- Yamaha AICA ADPCM
- Procyon Studio ADPCM
- Level-5 0x555 ADPCM
- Activision EXAKT SASSC DPCM
- lsf ADPCM
@ -215,6 +224,8 @@ are used in few games.
- CRI HCA
- Electronic Arts MicroTalk a.k.a. UTK or UMT
- FMOD FADPCM 4-bit ADPCM
- Konami XMD 4-bit ADPCM
- Argonaut ASF 4-bit ADPCM
- Xiph Vorbis (Ogg, FSB5, Wwise, OGL, Silicon Knights)
- MPEG MP1/2/3 (standard, AHX, XVAG, FSB, AWC, P3D, etc)
- ITU-T G.722.1 (Polycom Siren 7)
@ -230,6 +241,7 @@ are used in few games.
- Bink
- AC3/SPDIF
- Xiph Opus (Ogg, Switch)
- Xiph CELT (FSB)
- Musepack
- FLAC
- Others

View File

@ -87,6 +87,13 @@ ifeq ($(VGM_ENABLE_ATRAC9),1)
TARGET_EXT_LIBS += libatrac9.a
endif
VGM_ENABLE_CELT = 1
ifeq ($(VGM_ENABLE_CELT),1)
CFLAGS += -DVGM_USE_CELT
LDFLAGS += -lcelt-0061 -lcelt-0110
TARGET_EXT_LIBS += libcelt-0061.a libcelt-0110.a
endif
endif #if WIN32
export CFLAGS LDFLAGS

View File

@ -71,7 +71,7 @@
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../ext_libs/Getopt;../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;_DEBUG;_WINDOWS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@ -81,7 +81,7 @@
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;../ext_libs/libcelt-0061.lib;../ext_libs/libcelt-0110.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -97,7 +97,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>../ext_libs/Getopt;../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;NDEBUG;_WINDOWS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;NDEBUG;_WINDOWS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
@ -106,7 +106,7 @@
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<AdditionalDependencies>../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;../ext_libs/libcelt-0061.lib;../ext_libs/libcelt-0110.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<SubSystem>Console</SubSystem>

View File

@ -0,0 +1,131 @@
/* Copyright (c) 2007-2008 CSIRO
Copyright (c) 2007-2009 Xiph.Org Foundation
Copyright (c) 2008 Gregory Maxwell
Written by Jean-Marc Valin and Gregory Maxwell */
/**
@file celt.h
@brief Contains all the functions for encoding and decoding audio
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* this is a custom celt.h based on libcelt's, only importing and
* renaming certain functions, allowing multiple libcelts to coexist.
* See the original celt.h for extra info. */
#ifndef _CELT_FSB_H
#define _CELT_FSB_H
#include "celt_types.h"
#define EXPORT
/** No error */
#define CELT_OK 0
/** GET the bit-stream version for compatibility check */
#define CELT_GET_BITSTREAM_VERSION 2000
/** State of the decoder. One decoder state is needed for each stream.
It is initialised once at the beginning of the stream. Do *not*
re-initialise the state for every frame */
typedef struct CELT0061Decoder CELT0061Decoder;
typedef struct CELT0110Decoder CELT0110Decoder;
/** The mode contains all the information necessary to create an
encoder. Both the encoder and decoder need to be initialised
with exactly the same mode, otherwise the quality will be very
bad */
typedef struct CELT0061Mode CELT0061Mode;
typedef struct CELT0110Mode CELT0110Mode;
/* Mode calls */
/** Creates a new mode struct. This will be passed to an encoder or
decoder. The mode MUST NOT BE DESTROYED until the encoders and
decoders that use it are destroyed as well.
@param Fs Sampling rate (32000 to 96000 Hz)
@param channels Number of channels
@param frame_size Number of samples (per channel) to encode in each
packet (even values; 64 - 512)
@param error Returned error code (if NULL, no error will be returned)
@return A newly created mode
*/
EXPORT CELT0061Mode *celt_0061_mode_create(celt_int32 Fs, int channels, int frame_size, int *error);
EXPORT CELT0110Mode *celt_0110_mode_create(celt_int32 Fs, int frame_size, int *error);
/** Destroys a mode struct. Only call this after all encoders and
decoders using this mode are destroyed as well.
@param mode Mode to be destroyed
*/
EXPORT void celt_0061_mode_destroy(CELT0061Mode *mode);
EXPORT void celt_0110_mode_destroy(CELT0110Mode *mode);
/** Query information from a mode */
EXPORT int celt_0061_mode_info(const CELT0061Mode *mode, int request, celt_int32 *value);
EXPORT int celt_0110_mode_info(const CELT0110Mode *mode, int request, celt_int32 *value);
/* Decoder stuff */
/** Creates a new decoder state. Each stream needs its own decoder state (can't
be shared across simultaneous streams).
@param mode Contains all the information about the characteristics of the
stream (must be the same characteristics as used for the encoder)
@param channels Number of channels
@param error Returns an error code
@return Newly created decoder state.
*/
EXPORT CELT0061Decoder *celt_0061_decoder_create(const CELT0061Mode *mode);
EXPORT CELT0110Decoder *celt_0110_decoder_create_custom(const CELT0110Mode *mode, int channels, int *error);
/** Destroys a a decoder state.
@param st Decoder state to be destroyed
*/
EXPORT void celt_0061_decoder_destroy(CELT0061Decoder *st);
EXPORT void celt_0110_decoder_destroy(CELT0110Decoder *st);
/** Decodes a frame of audio.
@param st Decoder state
@param data Compressed data produced by an encoder
@param len Number of bytes to read from "data". This MUST be exactly the number
of bytes returned by the encoder. Using a larger value WILL NOT WORK.
@param pcm One frame (frame_size samples per channel) of decoded PCM will be
returned here in 16-bit PCM format (native endian).
@return Error code.
*/
EXPORT int celt_0061_decode(CELT0061Decoder *st, const unsigned char *data, int len, celt_int16 *pcm);
EXPORT int celt_0110_decode(CELT0110Decoder *st, const unsigned char *data, int len, celt_int16 *pcm, int frame_size);
#endif /*_CELT_FSB_H */

View File

@ -0,0 +1,140 @@
/* celt_types.h taken from libogg */
/********************************************************************
* *
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
* by the Xiph.Org Foundation http://www.xiph.org/ *
* *
********************************************************************
function: #ifdef jail to whip a few platforms into the UNIX ideal.
last mod: $Id: os_types.h 7524 2004-08-11 04:20:36Z conrad $
********************************************************************/
/**
@file celt_types.h
@brief CELT types
*/
#ifndef _CELT_TYPES_H
#define _CELT_TYPES_H
/* Use the real stdint.h if it's there (taken from Paul Hsieh's pstdint.h) */
#if (defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) || defined (HAVE_STDINT_H))
#include <stdint.h>
typedef int16_t celt_int16;
typedef uint16_t celt_uint16;
typedef int32_t celt_int32;
typedef uint32_t celt_uint32;
#elif defined(_WIN32)
# if defined(__CYGWIN__)
# include <_G_config.h>
typedef _G_int32_t celt_int32;
typedef _G_uint32_t celt_uint32;
typedef _G_int16 celt_int16;
typedef _G_uint16 celt_uint16;
# elif defined(__MINGW32__)
typedef short celt_int16;
typedef unsigned short celt_uint16;
typedef int celt_int32;
typedef unsigned int celt_uint32;
# elif defined(__MWERKS__)
typedef int celt_int32;
typedef unsigned int celt_uint32;
typedef short celt_int16;
typedef unsigned short celt_uint16;
# else
/* MSVC/Borland */
typedef __int32 celt_int32;
typedef unsigned __int32 celt_uint32;
typedef __int16 celt_int16;
typedef unsigned __int16 celt_uint16;
# endif
#elif defined(__MACOS__)
# include <sys/types.h>
typedef SInt16 celt_int16;
typedef UInt16 celt_uint16;
typedef SInt32 celt_int32;
typedef UInt32 celt_uint32;
#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */
# include <sys/types.h>
typedef int16_t celt_int16;
typedef u_int16_t celt_uint16;
typedef int32_t celt_int32;
typedef u_int32_t celt_uint32;
#elif defined(__BEOS__)
/* Be */
# include <inttypes.h>
typedef int16 celt_int16;
typedef u_int16 celt_uint16;
typedef int32_t celt_int32;
typedef u_int32_t celt_uint32;
#elif defined (__EMX__)
/* OS/2 GCC */
typedef short celt_int16;
typedef unsigned short celt_uint16;
typedef int celt_int32;
typedef unsigned int celt_uint32;
#elif defined (DJGPP)
/* DJGPP */
typedef short celt_int16;
typedef unsigned short celt_uint16;
typedef int celt_int32;
typedef unsigned int celt_uint32;
#elif defined(R5900)
/* PS2 EE */
typedef int celt_int32;
typedef unsigned celt_uint32;
typedef short celt_int16;
typedef unsigned short celt_uint16;
#elif defined(__SYMBIAN32__)
/* Symbian GCC */
typedef signed short celt_int16;
typedef unsigned short celt_uint16;
typedef signed int celt_int32;
typedef unsigned int celt_uint32;
#elif defined(CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
typedef short celt_int16;
typedef unsigned short celt_uint16;
typedef long celt_int32;
typedef unsigned long celt_uint32;
#elif defined(CONFIG_TI_C6X)
typedef short celt_int16;
typedef unsigned short celt_uint16;
typedef int celt_int32;
typedef unsigned int celt_uint32;
#else
/* Give up, take a reasonable guess */
typedef short celt_int16;
typedef unsigned short celt_uint16;
typedef int celt_int32;
typedef unsigned int celt_uint32;
#endif
#endif /* _CELT_TYPES_H */

View File

@ -35,7 +35,13 @@ libswresample.a: swresample-vgmstream-3.dll swresample-vgmstream-3.def
libatrac9.a: libatrac9.dll libatrac9.def
$(DLLTOOL) -D libatrac9.dll -d libatrac9.def -l libatrac9.a
libcelt-0061.a: libcelt-0061.dll libcelt-0061.def
$(DLLTOOL) -D libcelt-0061.dll -d libcelt-0061.def -l libcelt-0061.a
libcelt-0110.a: libcelt-0110.dll libcelt-0110.def
$(DLLTOOL) -D libcelt-0110.dll -d libcelt-0110.def -l libcelt-0110.a
clean:
$(RMF) libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libavcodec.a libavformat.a libavutil.a libswresample.a libatrac9.a
$(RMF) libvorbis.a libmpg123-0.a libg7221_decode.a libg719_decode.a libavcodec.a libavformat.a libavutil.a libswresample.a libatrac9.a libcelt-0061.a libcelt-0110.a
.PHONY: clean

View File

@ -95,15 +95,20 @@
<Command>lib /def:swresample-vgmstream-3.def /machine:x86 /out:swresample.lib</Command>
<Outputs>swresample.lib;swresample.exp;%(Outputs)</Outputs>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="libatrac9.def">
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">lib /def:libatrac9.def /machine:x86 /out:libatrac9.lib</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">lib /def:libatrac9.def /machine:x86 /out:libatrac9.lib</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Building library stub</Message>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Building library stub</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">libatrac9.lib;libatrac9.exp;%(Outputs)</Outputs>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">libatrac9.lib;libatrac9.exp;%(Outputs)</Outputs>
<Message>Building library stub</Message>
<Command>lib /def:libatrac9.def /machine:x86 /out:libatrac9.lib</Command>
<Outputs>libatrac9.lib;libatrac9.exp;%(Outputs)</Outputs>
</CustomBuild>
<CustomBuild Include="libcelt-0061.def">
<Message>Building library stub</Message>
<Command>lib /def:libcelt-0061.def /machine:x86 /out:libcelt-0061.lib</Command>
<Outputs>libcelt-0061.lib;libcelt-0061.exp;%(Outputs)</Outputs>
</CustomBuild>
<CustomBuild Include="libcelt-0110.def">
<Message>Building library stub</Message>
<Command>lib /def:libcelt-0110.def /machine:x86 /out:libcelt-0110.lib</Command>
<Outputs>libcelt-0110.lib;libcelt-0110.exp;%(Outputs)</Outputs>
</CustomBuild>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -0,0 +1,8 @@
LIBRARY libcelt-0061.dll
EXPORTS
celt_0061_decode
celt_0061_decoder_create
celt_0061_decoder_destroy
celt_0061_mode_create
celt_0061_mode_destroy
celt_0061_mode_info

BIN
ext_libs/libcelt-0061.dll Normal file

Binary file not shown.

View File

@ -0,0 +1,8 @@
LIBRARY libcelt-0110.dll
EXPORTS
celt_0110_decode
celt_0110_decoder_create_custom
celt_0110_decoder_destroy
celt_0110_mode_create
celt_0110_mode_destroy
celt_0110_mode_info

BIN
ext_libs/libcelt-0110.dll Normal file

Binary file not shown.

View File

@ -71,7 +71,7 @@
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../ext_includes;$(DependenciesDir)/WTL/Include;$(DependenciesDir)/foobar/foobar2000/SDK;$(DependenciesDir)/foobar/foobar2000/shared;$(DependenciesDir)/foobar/foobar2000;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@ -83,7 +83,7 @@
<AdditionalOptions>/d2notypeopt %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>$(DependenciesDir)/foobar/foobar2000/shared/shared.lib;../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>$(DependenciesDir)/foobar/foobar2000/shared/shared.lib;../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;../ext_libs/libcelt-0061.lib;../ext_libs/libcelt-0110.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(DependenciesDir)/foobar/foobar2000/shared/shared.lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<DelayLoadDLLs>%(DelayLoadDLLs)</DelayLoadDLLs>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -98,7 +98,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>../ext_includes;$(DependenciesDir)/WTL/Include;$(DependenciesDir)/foobar/foobar2000/SDK;$(DependenciesDir)/foobar/foobar2000/shared;$(DependenciesDir)/foobar/foobar2000;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
@ -109,7 +109,7 @@
<AdditionalOptions>/d2notypeopt</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>$(DependenciesDir)/foobar/foobar2000/shared/shared.lib;../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>$(DependenciesDir)/foobar/foobar2000/shared/shared.lib;../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;../ext_libs/libcelt-0061.lib;../ext_libs/libcelt-0110.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(DependenciesDir)/foobar/foobar2000/shared/shared.lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<DelayLoadDLLs>%(DelayLoadDLLs)</DelayLoadDLLs>

View File

@ -11,6 +11,7 @@ atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
atrac9_codec_data *data = NULL;
data = calloc(1, sizeof(atrac9_codec_data));
if (!data) goto fail;
data->handle = Atrac9GetHandle();
if (!data->handle) goto fail;
@ -41,6 +42,7 @@ atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
return data;
fail:
free_atrac9(data);
return NULL;
}

View File

@ -0,0 +1,239 @@
#include "coding.h"
#ifdef VGM_USE_CELT
#include "celt/celt_fsb.h"
#define FSB_CELT_0_06_1_VERSION 0x80000009 /* libcelt-0.6.1 */
#define FSB_CELT_0_11_0_VERSION 0x80000010 /* libcelt-0.6.1 */
#define FSB_CELT_SAMPLES_PER_FRAME 512
#define FSB_CELT_INTERNAL_SAMPLE_RATE 44100
#define FSB_CELT_MAX_DATA_SIZE 0x200 /* from 0x2e~0x172/1d0, all files are CBR though */
/* opaque struct */
struct celt_codec_data {
sample *buffer;
sample *sample_buffer;
size_t samples_filled; /* number of samples in the buffer */
size_t samples_used; /* number of samples extracted from the buffer */
int samples_to_discard;
int channel_mode;
celt_lib_t version;
void *mode_handle;
void *decoder_handle;
};
/* FSB CELT, frames with custom header and standard data (API info from FMOD DLLs).
* FMOD used various libcelt versions, thus some tweaks are needed for them to coexist. */
celt_codec_data *init_celt_fsb(int channels, celt_lib_t version) {
int error = 0, lib_version = 0;
celt_codec_data *data = NULL;
data = calloc(1, sizeof(celt_codec_data));
if (!data) goto fail;
data->channel_mode = channels; /* should be 1/2, or rejected by libcelt */
data->version = version;
switch(data->version) {
case CELT_0_06_1: /* older FSB4 (FMOD ~4.33) */
data->mode_handle = celt_0061_mode_create(FSB_CELT_INTERNAL_SAMPLE_RATE, data->channel_mode, FSB_CELT_SAMPLES_PER_FRAME, &error);
if (!data->mode_handle || error != CELT_OK) goto fail;
error = celt_0061_mode_info(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version);
if (error != CELT_OK || lib_version != FSB_CELT_0_06_1_VERSION) goto fail;
data->decoder_handle = celt_0061_decoder_create(data->mode_handle);
if (!data->decoder_handle) goto fail;
break;
case CELT_0_11_0: /* newer FSB4 (FMOD ~4.34), FSB5 */
data->mode_handle = celt_0110_mode_create(FSB_CELT_INTERNAL_SAMPLE_RATE, FSB_CELT_SAMPLES_PER_FRAME, &error); /* "custom" and not ok? */
if (!data->mode_handle || error != CELT_OK) goto fail;
error = celt_0110_mode_info(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version);
if (error != CELT_OK || lib_version != FSB_CELT_0_11_0_VERSION) goto fail;
data->decoder_handle = celt_0110_decoder_create_custom(data->mode_handle, data->channel_mode, &error);
if (!data->decoder_handle || error != CELT_OK) goto fail;
break;
default:
goto fail;
}
data->sample_buffer = calloc(sizeof(sample), data->channel_mode * FSB_CELT_SAMPLES_PER_FRAME);
if (!data->sample_buffer) goto fail;
/* there is ~128 samples of encoder delay, but FMOD DLLs don't discard it? */
return data;
fail:
free_celt_fsb(data);
return NULL;
}
void decode_celt_fsb(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
VGMSTREAMCHANNEL *stream = &vgmstream->ch[0];
celt_codec_data * data = vgmstream->codec_data;
int samples_done = 0;
while (samples_done < samples_to_do) {
if (data->samples_filled) { /* consume samples */
int samples_to_get = data->samples_filled;
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_used*channels,
samples_to_get*channels * sizeof(sample));
samples_done += samples_to_get;
}
/* mark consumed samples */
data->samples_used += samples_to_get;
data->samples_filled -= samples_to_get;
}
else { /* decode data */
int status;
uint8_t data_buffer[FSB_CELT_MAX_DATA_SIZE] = {0};
size_t bytes, frame_size;
data->samples_used = 0;
/* FSB DLLs do seem to check this fixed value */
if (read_32bitBE(stream->offset+0x00,stream->streamfile) != 0x17C30DF3) {
VGM_LOG("CELT: unknown frame ID at %lx\n",stream->offset);
goto decode_fail;
}
frame_size = read_32bitLE(stream->offset+0x04,stream->streamfile);
if (frame_size > FSB_CELT_MAX_DATA_SIZE) {
VGM_LOG("CELT: frame size %x bigger than expected\n", frame_size);
goto decode_fail;
}
/* read and decode one raw block and advance offsets */
bytes = read_streamfile(data_buffer,stream->offset+0x08, frame_size,stream->streamfile);
if (bytes != frame_size) {
VGM_LOG("CELT: read %x vs expected %x bytes at %lx\n", bytes,frame_size,stream->offset);
goto decode_fail;
}
switch(data->version) {
case CELT_0_06_1:
status = celt_0061_decode(data->decoder_handle, data_buffer,bytes, data->sample_buffer);
break;
case CELT_0_11_0:
status = celt_0110_decode(data->decoder_handle, data_buffer,bytes, data->sample_buffer, FSB_CELT_SAMPLES_PER_FRAME);
break;
default:
goto decode_fail;
}
if (status != CELT_OK) {
VGM_LOG("CELT: decode failed with %i at %lx\n", status,stream->offset);
goto decode_fail;
}
stream->offset += 0x04+0x04+frame_size;
data->samples_filled += FSB_CELT_SAMPLES_PER_FRAME;
}
}
return;
decode_fail:
/* on error just put some 0 samples */
VGM_LOG("CELT: decode fail at %lx, missing %i samples\n", stream->offset, (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels);
}
void reset_celt_fsb(VGMSTREAM *vgmstream) {
celt_codec_data *data = vgmstream->codec_data;
if (!data) return;
/* recreate decoder (mode should not change) */
switch(data->version) {
case CELT_0_06_1:
if (data->decoder_handle) celt_0061_decoder_destroy(data->decoder_handle);
data->decoder_handle = celt_0061_decoder_create(data->mode_handle);
if (!data->decoder_handle) goto fail;
break;
case CELT_0_11_0:
if (data->decoder_handle) celt_0110_decoder_destroy(data->decoder_handle);
data->decoder_handle = celt_0110_decoder_create_custom(data->mode_handle, data->channel_mode, NULL);
if (!data->decoder_handle) goto fail;
break;
default:
goto fail;
}
data->samples_used = 0;
data->samples_filled = 0;
data->samples_to_discard = 0;
return;
fail:
return; /* decode calls should fail... */
}
void seek_celt_fsb(VGMSTREAM *vgmstream, int32_t num_sample) {
celt_codec_data *data = vgmstream->codec_data;
if (!data) return;
reset_celt_fsb(vgmstream);
data->samples_to_discard = num_sample;
/* 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_celt_fsb(celt_codec_data *data) {
if (!data) return;
switch(data->version) {
case CELT_0_06_1:
if (data->decoder_handle) celt_0061_decoder_destroy(data->decoder_handle);
if (data->mode_handle) celt_0061_mode_destroy(data->mode_handle);
break;
case CELT_0_11_0:
if (data->decoder_handle) celt_0110_decoder_destroy(data->decoder_handle);
if (data->mode_handle) celt_0110_mode_destroy(data->mode_handle);
break;
default:
break;
}
free(data->sample_buffer);
free(data);
}
#endif

View File

@ -243,6 +243,15 @@ void free_atrac9(atrac9_codec_data *data);
size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data);
#endif
#ifdef VGM_USE_CELT
/* celt_fsb_decoder */
celt_codec_data *init_celt_fsb(int channels, celt_lib_t version);
void decode_celt_fsb(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels);
void reset_celt_fsb(VGMSTREAM *vgmstream);
void seek_celt_fsb(VGMSTREAM *vgmstream, int32_t num_sample);
void free_celt_fsb(celt_codec_data *data);
#endif
#ifdef VGM_USE_FFMPEG
/* ffmpeg_decoder */
ffmpeg_codec_data *init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);

View File

@ -9,11 +9,12 @@
static const char* extension_list[] = {
//"", /* vgmstream can plays extensionless files too, but plugins must accept them manually */
//"", /* vgmstream can play extensionless files too, but plugins must accept them manually */
"04sw",
"2dx9",
"2pfs",
"800",
//"aac", //common, also tri-Ace's
"aa3", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
@ -421,6 +422,7 @@ static const char* extension_list[] = {
"wpd",
"wsd",
"wsi",
"wua",
"wv2", //txth/reserved [Slave Zero (PC)]
"wv6",
"wve",
@ -601,6 +603,9 @@ static const coding_info coding_info_list[] = {
#ifdef VGM_USE_ATRAC9
{coding_ATRAC9, "ATRAC9"},
#endif
#ifdef VGM_USE_CELT
{coding_CELT_FSB, "Custom CELT"},
#endif
#ifdef VGM_USE_FFMPEG
{coding_FFmpeg, "FFmpeg"},
#endif
@ -695,7 +700,7 @@ static const meta_info meta_info_list[] = {
{meta_DSP_JETTERS, "Double DSP header stereo by _lr.dsp extension"},
{meta_DSP_MSS, "Double DSP header stereo by .mss extension"},
{meta_DSP_GCM, "Double DSP header stereo by .gcm extension"},
{meta_DSP_WII_IDSP, "Wii IDSP Double DSP header"},
{meta_IDSP_TT, "Traveller's Tales IDSP header"},
{meta_RSTM_SPM, "Nintendo RSTM header and .brstmspm extension"},
{meta_RAW, "assumed RAW PCM file by .raw extension"},
{meta_PS2_VAGi, "Sony VAG Interleaved header (VAGi)"},
@ -712,7 +717,7 @@ static const meta_info meta_info_list[] = {
{meta_EA_SCHL, "Electronic Arts SCHl header (variable)"},
{meta_EA_SCHL_fixed, "Electronic Arts SCHl header (fixed)"},
{meta_CAF, "tri-Crescendo CAF Header"},
{meta_PS2_VPK, "VPK Header"},
{meta_VPK, "SCE America VPK Header"},
{meta_GENH, "GENH generic header"},
{meta_DSP_SADB, "Procyon Studio SADB header"},
{meta_SADL, "Procyon Studio SADL header"},
@ -815,7 +820,9 @@ static const meta_info meta_info_list[] = {
{meta_FFXI_BGW, "BGW BGMStream header"},
{meta_FFXI_SPW, "SPW SeWave header"},
{meta_PS2_ASS, "ASS Header"},
{meta_IDSP, "IDSP Header"},
{meta_NUB_IDSP, "Namco NUB IDSP header"},
{meta_IDSP_NL, "Next Level IDSP header"},
{meta_IDSP_IE, "Inevitable Entertainment IDSP Header"},
{meta_UBI_JADE, "Ubisoft Jade RIFF header"},
{meta_PS2_SEG, "SEG (PS2) Header"},
{meta_XBOX_SEG, "SEG (XBOX) Header"},

View File

@ -11,12 +11,23 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) {
size_t file_size = get_streamfile_size(streamFile);
/* EOF reads: signal we have nothing and let the layout fail */
if (block_offset >= file_size) {
vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset;
vgmstream->current_block_samples = -1;
return;
}
while (block_offset < file_size) {
uint32_t id = read_32bitBE(block_offset+0x00,streamFile);
block_size = read_32bitLE(block_offset+0x04,streamFile);
if (block_size > 0x00F00000) /* BE in SAT, but one file may have both BE and LE chunks */
/* BE in SAT, but one file may have both BE and LE chunks [FIFA 98 (SAT): movie LE, audio BE] */
if (guess_endianness32bit(block_offset+0x04,streamFile))
block_size = read_32bitBE(block_offset+0x04,streamFile);
else
block_size = read_32bitLE(block_offset+0x04,streamFile);
block_header = 0;
@ -31,7 +42,8 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) {
else if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
block_header = 0x08;
}
else if (id == 0x00000000) {
else if (id == 0x00000000 || id == 0xFFFFFFFF || id == 0x31534E65) { /* EOF or "1SNe" */
vgmstream->current_block_samples = -1;
break;
}
@ -42,9 +54,13 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) {
block_offset += block_size;
}
VGM_LOG("of=%lx, next=%lx, data=%x\n", block_offset, block_offset + block_size, block_size);//getchar();
vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset + block_size;
vgmstream->current_block_size = block_size - block_header;
if (vgmstream->current_block_samples == -1)
return;
/* set new channel offsets and block sizes */

View File

@ -17,7 +17,7 @@ void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM *
while (samples_done < sample_count) {
int32_t samples_to_do = LAYER_BUF_SIZE;
int layer;
int layer, ch = 0;
if (samples_to_do > sample_count - samples_done)
samples_to_do = sample_count - samples_done;
@ -31,14 +31,15 @@ void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM *
for (l_ch = 0; l_ch < layer_channels; l_ch++) {
for (s = 0; s < samples_to_do; s++) {
size_t layer_sample = s*layer_channels + l_ch;
size_t buffer_sample = (samples_done+s)*vgmstream->channels + (layer*layer_channels+l_ch);
size_t buffer_sample = (samples_done+s)*vgmstream->channels + ch;
buffer[buffer_sample] = interleave_buf[layer_sample];
}
ch++;
}
}
samples_done += samples_to_do;
}
}

View File

@ -232,6 +232,14 @@
RelativePath=".\meta\ea_schl_streamfile.h"
>
</File>
<File
RelativePath=".\meta\fsb_interleave_streamfile.h"
>
</File>
<File
RelativePath=".\meta\fsb5_interleave_streamfile.h"
>
</File>
<File
RelativePath=".\meta\opus_interleave_streamfile.h"
>
@ -1055,7 +1063,7 @@
>
</File>
<File
RelativePath=".\meta\ps2_vag.c"
RelativePath=".\meta\vag.c"
>
</File>
<File
@ -1083,7 +1091,7 @@
>
</File>
<File
RelativePath=".\meta\ps2_vpk.c"
RelativePath=".\meta\vpk.c"
>
</File>
<File

View File

@ -59,7 +59,7 @@
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_G719;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;USE_ALLOCA;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_G719;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;USE_ALLOCA;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@ -73,7 +73,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_WIN32_WINNT=0x501;WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_G719;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;USE_ALLOCA;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_WIN32_WINNT=0x501;WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_G719;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;USE_ALLOCA;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
@ -100,6 +100,8 @@
<ClInclude Include="meta\bar_streamfile.h" />
<ClInclude Include="meta\ea_eaac_streamfile.h" />
<ClInclude Include="meta\ea_schl_streamfile.h" />
<ClInclude Include="meta\fsb_interleave_streamfile.h" />
<ClInclude Include="meta\fsb5_interleave_streamfile.h" />
<ClInclude Include="meta\ppst_streamfile.h" />
<ClInclude Include="meta\opus_interleave_streamfile.h" />
<ClInclude Include="meta\sqex_scd_streamfile.h" />
@ -118,6 +120,7 @@
<ItemGroup>
<ClCompile Include="coding\at3plus_decoder.c" />
<ClCompile Include="coding\atrac9_decoder.c" />
<ClCompile Include="coding\celt_fsb_decoder.c" />
<ClCompile Include="coding\coding_utils.c" />
<ClCompile Include="coding\ffmpeg_decoder.c" />
<ClCompile Include="coding\ffmpeg_decoder_utils_ea_xma.c" />
@ -356,14 +359,14 @@
<ClCompile Include="meta\ps2_svag_snk.c" />
<ClCompile Include="meta\ps2_tec.c" />
<ClCompile Include="meta\ps2_tk5.c" />
<ClCompile Include="meta\ps2_vag.c" />
<ClCompile Include="meta\vag.c" />
<ClCompile Include="meta\ps2_vas.c" />
<ClCompile Include="meta\ps2_vbk.c" />
<ClCompile Include="meta\ps2_vgs.c" />
<ClCompile Include="meta\ps2_vgv.c" />
<ClCompile Include="meta\ps2_vms.c" />
<ClCompile Include="meta\ps2_voi.c" />
<ClCompile Include="meta\ps2_vpk.c" />
<ClCompile Include="meta\vpk.c" />
<ClCompile Include="meta\ps2_wad.c" />
<ClCompile Include="meta\ps2_wb.c" />
<ClCompile Include="meta\ps2_xa2.c" />

View File

@ -80,6 +80,12 @@
<ClInclude Include="meta\ea_schl_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\fsb_interleave_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\fsb5_interleave_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\opus_interleave_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
@ -643,7 +649,7 @@
<ClCompile Include="meta\ps2_tk5.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_vag.c">
<ClCompile Include="meta\vag.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_vas.c">
@ -664,7 +670,7 @@
<ClCompile Include="meta\ps2_voi.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_vpk.c">
<ClCompile Include="meta\vpk.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_wad.c">
@ -1291,6 +1297,9 @@
<ClCompile Include="coding\atrac9_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\celt_fsb_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\bcstm.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

View File

@ -35,7 +35,7 @@ VGMSTREAM * init_vgmstream_bgw(STREAMFILE *streamFile) {
/*0x2c: unk (vol?) */
/*0x2d: unk (0x10?) */
channel_count = read_8bit(0x2e,streamFile);
block_align = read_8bit(0x2f,streamFile);
block_align = (uint8_t)read_8bit(0x2f,streamFile);
if (file_size != get_streamfile_size(streamFile))
goto fail;

View File

@ -184,7 +184,7 @@ static void set_ea_1snh_num_samples(STREAMFILE* streamFile, off_t start_offset,
else if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
block_header = 0x08;
}
else if (id == 0x00000000) {
else if (id == 0x00000000 || id == 0xFFFFFFFF || id == 0x31534E65) { /* EOF or "1SNe" */
break;
}
else if (id == 0x31534E6C) { /* "1SNl" loop point found */

View File

@ -8,7 +8,7 @@
#define EAAC_VERSION_V0 0x00 /* SNR/SNS */
#define EAAC_VERSION_V1 0x01 /* SPS */
#define EAAC_CODEC_NONE 0x00 /* internal 'codec not set' flag */
#define EAAC_CODEC_NONE 0x00 /* internal 'codec not set' */
#define EAAC_CODEC_RESERVED 0x01 /* not used/reserved? /MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */
#define EAAC_CODEC_PCM 0x02
#define EAAC_CODEC_EAXMA 0x03
@ -26,7 +26,7 @@
#define EAAC_FLAG_STREAMED 0x04
#define EAAC_BLOCKID0_DATA 0x00
#define EAAC_BLOCKID0_END 0x80
#define EAAC_BLOCKID0_END 0x80 /* maybe meant to be a bitflag? */
#define EAAC_BLOCKID1_HEADER 0x48 /* 'H' */
#define EAAC_BLOCKID1_DATA 0x44 /* 'D' */
@ -36,6 +36,8 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
static size_t get_snr_size(STREAMFILE *streamFile, off_t offset);
static VGMSTREAM *parse_s10a_header(STREAMFILE *streamFile, off_t offset, uint16_t target_index, off_t ast_offset);
/* .SNR+SNS - from EA latest games (~2008-2013), v0 header */
VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) {
VGMSTREAM * vgmstream = NULL;
@ -131,7 +133,7 @@ fail:
}
/* .SPS - from Frostbite engine games, v1 header */
VGMSTREAM * init_vgmstream_ea_sps_fb(STREAMFILE *streamFile) {
VGMSTREAM * init_vgmstream_ea_sps_fb(STREAMFILE *streamFile) { //todo remove in the future, use better extractors
VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0, header_offset = 0, sps_offset, max_offset;
@ -459,6 +461,30 @@ fail:
return NULL;
}
/* ************************************************************************* */
typedef struct {
int version;
int codec;
int channel_config;
int sample_rate;
int flags;
int streamed;
int channels;
int num_samples;
int loop_start;
int loop_end;
int loop_flag;
off_t stream_offset;
off_t loop_offset;
} eaac_header;
static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *streamData, eaac_header *eaac);
/* EA newest header from RwAudioCore (RenderWare?) / EAAudioCore library (still generated by sx.exe).
* Audio "assets" come in separate RAM headers (.SNR/SPH) and raw blocked streams (.SNS/SPS),
* or together in pseudoformats (.SNU, .SBR+.SBS banks, .AEMS, .MUS, etc).
@ -466,91 +492,108 @@ fail:
static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE* temp_streamFile = NULL;
int channel_count, loop_flag = 0, streamed, version, codec, channel_config, flags;
int32_t header1, header2, sample_rate, num_samples, loop_start = 0, loop_end = 0;
uint32_t header1, header2;
eaac_header eaac = {0};
/* EA SNR/SPH header */
/* 4 bits: version */
/* 4 bits: codec */
/* 6 bits: channel config */
/* 18 bits: sample rate */
/* 4 bits: flags */
/* 28 bits: number of samples */
header1 = read_32bitBE(header_offset + 0x00, streamHead);
header2 = read_32bitBE(header_offset + 0x04, streamHead);
version = (header1 >> 28) & 0x0F;
codec = (header1 >> 24) & 0x0F;
channel_config = (header1 >> 18) & 0x3F;
sample_rate = (header1 & 0x03FFFF); /* some Dead Space 2 (PC) uses 96000 */
flags = (header2 >> 28) & 0x0F; /* TODO: maybe even 3 bits and not 4? */
num_samples = (header2 & 0x0FFFFFFF);
/* rest is optional, depends on flags header used (ex. SNU and SPS may have bigger headers): */
/* 0x02: loop start sample, 0x00/04: nothing, 0x06: loop start sample and loop start block offset */
header1 = (uint32_t)read_32bitBE(header_offset + 0x00, streamHead);
header2 = (uint32_t)read_32bitBE(header_offset + 0x04, streamHead);
eaac.version = (header1 >> 28) & 0x0F; /* 4 bits */
eaac.codec = (header1 >> 24) & 0x0F; /* 4 bits */
eaac.channel_config = (header1 >> 18) & 0x3F; /* 6 bits */
eaac.sample_rate = (header1 & 0x03FFFF); /* 18 bits (some Dead Space 2 (PC) do use 96000) */
eaac.flags = (header2 >> 28) & 0x0F; /* 4 bits *//* TODO: maybe even 3 bits and not 4? */
eaac.num_samples = (header2 & 0x0FFFFFFF); /* 28 bits */
/* rest is optional, depends on used flags and codec (handled below) */
eaac.stream_offset = start_offset;
/* V0: SNR+SNS, V1: SPR+SPS (no apparent differences, other than the block flags used) */
if (version != EAAC_VERSION_V0 && version != EAAC_VERSION_V1) {
VGM_LOG("EA SNS/SPS: unknown version\n");
/* V0: SNR+SNS, V1: SPR+SPS (no apparent differences, other than block flags) */
if (eaac.version != EAAC_VERSION_V0 && eaac.version != EAAC_VERSION_V1) {
VGM_LOG("EA EAAC: unknown version\n");
goto fail;
}
if (flags != EAAC_FLAG_NONE &&
!(flags & (EAAC_FLAG_LOOPED | EAAC_FLAG_STREAMED))) {
VGM_LOG("EA SNS/SPS: unknown flags 0x%02x\n", flags);
/* catch unknown/garbage values just in case */
if (eaac.flags != EAAC_FLAG_NONE && !(eaac.flags & (EAAC_FLAG_LOOPED | EAAC_FLAG_STREAMED))) {
VGM_LOG("EA EAAC: unknown flags 0x%02x\n", eaac.flags);
goto fail;
}
/* TODO: Properly implement looping, needed for Need for Speed: World (PC) */
if (flags & EAAC_FLAG_LOOPED) {
loop_flag = 1;
loop_start = 0;
loop_end = num_samples;
}
/* Non-streamed sounds are stored as a single block */
streamed = (flags & EAAC_FLAG_STREAMED) != 0;
/* get loops (fairly involved due to the multiple layouts and mutant streamfiles)
* full loops aren't too uncommon [Dead Space (PC) sfx/ambiance],
* while actual looping is very rare [Need for Speed: World (PC)] */
if (eaac.flags & EAAC_FLAG_LOOPED) {
eaac.loop_flag = 1;
/* accepted channel configs only seem to be mono/stereo/quad/5.1/7.1 */
/* fail with unknown values just in case */
switch(channel_config) {
case 0x00: channel_count = 1; break;
case 0x01: channel_count = 2; break;
case 0x03: channel_count = 4; break;
case 0x05: channel_count = 6; break;
case 0x07: channel_count = 8; break;
default:
VGM_LOG("EA SNS/SPS: unknown channel config 0x%02x\n", channel_config);
eaac.loop_start = read_32bitBE(header_offset+0x08, streamHead);
eaac.loop_end = eaac.num_samples;
//todo header size always depends on flags or is implicit by loop values? see get_snr_size
if (eaac.loop_start > 0)
eaac.loop_offset = read_32bitBE(header_offset+0x0c, streamHead); /* normal loop */
else
eaac.loop_offset = eaac.stream_offset; /* full loop */
//todo EATrax has extra values in header, which would coexist with loop values
if (eaac.codec == EAAC_CODEC_EATRAX) {
VGM_LOG("EA EAAC: unknown loop header for EATrax\n");
goto fail;
}
//todo need more cases to test how layout/streamfiles react
if (eaac.loop_start > 0 && !(eaac.codec == EAAC_CODEC_EALAYER3_V1 ||
eaac.codec == EAAC_CODEC_EALAYER3_V2_PCM || eaac.codec == EAAC_CODEC_EALAYER3_V2_SPIKE)) {
VGM_LOG("EA EAAC: unknown actual looping for non-EALayer3\n");
goto fail;
}
}
/* Non-streamed sounds are stored as a single block (may not set block end flags) */
eaac.streamed = (eaac.flags & EAAC_FLAG_STREAMED) != 0;
/* accepted channel configs only seem to be mono/stereo/quad/5.1/7.1, from debug strings */
switch(eaac.channel_config) {
case 0x00: eaac.channels = 1; break;
case 0x01: eaac.channels = 2; break;
case 0x03: eaac.channels = 4; break;
case 0x05: eaac.channels = 6; break;
case 0x07: eaac.channels = 8; break;
default:
VGM_LOG("EA EAAC: unknown channel config 0x%02x\n", eaac.channel_config);
goto fail; /* fail with unknown values just in case */
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(eaac.channels,eaac.loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->sample_rate = eaac.sample_rate;
vgmstream->num_samples = eaac.num_samples;
vgmstream->loop_start_sample = eaac.loop_start;
vgmstream->loop_end_sample = eaac.loop_end;
vgmstream->meta_type = meta_type;
/* EA decoder list and known internal FourCCs */
switch(codec) {
switch(eaac.codec) {
case EAAC_CODEC_PCM: /* "P6B0": PCM16BE [NBA Jam (Wii)] */
case EAAC_CODEC_PCM: /* "P6B0": PCM16BE [NBA Jam (Wii)] */
vgmstream->coding_type = coding_PCM16_int;
vgmstream->codec_endian = 1;
vgmstream->layout_type = layout_blocked_ea_sns;
break;
#ifdef VGM_USE_FFMPEG
case EAAC_CODEC_EAXMA: { /* "EXm0": EA-XMA [Dante's Inferno (X360)] */
case EAAC_CODEC_EAXMA: { /* "EXm0": EA-XMA [Dante's Inferno (X360)] */
uint8_t buf[0x100];
int bytes, block_size, block_count;
size_t stream_size, virtual_size;
ffmpeg_custom_config cfg = {0};
stream_size = get_streamfile_size(streamData) - start_offset;
virtual_size = ffmpeg_get_eaxma_virtual_size(vgmstream->channels, streamed, start_offset,stream_size, streamData);
stream_size = get_streamfile_size(streamData) - eaac.stream_offset; //todo not correct for banks
virtual_size = ffmpeg_get_eaxma_virtual_size(vgmstream->channels, eaac.streamed, eaac.stream_offset,stream_size, streamData);
block_size = 0x10000; /* todo unused and not correctly done by the parser */
block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0);
@ -561,7 +604,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
cfg.virtual_size = virtual_size;
cfg.channels = vgmstream->channels;
vgmstream->codec_data = init_ffmpeg_config(streamData, buf,bytes, start_offset,stream_size, &cfg);
vgmstream->codec_data = init_ffmpeg_config(streamData, buf,bytes, eaac.stream_offset,stream_size, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
@ -570,46 +613,60 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
}
#endif
case EAAC_CODEC_XAS: /* "Xas1": EA-XAS [Dead Space (PC/PS3)] */
case EAAC_CODEC_XAS: /* "Xas1": EA-XAS [Dead Space (PC/PS3)] */
vgmstream->coding_type = coding_EA_XAS;
vgmstream->layout_type = layout_blocked_ea_sns;
break;
#ifdef VGM_USE_MPEG
case EAAC_CODEC_EALAYER3_V1: /* "EL31": EALayer3 v1 [Need for Speed: Hot Pursuit (PS3)] */
case EAAC_CODEC_EALAYER3_V2_PCM: /* "L32P": EALayer3 v2 "PCM" [Battlefield 1943 (PS3)] */
case EAAC_CODEC_EALAYER3_V2_SPIKE: { /* "L32S": EALayer3 v2 "Spike" [Dante's Inferno (PS3)] */
case EAAC_CODEC_EALAYER3_V1: /* "EL31": EALayer3 v1 [Need for Speed: Hot Pursuit (PS3)] */
case EAAC_CODEC_EALAYER3_V2_PCM: /* "L32P": EALayer3 v2 "PCM" [Battlefield 1943 (PS3)] */
case EAAC_CODEC_EALAYER3_V2_SPIKE: { /* "L32S": EALayer3 v2 "Spike" [Dante's Inferno (PS3)] */
mpeg_custom_config cfg = {0};
mpeg_custom_t type = (codec == 0x05 ? MPEG_EAL31b : (codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S);
mpeg_custom_t type = (eaac.codec == 0x05 ? MPEG_EAL31b : (eaac.codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S);
/* remove blocks on reads for some edge cases in L32P and to properly apply discard modes
* (otherwise, and removing discards, it'd work with layout_blocked_ea_sns) */
temp_streamFile = setup_eaac_streamfile(streamData, version, codec, streamed, start_offset, 0);
if (!temp_streamFile) goto fail;
/* EALayer3 needs custom IO that removes blocks on reads to fix some edge cases in L32P
* and to properly apply discard modes (see ealayer3 decoder)
* (otherwise, and after removing discard code, it'd work with layout_blocked_ea_sns) */
start_offset = 0x00; /* must point to the custom streamfile's beginning */
/* layout is still blocks, but should work fine with the custom mpeg decoder */
vgmstream->codec_data = init_mpeg_custom(temp_streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
if (eaac.loop_start > 0) { /* special (if hacky) loop handling, see comments */
segmented_layout_data *data = build_segmented_eaaudiocore_looping(streamData, &eaac);
if (!data) goto fail;
vgmstream->layout_data = data;
vgmstream->coding_type = data->segments[0]->coding_type;
vgmstream->layout_type = layout_segmented;
}
else {
temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed, eaac.stream_offset,0);
if (!temp_streamFile) goto fail;
vgmstream->codec_data = init_mpeg_custom(temp_streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
}
break;
}
#endif
case EAAC_CODEC_DSP: /* "Gca0"?: DSP [Need for Speed: Nitro sfx (Wii)] */
case EAAC_CODEC_DSP: /* "Gca0"?: DSP [Need for Speed: Nitro (Wii) sfx] */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_blocked_ea_sns;
/* DSP coefs are read in the blocks */
break;
#ifdef VGM_USE_ATRAC9
case EAAC_CODEC_EATRAX: { /* EATrax */
case EAAC_CODEC_EATRAX: { /* EATrax (unknown FourCC) [Need for Speed: Most Wanted (Vita)] */
atrac9_config cfg = {0};
size_t total_size;
cfg.channels = vgmstream->channels;
/* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */
start_offset = 0x00; /* must point to the custom streamfile's beginning */
cfg.channels = eaac.channels;
cfg.config_data = read_32bitBE(header_offset + 0x08,streamHead);
/* 0x10: frame size? (same as config data?) */
total_size = read_32bitLE(header_offset + 0x0c,streamHead); /* actual data size without blocks, LE b/c why make sense */
@ -619,11 +676,9 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none;
/* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */
temp_streamFile = setup_eaac_streamfile(streamData, version, codec, streamed, start_offset, total_size);
temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed, eaac.stream_offset, total_size);
if (!temp_streamFile) goto fail;
start_offset = 0x00; /* must point to the custom streamfile's beginning */
break;
}
#endif
@ -633,7 +688,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
case EAAC_CODEC_EAOPUS: /* EAOpus (inside each SNS/SPS block is 16b frame size + standard? Opus packet) */
/* TODO */
default:
VGM_LOG("EA SNS/SPS: unknown codec 0x%02x\n", codec);
VGM_LOG("EA EAAC: unknown codec 0x%02x\n", eaac.codec);
goto fail;
}
@ -660,3 +715,73 @@ static size_t get_snr_size(STREAMFILE *streamFile, off_t offset) {
default: return 0x08;
}
}
/* Actual looping uses 2 block sections, separated by a block end flag *and* padded.
*
* We use the segmented layout, since the eaac_streamfile doesn't handle padding properly ATM
* (getting EALayer3 frame sizes + skip sizes can be fairly involved), plus seems likely
* that after a block end the decoder needs to be reset (not possible from a streamfile).
*
* Or could fix the blocked_layout+L32P bug, though that involves a lot of rewrites.
* So this is the simplest, surest way ATM (if very ugly). */
// todo consider better ways to handle this once more looped files for other codecs are found
static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *streamData, eaac_header *eaac) {
segmented_layout_data *data = NULL;
STREAMFILE* temp_streamFile[2] = {0};
off_t offsets[2] = { eaac->stream_offset, eaac->loop_offset };
off_t sizes[2] = { eaac->stream_offset + eaac->loop_offset, 0}; /* 0 = let streamfile guess */
int num_samples[2] = { eaac->loop_start, eaac->num_samples - eaac->loop_start};
int segment_count = 2; /* intro/loop */
int i;
/* init layout */
data = init_layout_segmented(segment_count);
if (!data) goto fail;
for (i = 0; i < segment_count; i++) {
temp_streamFile[i] = setup_eaac_streamfile(streamData, eaac->version,eaac->codec,eaac->streamed, offsets[i], sizes[i]);
if (!temp_streamFile[i]) goto fail;
data->segments[i] = allocate_vgmstream(eaac->channels, 0);
if (!data->segments[i]) goto fail;
data->segments[i]->sample_rate = eaac->sample_rate;
data->segments[i]->num_samples = num_samples[i];
//data->segments[i]->meta_type = eaac->meta_type; /* bleh */
switch(eaac->codec) {
#ifdef VGM_USE_MPEG
case EAAC_CODEC_EALAYER3_V1:
case EAAC_CODEC_EALAYER3_V2_PCM:
case EAAC_CODEC_EALAYER3_V2_SPIKE: {
mpeg_custom_config cfg = {0};
mpeg_custom_t type = (eaac->codec == 0x05 ? MPEG_EAL31b : (eaac->codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S);
data->segments[i]->codec_data = init_mpeg_custom(temp_streamFile[i], 0x00, &data->segments[i]->coding_type, eaac->channels, type, &cfg);
if (!data->segments[i]->codec_data) goto fail;
data->segments[i]->layout_type = layout_none;
break;
}
#endif
default:
goto fail;
}
if (!vgmstream_open_stream(data->segments[i],temp_streamFile[i],0x00))
goto fail;
}
/* setup segmented VGMSTREAMs */
if (!setup_layout_segmented(data))
goto fail;
data->loop_segment = 1;
return data;
fail:
for (i = 0; i < segment_count; i++)
close_streamfile(temp_streamFile[i]);
free_layout_segmented(data);
return NULL;
}

View File

@ -1,13 +1,13 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "fsb_interleave_streamfile.h"
/* ************************************************************************************************************
/* ************************************************************************************************
* FSB defines, copied from the public spec (https://www.fmod.org/questions/question/forum-4928/)
* The format is mostly compatible for FSB1/2/3/4, but not FSB5. Headers always use LE. A FSB contains
* main header + sample header(s) + raw data. In multistreams N sample headers are stored (and
* if the BASICHEADERS flag is set, all headers but the first use HEADER_BASIC = numsamples + datasize)
* ************************************************************************************************************ */
* for reference. The format is mostly compatible for FSB1/2/3/4, but not FSB5.
* ************************************************************************************************ */
/* These flags are used for FMOD_FSB_HEADER::mode */
#define FMOD_FSB_SOURCE_FORMAT 0x00000001 /* all samples stored in their original compressed format */
#define FMOD_FSB_SOURCE_BASICHEADERS 0x00000002 /* samples should use the basic header structure */
@ -67,12 +67,13 @@
/* simplified struct based on the original definitions */
typedef enum { MPEG, IMA, PSX, XMA2, DSP, CELT, PCM8, PCM16 } fsb_codec_t;
typedef struct {
/* main header */
uint32_t id;
int32_t total_subsongs;
uint32_t sample_header_size; /* all of the sample headers including extended information */
uint32_t data_size;
uint32_t sample_headers_size; /* all of them including extended information */
uint32_t sample_data_size;
uint32_t version; /* extended fsb version (in FSB 3/3.1/4) */
uint32_t flags; /* flags common to all streams (in FSB 3/3.1/4)*/
/* sample header */
@ -86,25 +87,36 @@ typedef struct {
/* extra */
uint32_t base_header_size;
uint32_t sample_header_min;
off_t extradata_offset;
off_t first_extradata_offset;
meta_t meta_type;
off_t name_offset;
size_t name_size;
int loop_flag;
off_t stream_offset;
fsb_codec_t codec;
} fsb_header;
/* ********************************************************************************** */
/* FSB4 */
#ifdef VGM_USE_CELT
static layered_layout_data* build_layered_fsb_celt(STREAMFILE *streamFile, fsb_header* fsb, celt_lib_t version);
#endif
/* FSB1~4 - from games using FMOD audio middleware */
VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t custom_data_offset;
int loop_flag = 0;
int target_subsong = streamFile->stream_index;
fsb_header fsb = {0};
/* check extensions (.bnk = Hard Corps Uprising PS3) */
/* checks
* .fsb: standard
* .bnk: Hard Corps Uprising (PS3) */
if ( !check_extensions(streamFile, "fsb,bnk") )
goto fail;
@ -117,8 +129,8 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
/* main header */
fsb.total_subsongs = read_32bitLE(0x04,streamFile);
fsb.data_size = read_32bitLE(0x08,streamFile);
fsb.sample_header_size = 0x40;
fsb.sample_data_size = read_32bitLE(0x08,streamFile);
fsb.sample_headers_size = 0x40;
fsb.version = 0;
fsb.flags = 0;
@ -141,9 +153,11 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
fsb.channels = (fsb.mode & FSOUND_STEREO) ? 2 : 1;
if (fsb.loop_end > fsb.num_samples) /* this seems common... */
fsb.num_samples = fsb.loop_end;
start_offset = fsb.base_header_size + fsb.sample_header_size;
custom_data_offset = fsb.base_header_size + fsb.sample_header_min; /* DSP coefs, seek tables, etc */
/* DSP coefs, seek tables, etc */
fsb.extradata_offset = header_offset+fsb.sample_header_min;
fsb.stream_offset = fsb.base_header_size + fsb.sample_headers_size;
}
}
else { /* other FSBs (common/extended format) */
@ -165,12 +179,12 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
/* main header */
fsb.total_subsongs = read_32bitLE(0x04,streamFile);
fsb.sample_header_size = read_32bitLE(0x08,streamFile);
fsb.data_size = read_32bitLE(0x0c,streamFile);
fsb.sample_headers_size = read_32bitLE(0x08,streamFile);
fsb.sample_data_size = read_32bitLE(0x0c,streamFile);
if (fsb.base_header_size > 0x10) {
fsb.version = read_32bitLE(0x10,streamFile);
fsb.flags = read_32bitLE(0x14,streamFile);
/* FSB4: 0x18:hash 0x20:guid */
/* FSB4: 0x18(8):hash 0x20(10):guid */
} else {
fsb.version = 0;
fsb.flags = 0;
@ -184,7 +198,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
goto fail;
}
if (fsb.sample_header_size < fsb.sample_header_min) goto fail;
if (fsb.sample_headers_size < fsb.sample_header_min) goto fail;
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > fsb.total_subsongs || fsb.total_subsongs < 1) goto fail;
@ -192,7 +206,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
{
int i;
off_t header_offset = fsb.base_header_size;
off_t data_offset = fsb.base_header_size + fsb.sample_header_size;
off_t data_offset = fsb.base_header_size + fsb.sample_headers_size;
/* find target_stream header (variable sized) */
for (i = 0; i < fsb.total_subsongs; i++) {
@ -217,10 +231,18 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
fsb.loop_end = read_32bitLE(header_offset+0x2c,streamFile);
fsb.mode = read_32bitLE(header_offset+0x30,streamFile);
fsb.sample_rate = read_32bitLE(header_offset+0x34,streamFile);
/* 0x38:defvol 0x3a:defpan 0x3c:defpri */
/* 0x38: defvol, 0x3a: defpan, 0x3c: defpri */
fsb.channels = read_16bitLE(header_offset+0x3e,streamFile);
/* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsb.varpan */
/* FSB3/4: 0x50:extended_data size_32bits (not always given) */
/* FSB3.1/4:
* 0x40: mindistance, 0x44: maxdistance, 0x48: varfreq/size_32bits
* 0x4c: varvol, 0x4e: fsb.varpan */
/* DSP coefs, seek tables, etc */
if (stream_header_size > fsb.sample_header_min) {
fsb.extradata_offset = header_offset+fsb.sample_header_min;
if (fsb.first_extradata_offset == 0)
fsb.first_extradata_offset = fsb.extradata_offset;
}
}
if (i+1 == target_subsong) /* final data_offset found */
@ -236,36 +258,46 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
data_offset += 0x20 - (data_offset % 0x20);
}
}
if (i > fsb.total_subsongs) goto fail; /* not found */
if (i > fsb.total_subsongs)
goto fail; /* not found */
start_offset = data_offset;
custom_data_offset = header_offset + fsb.sample_header_min; /* DSP coefs, seek tables, etc */
fsb.stream_offset = data_offset;
}
}
/* XOR encryption for some FSB4, though the flag is only seen after decrypting */
//VGM_ASSERT(fsb.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n");
//;VGM_ASSERT(fsb.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n");
/* sometimes there is garbage at the end or missing bytes due to improper demuxing */
VGM_ASSERT(fsb.base_header_size + fsb.sample_header_size + fsb.data_size != streamFile->get_size(streamFile),
/* sometimes there is garbage at the end or missing bytes due to improper ripping */
VGM_ASSERT(fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size != streamFile->get_size(streamFile),
"FSB wrong head/data_size found (expected 0x%x vs 0x%x)\n",
fsb.base_header_size + fsb.sample_header_size + fsb.data_size, streamFile->get_size(streamFile));
fsb.base_header_size + fsb.sample_headers_size + fsb.sample_data_size, streamFile->get_size(streamFile));
/* Loops unless disabled. FMOD default seems to be full loops (0/num_samples-1) without flags, for repeating tracks
* that should loop and jingles/sfx that shouldn't. We'll try to disable looping if it looks jingly enough. */
loop_flag = !(fsb.mode & FSOUND_LOOP_OFF);
fsb.loop_flag = !(fsb.mode & FSOUND_LOOP_OFF);
if(!(fsb.mode & FSOUND_LOOP_NORMAL) /* rarely set */
&& fsb.loop_start+fsb.loop_end+1 == fsb.num_samples /* full loop */
&& fsb.num_samples < 20*fsb.sample_rate) /* in seconds (lame but no other way to know) */
loop_flag = 0;
fsb.loop_flag = 0;
/* ping-pong looping = no looping? (forward > reverse > forward) [ex. Biker Mice from Mars (PS2)] */
VGM_ASSERT(fsb.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n");
/* convert to clean some code */
if (fsb.mode & FSOUND_MPEG) fsb.codec = MPEG;
else if (fsb.mode & FSOUND_IMAADPCM) fsb.codec = IMA;
else if (fsb.mode & FSOUND_VAG) fsb.codec = PSX;
else if (fsb.mode & FSOUND_XMA) fsb.codec = XMA2;
else if (fsb.mode & FSOUND_GCADPCM) fsb.codec = DSP;
else if (fsb.mode & FSOUND_CELT) fsb.codec = CELT;
else if (fsb.mode & FSOUND_8BITS) fsb.codec = PCM8;
else fsb.codec = PCM16;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(fsb.channels,loop_flag);
vgmstream = allocate_vgmstream(fsb.channels,fsb.loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = fsb.sample_rate;
@ -278,98 +310,142 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
if (fsb.name_offset)
read_string(vgmstream->stream_name,fsb.name_size+1, fsb.name_offset,streamFile);
switch(fsb.codec) {
#ifdef VGM_USE_MPEG
case MPEG: { /* FSB4: Shatter (PS3), Way of the Samurai 3/4 (PS3) */
mpeg_custom_config cfg = {0};
/* parse codec */
if (fsb.mode & FSOUND_MPEG) { /* FSB4: Shatter (PS3), Way of the Samurai 3/4 (PS3) */
#if defined(VGM_USE_MPEG)
mpeg_custom_config cfg = {0};
cfg.fsb_padding = (vgmstream->channels > 2 ? 16 :
(fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 :
(fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0)));
cfg.fsb_padding = (vgmstream->channels > 2 ? 16 :
(fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 :
(fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0)));
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
//VGM_ASSERT(fsb.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */
VGM_ASSERT(fsb.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n");
#else
goto fail; /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */
#endif
}
else if (fsb.mode & FSOUND_IMAADPCM) { /* FSB3: Bioshock (PC), FSB4: Blade Kitten (PC) */
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
/* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 6ch)
* or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1) */
if (vgmstream->channels > 2 || (fsb.mode & FSOUND_MULTICHANNEL))
vgmstream->coding_type = coding_FSB_IMA;
/* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different
* (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */
}
else if (fsb.mode & FSOUND_VAG) { /* FSB1: Jurassic Park Operation Genesis (PS2), FSB4: Spider Man Web of Shadows (PSP) */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
if (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED) {
vgmstream->interleave_block_size = fsb.stream_size / fsb.channels;
}
else {
vgmstream->interleave_block_size = 0x10;
}
}
else if (fsb.mode & FSOUND_XMA) { /* FSB3: The Bourne Conspiracy 2008 (X360), FSB4: Armored Core V (X360), Hard Corps (X360) */
#if defined(VGM_USE_FFMPEG)
uint8_t buf[0x100];
size_t bytes, block_size, block_count;
block_size = 0x8000; /* FSB default */
block_count = fsb.stream_size / block_size; /* not accurate but not needed (custom_data_offset+0x14 -1?) */
bytes = ffmpeg_make_riff_xma2(buf, 0x100, fsb.num_samples, fsb.stream_size, fsb.channels, fsb.sample_rate, block_count, block_size);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,fsb.stream_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
#else
goto fail;
#endif
}
else if (fsb.mode & FSOUND_GCADPCM) { /* FSB3: Metroid Prime 3 (GC), FSB4: de Blob (Wii) */
if (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED) { /* [de Blob (Wii) sfx)] */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = fsb.stream_size / fsb.channels;
}
else {
vgmstream->coding_type = coding_NGC_DSP_subint;
vgmstream->codec_data = init_mpeg_custom(streamFile, fsb.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x2;
//VGM_ASSERT(fsb.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */
VGM_ASSERT(fsb.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); /* not seen */
break;
}
dsp_read_coefs_be(vgmstream, streamFile, custom_data_offset, 0x2e);
}
else if (fsb.mode & FSOUND_CELT) { /* FSB4: War Thunder (PC), The Witcher 2 (PC) */
VGM_LOG("FSB4: FSOUND_CELT found\n");
goto fail;
}
else if (fsb.mode & FSOUND_8BITS) { /* assumed, no games known */
vgmstream->coding_type = (fsb.mode & FSOUND_UNSIGNED) ? coding_PCM8_U : coding_PCM8;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x1;
}
else { /* (PCM16) FSB4: Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */
vgmstream->coding_type = (fsb.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x2;
#endif
/* sometimes FSOUND_MONO/FSOUND_STEREO is not set (ex. Dead Space iOS),
* or only STEREO/MONO but not FSOUND_8BITS/FSOUND_16BITS is set */
case IMA: /* FSB3: Bioshock (PC), FSB4: Blade Kitten (PC) */
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
/* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 6ch)
* or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1) */
if (vgmstream->channels > 2 || (fsb.mode & FSOUND_MULTICHANNEL))
vgmstream->coding_type = coding_FSB_IMA;
/* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different
* (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */
break;
case PSX: /* FSB1: Jurassic Park Operation Genesis (PS2), FSB4: Spider Man Web of Shadows (PSP) */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
if (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED) {
vgmstream->interleave_block_size = fsb.stream_size / fsb.channels;
}
else {
vgmstream->interleave_block_size = 0x10;
}
break;
#ifdef VGM_USE_FFMPEG
case XMA2: { /* FSB3: The Bourne Conspiracy 2008 (X360), FSB4: Armored Core V (X360), Hard Corps (X360) */
uint8_t buf[0x100];
size_t bytes, block_size, block_count;
block_size = 0x8000; /* FSB default */
block_count = fsb.stream_size / block_size; /* not accurate but not needed (custom_data_offset+0x14 -1?) */
bytes = ffmpeg_make_riff_xma2(buf,0x100, fsb.num_samples, fsb.stream_size, fsb.channels, fsb.sample_rate, block_count, block_size);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, fsb.stream_offset,fsb.stream_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
#endif
case DSP: /* FSB3: Metroid Prime 3 (GC), FSB4: de Blob (Wii) */
if (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED) { /* [de Blob (Wii) sfx)] */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = fsb.stream_size / fsb.channels;
}
else {
vgmstream->coding_type = coding_NGC_DSP_subint;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x2;
}
dsp_read_coefs_be(vgmstream, streamFile, fsb.extradata_offset, 0x2e);
break;
#ifdef VGM_USE_CELT
case CELT: { /* FSB4: War Thunder (PC), The Witcher 2 (PC), Vessel (PC) */
celt_lib_t version;
/* get libcelt version (set in the first subsong only, but try all extradata just in case) */
if (fsb.first_extradata_offset || fsb.extradata_offset) {
uint32_t lib = fsb.first_extradata_offset ?
(uint32_t)read_32bitLE(fsb.first_extradata_offset, streamFile) :
(uint32_t)read_32bitLE(fsb.extradata_offset, streamFile);;
switch(lib) {
case 0x80000009: version = CELT_0_06_1; break; /* War Thunder (PC) */
case 0x80000010: version = CELT_0_11_0; break; /* Vessel (PC) */
default: VGM_LOG("FSB: unknown CELT lib 0x%x\n", lib); goto fail;
}
}
else {
/* split FSBs? try to guess from observed bitstreams */
uint16_t frame = (uint16_t)read_16bitBE(fsb.stream_offset+0x04+0x04,streamFile);
if ((frame & 0xF000) == 0x6000 || frame == 0xFFFE) {
version = CELT_0_11_0;
} else {
version = CELT_0_06_1;
}
}
if (fsb.channels > 2) { /* multistreams */
vgmstream->layout_data = build_layered_fsb_celt(streamFile, &fsb, version);
if (!vgmstream->layout_data) goto fail;
vgmstream->coding_type = coding_CELT_FSB;
vgmstream->layout_type = layout_layered;
}
else {
vgmstream->codec_data = init_celt_fsb(vgmstream->channels, version);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_CELT_FSB;
vgmstream->layout_type = layout_none;
}
break;
}
#endif
case PCM8: /* assumed, no games known */
vgmstream->coding_type = (fsb.mode & FSOUND_UNSIGNED) ? coding_PCM8_U : coding_PCM8;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x1;
break;
case PCM16: /* (PCM16) FSB4: Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */
vgmstream->coding_type = (fsb.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x2;
/* sometimes FSOUND_MONO/FSOUND_STEREO is not set (ex. Dead Space iOS),
* or only STEREO/MONO but not FSOUND_8BITS/FSOUND_16BITS is set */
break;
default:
goto fail;
}
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
if ( !vgmstream_open_stream(vgmstream, streamFile, fsb.stream_offset) )
goto fail;
return vgmstream;
@ -378,6 +454,62 @@ fail:
return NULL;
}
#ifdef VGM_USE_CELT
static layered_layout_data* build_layered_fsb_celt(STREAMFILE *streamFile, fsb_header* fsb, celt_lib_t version) {
layered_layout_data* data = NULL;
STREAMFILE* temp_streamFile = NULL;
int i, layers = (fsb->channels+1) / 2;
/* init layout */
data = init_layout_layered(layers);
if (!data) goto fail;
/* open each layer subfile (1/2ch CELT streams: 2ch+2ch..+1ch or 2ch+2ch..+2ch) */
for (i = 0; i < layers; i++) {
int layer_channels = (i+1 == layers && fsb->channels % 2 == 1)
? 1 : 2; /* last layer can be 1/2ch */
/* build the layer VGMSTREAM */
data->layers[i] = allocate_vgmstream(layer_channels, fsb->loop_flag);
if (!data->layers[i]) goto fail;
data->layers[i]->sample_rate = fsb->sample_rate;
data->layers[i]->num_samples = fsb->num_samples;
data->layers[i]->loop_start_sample = fsb->loop_start;
data->layers[i]->loop_end_sample = fsb->loop_end;
#ifdef VGM_USE_CELT
data->layers[i]->codec_data = init_celt_fsb(layer_channels, version);
if (!data->layers[i]->codec_data) goto fail;
data->layers[i]->coding_type = coding_CELT_FSB;
data->layers[i]->layout_type = layout_none;
#else
goto fail;
#endif
temp_streamFile = setup_fsb_interleave_streamfile(streamFile, fsb->stream_offset, fsb->stream_size, layers, i, FSB_INT_CELT);
if (!temp_streamFile) goto fail;
if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) ) {
goto fail;
}
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
close_streamfile(temp_streamFile);
return data;
fail:
close_streamfile(temp_streamFile);
free_layout_layered(data);
return NULL;
}
#endif
/* ****************************************** */
static STREAMFILE* setup_fsb4_wav_streamfile(STREAMFILE *streamfile, off_t subfile_offset, size_t subfile_size);
@ -387,7 +519,7 @@ VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE *test_streamFile = NULL;
off_t subfile_start = 0x10;
size_t subfile_size = get_streamfile_size(streamFile) - 0x10 - 0x10; //todo
size_t subfile_size = get_streamfile_size(streamFile) - 0x10 - 0x10;
/* check extensions */
if ( !check_extensions(streamFile, "fsb,wii") )

View File

@ -1,19 +1,51 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../util.h"
#include "../layout/layout.h"
#include "fsb5_interleave_streamfile.h"
typedef struct {
int total_subsongs;
int version;
int codec;
int flags;
int channels;
int sample_rate;
int32_t num_samples;
int32_t loop_start;
int32_t loop_end;
int loop_flag;
off_t sample_header_offset;
size_t sample_header_size;
size_t name_table_size;
size_t sample_data_size;
size_t base_header_size;
off_t extradata_offset;
size_t extradata_size;
off_t stream_offset;
size_t stream_size;
off_t name_offset;
} fsb5_header;
/* ********************************************************************************** */
#ifdef VGM_USE_CELT
static layered_layout_data* build_layered_fsb5_celt(STREAMFILE *streamFile, fsb5_header* fsb5, celt_lib_t version);
#endif
static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE *streamFile, fsb5_header* fsb5, off_t configs_offset, size_t configs_size);
/* FSB5 - FMOD Studio multiplatform format */
VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t StartOffset = 0, NameOffset = 0;
off_t SampleHeaderStart = 0, ExtraInfoStart = 0;
size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0, ExtraInfoSize = 0;
uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0;
int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, Codec, Flags = 0;
int TotalSubsongs, TargetSubsong = streamFile->stream_index;
fsb5_header fsb5 = {0};
int target_subsong = streamFile->stream_index;
int i;
/* check extension, case insensitive */
if (!check_extensions(streamFile,"fsb"))
goto fail;
@ -22,125 +54,125 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
goto fail;
/* 0x00 is rare (seen in Tales from Space Vita) */
Version = read_32bitLE(0x04,streamFile);
if (Version != 0x00 && Version != 0x01) goto fail;
fsb5.version = read_32bitLE(0x04,streamFile);
if (fsb5.version != 0x00 && fsb5.version != 0x01) goto fail;
TotalSubsongs = read_32bitLE(0x08,streamFile);
SampleHeaderLength = read_32bitLE(0x0C,streamFile);
NameTableLength = read_32bitLE(0x10,streamFile);
SampleDataLength = read_32bitLE(0x14,streamFile);
Codec = read_32bitLE(0x18,streamFile);
fsb5.total_subsongs = read_32bitLE(0x08,streamFile);
fsb5.sample_header_size = read_32bitLE(0x0C,streamFile);
fsb5.name_table_size = read_32bitLE(0x10,streamFile);
fsb5.sample_data_size = read_32bitLE(0x14,streamFile);
fsb5.codec = read_32bitLE(0x18,streamFile);
/* version 0x01 - 0x1c(4): zero, 0x24(16): hash, 0x34(8): unk
* version 0x00 has an extra field (always 0?) at 0x1c */
if (Version == 0x01) {
Flags = read_32bitLE(0x20,streamFile); /* found by tests and assumed to be flags, no games known */
if (fsb5.version == 0x01) {
/* found by tests and assumed to be flags, no games known */
fsb5.flags = read_32bitLE(0x20,streamFile);
}
BaseHeaderLength = (Version==0x00) ? 0x40 : 0x3C;
fsb5.base_header_size = (fsb5.version==0x00) ? 0x40 : 0x3C;
if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile)) {
VGM_LOG("FSB5: bad size (%x + %x + %x + %x != %x)\n", SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, get_streamfile_size(streamFile));
if ((fsb5.sample_header_size + fsb5.name_table_size + fsb5.sample_data_size + fsb5.base_header_size) != get_streamfile_size(streamFile)) {
VGM_LOG("FSB5: bad size (%x + %x + %x + %x != %x)\n", fsb5.sample_header_size, fsb5.name_table_size, fsb5.sample_data_size, fsb5.base_header_size, get_streamfile_size(streamFile));
goto fail;
}
if (TargetSubsong == 0) TargetSubsong = 1;
if (TargetSubsong > TotalSubsongs || TotalSubsongs <= 0) goto fail;
if (target_subsong == 0) target_subsong = 1;
if (target_subsong > fsb5.total_subsongs || fsb5.total_subsongs <= 0) goto fail;
SampleHeaderStart = BaseHeaderLength;
fsb5.sample_header_offset = fsb5.base_header_size;
/* find target stream header and data offset, and read all needed values for later use
* (reads one by one as the size of a single stream header is variable) */
for (i = 1; i <= TotalSubsongs; i++) {
off_t DataStart = 0;
size_t StreamHeaderLength = 0;
uint32_t SampleMode1, SampleMode2; /* maybe one uint64? */
for (i = 1; i <= fsb5.total_subsongs; i++) {
size_t stream_header_size = 0;
off_t data_offset = 0;
uint32_t sample_mode1, sample_mode2; /* maybe one uint64? */
SampleMode1 = (uint32_t)read_32bitLE(SampleHeaderStart+0x00,streamFile);
SampleMode2 = (uint32_t)read_32bitLE(SampleHeaderStart+0x04,streamFile);
StreamHeaderLength += 0x08;
sample_mode1 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+0x00,streamFile);
sample_mode2 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+0x04,streamFile);
stream_header_size += 0x08;
/* get samples */
NumSamples = ((SampleMode2 >> 2) & 0x3FFFFFFF); /* bits2: 31..2 (30) */
fsb5.num_samples = ((sample_mode2 >> 2) & 0x3FFFFFFF); /* bits2: 31..2 (30) */
/* get offset inside data section */
/* up to 0x07FFFFFF * 0x20 = full 32b offset 0xFFFFFFE0 (recheck, after 0x80000000 some calcs may be off?) */
DataStart = ((SampleMode2 & 0x03) << 25) | ((SampleMode1 >> 7) & 0x1FFFFFF) << 5; /* bits2: 1..0 (2) | bits1: 31..8 (25) */
/* up to 0x07FFFFFF * 0x20 = full 32b offset 0xFFFFFFE0 */
data_offset = ((sample_mode2 & 0x03) << 25) | ((sample_mode1 >> 7) & 0x1FFFFFF) << 5; /* bits2: 1..0 (2) | bits1: 31..8 (25) */
/* get channels */
switch ((SampleMode1 >> 5) & 0x03) { /* bits1: 7..6 (2) */
case 0: ChannelCount = 1; break;
case 1: ChannelCount = 2; break;
case 2: ChannelCount = 6; break;/* some Dark Souls 2 MPEG; some IMA ADPCM */
case 3: ChannelCount = 8; break;/* some IMA ADPCM */
switch ((sample_mode1 >> 5) & 0x03) { /* bits1: 7..6 (2) */
case 0: fsb5.channels = 1; break;
case 1: fsb5.channels = 2; break;
case 2: fsb5.channels = 6; break; /* some Dark Souls 2 MPEG; some IMA ADPCM */
case 3: fsb5.channels = 8; break; /* some IMA ADPCM */
/* other channels (ex. 4/10/12ch) use 0 here + set extra flags */
default: /* not possible */
goto fail;
}
/* get sample rate */
switch ((SampleMode1 >> 1) & 0x0f) { /* bits1: 5..1 (4) */
case 0: SampleRate = 4000; break;
case 1: SampleRate = 8000; break;
case 2: SampleRate = 11000; break;
case 3: SampleRate = 11025; break;
case 4: SampleRate = 16000; break;
case 5: SampleRate = 22050; break;
case 6: SampleRate = 24000; break;
case 7: SampleRate = 32000; break;
case 8: SampleRate = 44100; break;
case 9: SampleRate = 48000; break;
case 10: SampleRate = 96000; break;
switch ((sample_mode1 >> 1) & 0x0f) { /* bits1: 5..1 (4) */
case 0: fsb5.sample_rate = 4000; break;
case 1: fsb5.sample_rate = 8000; break;
case 2: fsb5.sample_rate = 11000; break;
case 3: fsb5.sample_rate = 11025; break;
case 4: fsb5.sample_rate = 16000; break;
case 5: fsb5.sample_rate = 22050; break;
case 6: fsb5.sample_rate = 24000; break;
case 7: fsb5.sample_rate = 32000; break;
case 8: fsb5.sample_rate = 44100; break;
case 9: fsb5.sample_rate = 48000; break;
case 10: fsb5.sample_rate = 96000; break;
/* other sample rates (ex. 3000/64000/192000) use 0 here + set extra flags */
default: /* 11-15: rejected (FMOD error) */
goto fail;
}
/* get extra flags */
if (SampleMode1 & 0x01) { /* bits1: 0 (1) */
uint32_t ExtraFlag, ExtraFlagStart, ExtraFlagType, ExtraFlagSize, ExtraFlagEnd;
if (sample_mode1 & 0x01) { /* bits1: 0 (1) */
off_t extraflag_offset = fsb5.sample_header_offset+0x08;
uint32_t extraflag, extraflag_type, extraflag_size, extraflag_end;
ExtraFlagStart = SampleHeaderStart+0x08;
do {
ExtraFlag = read_32bitLE(ExtraFlagStart,streamFile);
ExtraFlagType = (ExtraFlag >> 25) & 0x7F; /* bits 32..26 (7) */
ExtraFlagSize = (ExtraFlag >> 1) & 0xFFFFFF; /* bits 25..1 (24)*/
ExtraFlagEnd = (ExtraFlag & 0x01); /* bit 0 (1) */
extraflag = read_32bitLE(extraflag_offset,streamFile);
extraflag_type = (extraflag >> 25) & 0x7F; /* bits 32..26 (7) */
extraflag_size = (extraflag >> 1) & 0xFFFFFF; /* bits 25..1 (24)*/
extraflag_end = (extraflag & 0x01); /* bit 0 (1) */
switch(ExtraFlagType) {
switch(extraflag_type) {
case 0x01: /* channels */
ChannelCount = read_8bit(ExtraFlagStart+0x04,streamFile);
fsb5.channels = read_8bit(extraflag_offset+0x04,streamFile);
break;
case 0x02: /* sample rate */
SampleRate = read_32bitLE(ExtraFlagStart+0x04,streamFile);
fsb5.sample_rate = read_32bitLE(extraflag_offset+0x04,streamFile);
break;
case 0x03: /* loop info */
LoopStart = read_32bitLE(ExtraFlagStart+0x04,streamFile);
if (ExtraFlagSize > 0x04) /* probably not needed */
LoopEnd = read_32bitLE(ExtraFlagStart+0x08,streamFile);
fsb5.loop_start = read_32bitLE(extraflag_offset+0x04,streamFile);
if (extraflag_size > 0x04) /* probably not needed */
fsb5.loop_end = read_32bitLE(extraflag_offset+0x08,streamFile);
/* when start is 0 seems the song repeats with no real looping (ex. Sonic Boom Fire & Ice jingles) */
LoopFlag = (LoopStart != 0x00);
fsb5.loop_flag = (fsb5.loop_start != 0x00);
break;
case 0x04: /* free comment, or maybe SFX info */
break;
//case 0x05: /* Unknown (32b) */
//case 0x05: /* Unknown (32b) */ //todo multistream marker?
// /* found in Tearaway Vita, value 0, first stream only */
// break;
case 0x06: /* XMA seek table */
/* no need for it */
break;
case 0x07: /* DSP coefs */
ExtraInfoStart = ExtraFlagStart + 0x04;
fsb5.extradata_offset = extraflag_offset + 0x04;
break;
case 0x09: /* ATRAC9 config */
ExtraInfoStart = ExtraFlagStart + 0x04;
ExtraInfoSize = ExtraFlagSize;
fsb5.extradata_offset = extraflag_offset + 0x04;
fsb5.extradata_size = extraflag_size;
break;
case 0x0a: /* XWMA config */
ExtraInfoStart = ExtraFlagStart + 0x04;
fsb5.extradata_offset = extraflag_offset + 0x04;
break;
case 0x0b: /* Vorbis setup ID and seek table */
ExtraInfoStart = ExtraFlagStart + 0x04;
fsb5.extradata_offset = extraflag_offset + 0x04;
/* seek table format:
* 0x08: table_size (total_entries = seek_table_size / (4+4)), not counting this value; can be 0
* 0x0C: sample number (only some samples are saved in the table)
@ -152,73 +184,78 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
// /* found in some XMA2/Vorbis/FADPCM */
// break;
default:
VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x + 0x04 (size 0x%x)\n", ExtraFlagType, ExtraFlagStart, ExtraFlagSize);
VGM_LOG("FSB5: unknown extraflag 0x%x at %lx + 0x04 (size 0x%x)\n", extraflag_type, extraflag_offset, extraflag_size);
break;
}
ExtraFlagStart += 0x04 + ExtraFlagSize;
StreamHeaderLength += 0x04 + ExtraFlagSize;
} while (ExtraFlagEnd != 0x00);
extraflag_offset += 0x04 + extraflag_size;
stream_header_size += 0x04 + extraflag_size;
} while (extraflag_end != 0x00);
}
/* stream found */
if (i == TargetSubsong) {
StartOffset = BaseHeaderLength + SampleHeaderLength + NameTableLength + DataStart;
if (i == target_subsong) {
fsb5.stream_offset = fsb5.base_header_size + fsb5.sample_header_size + fsb5.name_table_size + data_offset;
/* get stream size from next stream or datasize if there is only one */
if (i == TotalSubsongs) {
StreamSize = SampleDataLength - DataStart;
} else {
uint32_t NextSampleMode = (uint32_t)read_32bitLE(SampleHeaderStart+StreamHeaderLength+0x00,streamFile);
StreamSize = (((NextSampleMode >> 7) & 0x00FFFFFF) << 5) - DataStart;
/* get stream size from next stream offset or full size if there is only one */
if (i == fsb5.total_subsongs) {
fsb5.stream_size = fsb5.sample_data_size - data_offset;
}
else {
off_t next_data_offset;
uint32_t next_sample_mode1, next_sample_mode2;
next_sample_mode1 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+stream_header_size+0x00,streamFile);
next_sample_mode2 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+stream_header_size+0x04,streamFile);
next_data_offset = ((next_sample_mode2 & 0x03) << 25) | ((next_sample_mode1 >> 7) & 0x1FFFFFF) << 5;
fsb5.stream_size = next_data_offset - data_offset;
}
break;
}
/* continue searching */
SampleHeaderStart += StreamHeaderLength;
fsb5.sample_header_offset += stream_header_size;
}
/* target stream not found*/
if (!StartOffset || !StreamSize) goto fail;
if (!fsb5.stream_offset || !fsb5.stream_size) goto fail;
/* get stream name */
if (NameTableLength) {
NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetSubsong-1),streamFile);
if (fsb5.name_table_size) {
off_t name_suboffset = fsb5.base_header_size + fsb5.sample_header_size + 0x04*(target_subsong-1);
fsb5.name_offset = fsb5.base_header_size + fsb5.sample_header_size + read_32bitLE(name_suboffset,streamFile);
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(ChannelCount,LoopFlag);
vgmstream = allocate_vgmstream(fsb5.channels,fsb5.loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = SampleRate;
vgmstream->num_samples = NumSamples;
if (LoopFlag) {
vgmstream->loop_start_sample = LoopStart;
vgmstream->loop_end_sample = LoopEnd;
vgmstream->sample_rate = fsb5.sample_rate;
vgmstream->num_samples = fsb5.num_samples;
if (fsb5.loop_flag) {
vgmstream->loop_start_sample = fsb5.loop_start;
vgmstream->loop_end_sample = fsb5.loop_end;
}
vgmstream->num_streams = TotalSubsongs;
vgmstream->stream_size = StreamSize;
vgmstream->num_streams = fsb5.total_subsongs;
vgmstream->stream_size = fsb5.stream_size;
vgmstream->meta_type = meta_FSB5;
if (NameOffset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, NameOffset,streamFile);
if (fsb5.name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, fsb5.name_offset,streamFile);
/* parse codec */
switch (Codec) {
switch (fsb5.codec) {
case 0x00: /* FMOD_SOUND_FORMAT_NONE */
goto fail;
case 0x01: /* FMOD_SOUND_FORMAT_PCM8 [Anima - Gate of Memories (PC)] */
vgmstream->coding_type = coding_PCM8_U;
vgmstream->layout_type = ChannelCount == 1 ? layout_none : layout_interleave;
vgmstream->layout_type = fsb5.channels == 1 ? layout_none : layout_interleave;
vgmstream->interleave_block_size = 0x01;
break;
case 0x02: /* FMOD_SOUND_FORMAT_PCM16 [Shantae Risky's Revenge (PC)] */
vgmstream->coding_type = (Flags & 0x01) ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = ChannelCount == 1 ? layout_none : layout_interleave;
vgmstream->coding_type = (fsb5.flags & 0x01) ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = fsb5.channels == 1 ? layout_none : layout_interleave;
vgmstream->interleave_block_size = 0x02;
break;
@ -232,23 +269,22 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
case 0x05: /* FMOD_SOUND_FORMAT_PCMFLOAT [Anima: Gate of Memories (PC)] */
vgmstream->coding_type = coding_PCMFLOAT;
vgmstream->layout_type = (ChannelCount == 1) ? layout_none : layout_interleave;
vgmstream->layout_type = (fsb5.channels == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = 0x04;
break;
case 0x06: /* FMOD_SOUND_FORMAT_GCADPCM [Sonic Boom: Fire and Ice (3DS)] */
if (Flags & 0x02) { /* non-interleaved mode */
if (fsb5.flags & 0x02) { /* non-interleaved mode */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = (StreamSize / ChannelCount);
vgmstream->interleave_block_size = (fsb5.stream_size / fsb5.channels);
}
else {
vgmstream->coding_type = coding_NGC_DSP_subint;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x02;
}
dsp_read_coefs_be(vgmstream,streamFile,ExtraInfoStart,0x2E);
dsp_read_coefs_be(vgmstream,streamFile,fsb5.extradata_offset,0x2E);
break;
case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM [Skylanders] */
@ -259,8 +295,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
case 0x08: /* FMOD_SOUND_FORMAT_VAG [from fsbankex tests, no known games] */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
if (Flags & 0x02) { /* non-interleaved mode */
vgmstream->interleave_block_size = (StreamSize / ChannelCount);
if (fsb5.flags & 0x02) { /* non-interleaved mode */
vgmstream->interleave_block_size = (fsb5.stream_size / fsb5.channels);
}
else {
vgmstream->interleave_block_size = 0x10;
@ -279,10 +315,10 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
int bytes, block_size, block_count;
block_size = 0x8000; /* FSB default */
block_count = StreamSize / block_size + (StreamSize % block_size ? 1 : 0);
block_count = fsb5.stream_size / block_size + (fsb5.stream_size % block_size ? 1 : 0);
bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, StreamSize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, StartOffset,StreamSize);
bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, fsb5.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, fsb5.stream_offset,fsb5.stream_size);
if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
@ -296,43 +332,68 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : 4); /* observed default */
vgmstream->codec_data = init_mpeg_custom(streamFile, StartOffset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
vgmstream->codec_data = init_mpeg_custom(streamFile, fsb5.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
break;
}
#endif
case 0x0C: /* FMOD_SOUND_FORMAT_CELT [BIT.TRIP Presents Runner2 (PC), Full Bore (PC)] */
VGM_LOG("FSB5: FMOD_SOUND_FORMAT_CELT found\n");
goto fail;
#ifdef VGM_USE_CELT
case 0x0C: { /* FMOD_SOUND_FORMAT_CELT [BIT.TRIP Presents Runner2 (PC), Full Bore (PC)] */
int is_multistream = fsb5.channels > 2;
if (is_multistream) {
vgmstream->layout_data = build_layered_fsb5_celt(streamFile, &fsb5, CELT_0_11_0);
if (!vgmstream->layout_data) goto fail;
vgmstream->coding_type = coding_CELT_FSB;
vgmstream->layout_type = layout_layered;
}
else {
vgmstream->codec_data = init_celt_fsb(vgmstream->channels, CELT_0_11_0);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_CELT_FSB;
vgmstream->layout_type = layout_none;
}
break;
}
#endif
#ifdef VGM_USE_ATRAC9
case 0x0D: {/* FMOD_SOUND_FORMAT_AT9 */
atrac9_config cfg = {0};
int is_multistream;
off_t configs_offset = fsb5.extradata_offset;
size_t configs_size = fsb5.extradata_size;
cfg.channels = vgmstream->channels;
switch(ExtraInfoSize) {
case 0x04: /* Little Big Planet 2ch (Vita), Guacamelee (Vita) */
cfg.config_data = read_32bitBE(ExtraInfoStart,streamFile);
break;
case 0x08: /* Day of the Tentacle Remastered (Vita) */
/* 0x00: superframe size (also in config_data) */
cfg.config_data = read_32bitBE(ExtraInfoStart+0x04,streamFile);
break;
//case 0x0c: /* Little Big Planet 6ch (Vita) */
// //todo: this is just 0x04 x3, in case of 4ch would be 0x08 --must improve detection
// //each stream has its own config_data (but seem to be the same), interleaves 1 super frame per stream
// break;
default:
VGM_LOG("FSB5: unknown extra info size 0x%x\n", ExtraInfoSize);
goto fail;
/* skip frame size in newer FSBs [Day of the Tentacle Remastered (Vita), Tearaway Unfolded (PS4)] */
if (configs_size >= 0x08 && (uint8_t)read_8bit(configs_offset, streamFile) != 0xFE) { /* ATRAC9 sync */
configs_offset += 0x04;
configs_size -= 0x04;
}
//cfg.encoder_delay = 0x100; //todo not used? num_samples seems to count all data
vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none;
is_multistream = (configs_size / 0x04) > 1;
if (is_multistream) {
/* multichannel made of various streams [Little Big Planet (Vita)] */
vgmstream->layout_data = build_layered_fsb5_atrac9(streamFile, &fsb5, configs_offset, configs_size);
if (!vgmstream->layout_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_layered;
}
else {
/* standard ATRAC9, can be multichannel [Tearaway Unfolded (PS4)] */
atrac9_config cfg = {0};
cfg.channels = vgmstream->channels;
cfg.config_data = read_32bitBE(configs_offset,streamFile);
//cfg.encoder_delay = 0x100; //todo not used? num_samples seems to count all data
vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_none;
}
break;
}
#endif
@ -342,14 +403,14 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
uint8_t buf[0x100];
int bytes, format, average_bps, block_align;
format = read_16bitBE(ExtraInfoStart+0x00,streamFile);
block_align = (uint16_t)read_16bitBE(ExtraInfoStart+0x02,streamFile);
average_bps = (uint32_t)read_32bitBE(ExtraInfoStart+0x04,streamFile);
format = read_16bitBE(fsb5.extradata_offset+0x00,streamFile);
block_align = (uint16_t)read_16bitBE(fsb5.extradata_offset+0x02,streamFile);
average_bps = (uint32_t)read_32bitBE(fsb5.extradata_offset+0x04,streamFile);
/* rest: seek entries + mini seek table? */
/* XWMA encoder only does up to 6ch (doesn't use FSB multistreams for more) */
bytes = ffmpeg_make_riff_xwma(buf,0x100, format, StreamSize, vgmstream->channels, vgmstream->sample_rate, average_bps, block_align);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, StartOffset,StreamSize);
bytes = ffmpeg_make_riff_xwma(buf,0x100, format, fsb5.stream_size, vgmstream->channels, vgmstream->sample_rate, average_bps, block_align);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, fsb5.stream_offset,fsb5.stream_size);
if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
@ -363,11 +424,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
cfg.channels = vgmstream->channels;
cfg.sample_rate = vgmstream->sample_rate;
cfg.setup_id = read_32bitLE(ExtraInfoStart,streamFile);
cfg.setup_id = read_32bitLE(fsb5.extradata_offset,streamFile);
vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_VORBIS_custom;
vgmstream->codec_data = init_vorbis_custom(streamFile, StartOffset, VORBIS_FSB, &cfg);
vgmstream->codec_data = init_vorbis_custom(streamFile, fsb5.stream_offset, VORBIS_FSB, &cfg);
if (!vgmstream->codec_data) goto fail;
break;
@ -381,11 +442,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
break;
default:
VGM_LOG("FSB5: unknown codec %x found\n", Codec);
VGM_LOG("FSB5: unknown codec %x found\n", fsb5.codec);
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,StartOffset))
if (!vgmstream_open_stream(vgmstream,streamFile,fsb5.stream_offset))
goto fail;
return vgmstream;
@ -394,3 +455,144 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
#ifdef VGM_USE_CELT
static layered_layout_data* build_layered_fsb5_celt(STREAMFILE *streamFile, fsb5_header* fsb5, celt_lib_t version) {
layered_layout_data* data = NULL;
STREAMFILE* temp_streamFile = NULL;
int i, layers = (fsb5->channels+1) / 2;
size_t interleave;
if (read_32bitBE(fsb5->stream_offset+0x00,streamFile) != 0x17C30DF3) /* FSB CELT frame ID */
goto fail;
interleave = 0x04+0x04+read_32bitLE(fsb5->stream_offset+0x04,streamFile); /* frame size */
//todo unknown interleave for max quality odd channel streams (found in test files)
/* FSB5 odd channels use 2ch+2ch...+1ch streams, and the last only goes up to 0x17a, and other
* streams only use that max (doesn't happen for smaller frames, even channels, or FSB4)
* however streams other than the last seem to be padded with 0s somehow and wont work */
if (interleave > 0x17a && (fsb5->channels % 2 == 1))
interleave = 0x17a;
/* init layout */
data = init_layout_layered(layers);
if (!data) goto fail;
/* open each layer subfile (1/2ch CELT streams: 2ch+2ch..+1ch or 2ch+2ch..+2ch) */
for (i = 0; i < layers; i++) {
int layer_channels = (i+1 == layers && fsb5->channels % 2 == 1)
? 1 : 2; /* last layer can be 1/2ch */
/* build the layer VGMSTREAM */
data->layers[i] = allocate_vgmstream(layer_channels, fsb5->loop_flag);
if (!data->layers[i]) goto fail;
data->layers[i]->sample_rate = fsb5->sample_rate;
data->layers[i]->num_samples = fsb5->num_samples;
data->layers[i]->loop_start_sample = fsb5->loop_start;
data->layers[i]->loop_end_sample = fsb5->loop_end;
#ifdef VGM_USE_CELT
data->layers[i]->codec_data = init_celt_fsb(layer_channels, version);
if (!data->layers[i]->codec_data) goto fail;
data->layers[i]->coding_type = coding_CELT_FSB;
data->layers[i]->layout_type = layout_none;
#else
goto fail;
#endif
temp_streamFile = setup_fsb5_interleave_streamfile(streamFile, fsb5->stream_offset, fsb5->stream_size, layers, i, FSB5_INT_CELT, interleave);
if (!temp_streamFile) goto fail;
if (!vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00))
goto fail;
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
close_streamfile(temp_streamFile);
return data;
fail:
close_streamfile(temp_streamFile);
free_layout_layered(data);
return NULL;
}
#endif
static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE *streamFile, fsb5_header* fsb5, off_t configs_offset, size_t configs_size) {
layered_layout_data* data = NULL;
STREAMFILE* temp_streamFile = NULL;
int i, layers = (configs_size / 0x04);
size_t interleave = 0;
/* init layout */
data = init_layout_layered(layers);
if (!data) goto fail;
/* open each layer subfile (2ch+2ch..+1/2ch) */
for (i = 0; i < layers; i++) {
uint32_t config = read_32bitBE(configs_offset + 0x04*i, streamFile);
int channel_index, layer_channels;
size_t frame_size;
/* parse ATRAC9 config (see VGAudio docs) */
channel_index = ((config >> 17) & 0x7);
frame_size = (((config >> 5) & 0x7FF) + 1) * (1 << ((config >> 3) & 0x2)); /* frame size * superframe index */
if (channel_index > 2)
goto fail; /* only 1/2ch expected */
layer_channels = (channel_index==0) ? 1 : 2;
if (interleave == 0)
interleave = frame_size;
//todo in test files with 2ch+..+1ch interleave is off (uses some strange padding)
/* build the layer VGMSTREAM */
data->layers[i] = allocate_vgmstream(layer_channels, fsb5->loop_flag);
if (!data->layers[i]) goto fail;
data->layers[i]->sample_rate = fsb5->sample_rate;
data->layers[i]->num_samples = fsb5->num_samples;
data->layers[i]->loop_start_sample = fsb5->loop_start;
data->layers[i]->loop_end_sample = fsb5->loop_end;
#ifdef VGM_USE_ATRAC9
{
atrac9_config cfg = {0};
cfg.channels = layer_channels;
cfg.config_data = config;
//cfg.encoder_delay = 0x100; //todo not used? num_samples seems to count all data
data->layers[i]->codec_data = init_atrac9(&cfg);
if (!data->layers[i]->codec_data) goto fail;
data->layers[i]->coding_type = coding_ATRAC9;
data->layers[i]->layout_type = layout_none;
}
#else
goto fail;
#endif
temp_streamFile = setup_fsb5_interleave_streamfile(streamFile, fsb5->stream_offset, fsb5->stream_size, layers, i, FSB5_INT_ATRAC9, interleave);
if (!temp_streamFile) goto fail;
if (!vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00))
goto fail;
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
close_streamfile(temp_streamFile);
return data;
fail:
close_streamfile(temp_streamFile);
free_layout_layered(data);
return NULL;
}

View File

@ -0,0 +1,201 @@
#ifndef _FSB5_INTERLEAVE_STREAMFILE_H_
#define _FSB5_INTERLEAVE_STREAMFILE_H_
#include "../streamfile.h"
typedef enum { FSB5_INT_CELT, FSB5_INT_ATRAC9 } fsb_interleave_codec_t;
typedef struct {
/* state */
off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */
int skip_frames; /* frames to skip from other streams at points */
/* config */
fsb_interleave_codec_t codec;
size_t interleave;
int stream_count;
int stream_number;
size_t stream_size;
off_t start_offset; /* pointing to the stream's beginning */
size_t total_size; /* size of the resulting substream */
} fsb_interleave_io_data;
/* Reads skipping other streams */
static size_t fsb_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_interleave_io_data* data) {
size_t total_read = 0;
/* ignore bad reads */
if (offset < 0 || offset > data->total_size) {
return total_read;
}
/* previous offset: re-start as we can't map logical<>physical offsets
* (kinda slow as it trashes buffers, but shouldn't happen often) */
if (offset < data->logical_offset) {
data->physical_offset = data->start_offset;
data->logical_offset = 0x00;
data->skip_frames = data->stream_number;
}
/* read doing one frame at a time */
while (length > 0) {
size_t to_read, bytes_read;
off_t intrablock_offset, intradata_offset;
uint32_t data_size;
if (offset >= data->total_size)
break;
/* get current data */
switch (data->codec) {
case FSB5_INT_CELT:
case FSB5_INT_ATRAC9:
data_size = data->interleave;
break;
default:
return 0;
}
/* skip frames from other streams */
if (data->skip_frames) {
data->physical_offset += data_size;
data->skip_frames--;
continue;
}
/* requested offset is outside current block, try next */
if (offset >= data->logical_offset + data_size) {
data->physical_offset += data_size;
data->logical_offset += data_size;
data->skip_frames = data->stream_count - 1;
continue;
}
/* reads could fall in the middle of the block */
intradata_offset = offset - data->logical_offset;
intrablock_offset = intradata_offset;
/* clamp reads up to this block's end */
to_read = (data_size - intradata_offset);
if (to_read > length)
to_read = length;
if (to_read == 0)
break; /* should never happen... */
/* finally read and move buffer/offsets */
bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile);
total_read += bytes_read;
if (bytes_read != to_read)
break; /* couldn't read fully */
dest += bytes_read;
offset += bytes_read;
length -= bytes_read;
/* block fully read, go next */
if (intradata_offset + bytes_read == data_size) {
data->physical_offset += data_size;
data->logical_offset += data_size;
data->skip_frames = data->stream_count - 1;
}
}
return total_read;
}
static size_t fsb_interleave_io_size(STREAMFILE *streamfile, fsb_interleave_io_data* data) {
off_t physical_offset, max_physical_offset;
size_t total_size = 0;
int skip_frames = 0;
if (data->total_size)
return data->total_size;
physical_offset = data->start_offset;
max_physical_offset = physical_offset + data->stream_size;
skip_frames = data->stream_number;
/* get size of the underlying stream, skipping other streams
* (all streams should have the same frame count) */
while (physical_offset < max_physical_offset) {
size_t data_size;
data_size = data->interleave;
switch(data->codec) {
case FSB5_INT_CELT:
case FSB5_INT_ATRAC9:
data_size = data->interleave;
break;
default:
return 0;
}
/* there may be padding at the end, so this doubles as EOF marker */
if (data_size == 0)
break;
if (physical_offset+data_size > max_physical_offset)
break;
/* skip frames from other streams */
if (skip_frames) {
physical_offset += data_size;
skip_frames--;
continue;
}
physical_offset += data_size;
total_size += data_size;
skip_frames = data->stream_count - 1;
}
data->total_size = total_size;
return data->total_size;
}
/* Prepares custom IO for multistreams, interleaves 1 packet per stream */
static STREAMFILE* setup_fsb5_interleave_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t stream_size, int stream_count, int stream_number, fsb_interleave_codec_t codec, size_t interleave) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
fsb_interleave_io_data io_data = {0};
size_t io_data_size = sizeof(fsb_interleave_io_data);
io_data.start_offset = start_offset;
io_data.physical_offset = start_offset;
io_data.skip_frames = stream_number; /* adjust since start_offset points to the first */
io_data.codec = codec;
io_data.interleave = interleave;
io_data.stream_count = stream_count;
io_data.stream_number = stream_number;
io_data.stream_size = stream_size; /* full size for all streams */
io_data.total_size = fsb_interleave_io_size(streamFile, &io_data); /* force init, size of a single stream */
if (io_data.total_size == 0 || io_data.total_size > io_data.stream_size) {
VGM_LOG("FSB5 INTERLEAVE: wrong total_size %x vs %x\n", io_data.total_size,io_data.stream_size);
goto fail;
}
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_interleave_io_read,fsb_interleave_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _FSB_INTERLEAVE_STREAMFILE_H_ */

View File

@ -0,0 +1,198 @@
#ifndef _FSB_INTERLEAVE_STREAMFILE_H_
#define _FSB_INTERLEAVE_STREAMFILE_H_
#include "../streamfile.h"
typedef enum { FSB_INT_CELT } fsb_interleave_codec_t;
typedef struct {
/* state */
off_t logical_offset; /* offset that corresponds to physical_offset */
off_t physical_offset; /* actual file offset */
int skip_frames; /* frames to skip from other streams at points */
/* config */
fsb_interleave_codec_t codec;
int stream_count;
int stream_number;
size_t stream_size;
off_t start_offset; /* pointing to the stream's beginning */
size_t total_size; /* size of the resulting substream */
} fsb_interleave_io_data;
/* Reads skipping other streams */
static size_t fsb_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, fsb_interleave_io_data* data) {
size_t total_read = 0;
/* ignore bad reads */
if (offset < 0 || offset > data->total_size) {
return total_read;
}
/* previous offset: re-start as we can't map logical<>physical offsets
* (kinda slow as it trashes buffers, but shouldn't happen often) */
if (offset < data->logical_offset) {
data->physical_offset = data->start_offset;
data->logical_offset = 0x00;
data->skip_frames = data->stream_number;
}
/* read doing one frame at a time */
while (length > 0) {
size_t to_read, bytes_read;
off_t intrablock_offset, intradata_offset;
uint32_t data_size;
if (offset >= data->total_size)
break;
/* get current data */
switch (data->codec) {
case FSB_INT_CELT:
data_size = 0x04+0x04+read_32bitLE(data->physical_offset+0x04,streamfile);
break;
default:
return 0;
}
/* skip frames from other streams */
if (data->skip_frames) {
data->physical_offset += data_size;
data->skip_frames--;
continue;
}
/* requested offset is outside current block, try next */
if (offset >= data->logical_offset + data_size) {
data->physical_offset += data_size;
data->logical_offset += data_size;
data->skip_frames = data->stream_count - 1;
continue;
}
/* reads could fall in the middle of the block */
intradata_offset = offset - data->logical_offset;
intrablock_offset = intradata_offset;
/* clamp reads up to this block's end */
to_read = (data_size - intradata_offset);
if (to_read > length)
to_read = length;
if (to_read == 0)
break; /* should never happen... */
/* finally read and move buffer/offsets */
bytes_read = read_streamfile(dest, data->physical_offset + intrablock_offset, to_read, streamfile);
total_read += bytes_read;
if (bytes_read != to_read)
break; /* couldn't read fully */
dest += bytes_read;
offset += bytes_read;
length -= bytes_read;
/* block fully read, go next */
if (intradata_offset + bytes_read == data_size) {
data->physical_offset += data_size;
data->logical_offset += data_size;
data->skip_frames = data->stream_count - 1;
}
}
return total_read;
}
static size_t fsb_interleave_io_size(STREAMFILE *streamfile, fsb_interleave_io_data* data) {
off_t physical_offset, max_physical_offset;
size_t total_size = 0;
int skip_frames = 0;
if (data->total_size)
return data->total_size;
physical_offset = data->start_offset;
max_physical_offset = physical_offset + data->stream_size;
skip_frames = data->stream_number;
/* get size of the underlying stream, skipping other streams
* (all streams should have the same frame count) */
while (physical_offset < max_physical_offset) {
size_t data_size;
uint32_t id;
switch(data->codec) {
case FSB_INT_CELT:
id = read_32bitBE(physical_offset+0x00,streamfile);
if (id != 0x17C30DF3) /* incorrect FSB CELT frame sync */
data_size = 0;
else
data_size = 0x04+0x04+read_32bitLE(physical_offset+0x04,streamfile);
break;
default:
return 0;
}
/* there may be padding at the end, so this doubles as EOF marker */
if (data_size == 0)
break;
/* skip frames from other streams */
if (skip_frames) {
physical_offset += data_size;
skip_frames--;
continue;
}
physical_offset += data_size;
total_size += data_size;
skip_frames = data->stream_count - 1;
}
data->total_size = total_size;
return data->total_size;
}
/* Prepares custom IO for multistreams, interleaves 1 packet per stream */
static STREAMFILE* setup_fsb_interleave_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t stream_size, int stream_count, int stream_number, fsb_interleave_codec_t codec) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
fsb_interleave_io_data io_data = {0};
size_t io_data_size = sizeof(fsb_interleave_io_data);
io_data.start_offset = start_offset;
io_data.physical_offset = start_offset;
io_data.skip_frames = stream_number; /* adjust since start_offset points to the first */
io_data.codec = codec;
io_data.stream_count = stream_count;
io_data.stream_number = stream_number;
io_data.stream_size = stream_size;
io_data.total_size = fsb_interleave_io_size(streamFile, &io_data); /* force init */
if (io_data.total_size == 0 || io_data.total_size > io_data.stream_size) {
VGM_LOG("FSB INTERLEAVE: wrong total_size %x vs %x\n", io_data.total_size,io_data.stream_size);
goto fail;
}
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, fsb_interleave_io_read,fsb_interleave_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_buffer_streamfile(new_streamFile,0);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _FSB_INTERLEAVE_STREAMFILE_H_ */

View File

@ -1,11 +1,9 @@
#include "meta.h"
#include "../util.h"
/* "idsp/IDSP"
Soul Calibur Legends (Wii)
Sky Crawlers: Innocent Aces (Wii)
*/
VGMSTREAM * init_vgmstream_idsp2(STREAMFILE *streamFile) {
//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;
@ -18,8 +16,8 @@ VGMSTREAM * init_vgmstream_idsp2(STREAMFILE *streamFile) {
if (strcasecmp("idsp",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x69647370 || /* "idsp" */
read_32bitBE(0xBC,streamFile) != 0x49445350) /* IDSP */
if (read_32bitBE(0x00,streamFile) != 0x69647370 || /* "idsp" */
read_32bitBE(0xBC,streamFile) != 0x49445350) /* "IDSP" */
goto fail;
loop_flag = read_32bitBE(0x20,streamFile);
@ -56,7 +54,7 @@ VGMSTREAM * init_vgmstream_idsp2(STREAMFILE *streamFile) {
}
}
vgmstream->meta_type = meta_IDSP;
vgmstream->meta_type = meta_NUB_IDSP;
{
if (vgmstream->coding_type == coding_NGC_DSP) {
@ -94,9 +92,8 @@ fail:
return NULL;
}
/* IDSP (Mario Strikers Charged)
- Single "IDSP" header... */
VGMSTREAM * init_vgmstream_idsp3(STREAMFILE *streamFile) {
/* IDSP - from Next Level games [Mario Strikers Charged (Wii)] */
VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag = 1;
@ -133,7 +130,7 @@ VGMSTREAM * init_vgmstream_idsp3(STREAMFILE *streamFile) {
vgmstream->interleave_last_block_size = ((vgmstream->num_samples/7*8)%(vgmstream->interleave_block_size)/vgmstream->channels);
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_IDSP;
vgmstream->meta_type = meta_IDSP_NL;
if (vgmstream->coding_type == coding_NGC_DSP) {
int i;
@ -170,8 +167,8 @@ fail:
return NULL;
}
/* IDSP (Defender NGC) */
VGMSTREAM * init_vgmstream_idsp4(STREAMFILE *streamFile) {
/* 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;
@ -212,7 +209,7 @@ VGMSTREAM * init_vgmstream_idsp4(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = read_32bitBE(0x10,streamFile);
}
vgmstream->meta_type = meta_IDSP;
vgmstream->meta_type = meta_IDSP_IE;
{
int i;

View File

@ -260,7 +260,7 @@ VGMSTREAM * init_vgmstream_dc_idvi(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_rnd(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_wii_idsp(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_kraw(STREAMFILE *streamFile);
@ -268,9 +268,9 @@ VGMSTREAM * init_vgmstream_ps2_omu(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_xa2(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_idsp2(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_idsp3(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_idsp4(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);

View File

@ -597,17 +597,18 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile) {
dspm.start_offset = 0xc0;
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strlen(filename) > 7 && !strcasecmp("_lr.dsp",filename+strlen(filename)-7)) {
if (strlen(filename) > 7 && !strcasecmp("_lr.dsp",filename+strlen(filename)-7)) { //todo improve
dspm.interleave = 0x14180;
dspm.meta_type = meta_DSP_JETTERS; /* Bomberman Jetters (GC) */
} else if (!strcasecmp("mss",filename_extension(filename))) {
} else if (check_extensions(streamFile, "mss")) {
dspm.interleave = 0x1000;
dspm.meta_type = meta_DSP_MSS; /* Free Radical GC games */
/* Timesplitters 2 GC's ts2_atom_smasher_44_fx.mss differs slightly in samples but plays ok */
dspm.ignore_header_agreement = 1;
} else if (!strcasecmp("gcm",filename_extension(filename))) {
} else if (check_extensions(streamFile, "gcm")) {
/* older Traveller's Tales games [Lego Star Wars (GC), The Chronicles of Narnia (GC), Sonic R (GC)] */
dspm.interleave = 0x8000;
dspm.meta_type = meta_DSP_GCM; /* some of Traveller's Tales games */
dspm.meta_type = meta_DSP_GCM;
} else {
goto fail;
}
@ -866,137 +867,60 @@ fail:
return NULL;
}
/* IDSP .gcm files, two standard DSP headers */
/* found in: Lego Batman (Wii)
Lego Indiana Jones - The Original Adventures (Wii)
Lego Indiana Jones 2 - The Adventure Continues (Wii)
Lego Star Wars - The Complete Saga (Wii)
Lego The Lord of the Rings (Wii)
The Chronicles of Narnia - Prince Caspian (Wii) */
VGMSTREAM * init_vgmstream_wii_idsp(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
off_t interleave;
struct dsp_header ch0_header,ch1_header;
int i;
/* IDSP - Traveller's Tales header + interleaved dsps [Lego Batman (Wii), Lego Dimensions (Wii U)] */
VGMSTREAM * init_vgmstream_idsp_tt(STREAMFILE *streamFile) {
dsp_meta dspm = {0};
int version_main, version_sub;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if ((strcasecmp("gcm",filename_extension(filename))) &&
(strcasecmp("idsp",filename_extension(filename))))
goto fail;
/* check header magic */
if (read_32bitBE(0x0,streamFile) != 0x49445350) goto fail; /* "IDSP" */
/* different versions? */
if (read_32bitBE(0x4, streamFile) == 1 &&
read_32bitBE(0x8, streamFile) == 0xc8)
{
if (read_dsp_header(&ch0_header, 0x10, streamFile)) goto fail;
if (read_dsp_header(&ch1_header, 0x70, streamFile)) goto fail;
start_offset = 0xd0;
}
else if (read_32bitBE(0x4, streamFile) == 2 &&
read_32bitBE(0x8, streamFile) == 0xd2)
{
if (read_dsp_header(&ch0_header, 0x20, streamFile)) goto fail;
if (read_dsp_header(&ch1_header, 0x80, streamFile)) goto fail;
start_offset = 0xe0;
}
else if (read_32bitBE(0x4, streamFile) == 3 && //Lego The Lord of the Rings (Wii)
read_32bitBE(0x8, streamFile) == 0x12c)
{
if (read_dsp_header(&ch0_header, 0x20, streamFile)) goto fail;
if (read_dsp_header(&ch1_header, 0x80, streamFile)) goto fail;
start_offset = 0xe0;
}
else goto fail;
interleave = read_32bitBE(0xc, streamFile);
/* check initial predictor/scale */
if (ch0_header.initial_ps != (uint8_t)read_8bit(start_offset,streamFile))
goto fail;
if (ch1_header.initial_ps != (uint8_t)read_8bit(start_offset+interleave,streamFile))
/* checks */
/* .gcm: standard
* .idsp: header id?
* .wua: Lego Dimensions (Wii U) */
if (!check_extensions(streamFile, "gcm,idsp,wua"))
goto fail;
/* check type==0 and gain==0 */
if (ch0_header.format || ch0_header.gain ||
ch1_header.format || ch1_header.gain)
if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */
goto fail;
/* check for agreement */
if (
ch0_header.sample_count != ch1_header.sample_count ||
ch0_header.nibble_count != ch1_header.nibble_count ||
ch0_header.sample_rate != ch1_header.sample_rate ||
ch0_header.loop_flag != ch1_header.loop_flag ||
ch0_header.loop_start_offset != ch1_header.loop_start_offset ||
ch0_header.loop_end_offset != ch1_header.loop_end_offset
) goto fail;
if (ch0_header.loop_flag) {
off_t loop_off;
/* check loop predictor/scale */
loop_off = ch0_header.loop_start_offset/16*8;
loop_off = (loop_off/interleave*interleave*2) + (loop_off%interleave);
if (ch0_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile))
goto fail;
if (ch1_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off+interleave,streamFile))
goto fail;
version_main = read_32bitBE(0x04, streamFile);
version_sub = read_32bitBE(0x08, streamFile); /* extra check since there are other IDSPs */
if (version_main == 0x01 && version_sub == 0xc8) {
/* Transformers: The Game (Wii) */
dspm.channel_count = 2;
dspm.max_channels = 2;
dspm.header_offset = 0x10;
}
else if (version_main == 0x02 && version_sub == 0xd2) {
/* Lego Batman (Wii)
* The Chronicles of Narnia: Prince Caspian (Wii)
* Lego Indiana Jones 2 (Wii)
* Lego Star Wars: The Complete Saga (Wii)
* Lego Pirates of the Caribbean (Wii)
* Lego Harry Potter: Years 1-4 (Wii) */
dspm.channel_count = 2;
dspm.max_channels = 2;
dspm.header_offset = 0x20;
/* 0x10+: null */
}
else if (version_main == 0x03 && version_sub == 0x12c) {
/* Lego The Lord of the Rings (Wii) */
/* Lego Dimensions (Wii U) */
dspm.channel_count = read_32bitBE(0x10, streamFile);
dspm.max_channels = 2;
dspm.header_offset = 0x20;
/* 0x14+: "I_AM_PADDING" */
}
else {
goto fail;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(2,ch0_header.loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = ch0_header.sample_count;
vgmstream->sample_rate = ch0_header.sample_rate;
/* TODO: adjust for interleave? */
vgmstream->loop_start_sample = dsp_nibbles_to_samples(ch0_header.loop_start_offset);
vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch0_header.loop_end_offset)+1;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
vgmstream->meta_type = meta_DSP_WII_IDSP;
/* coeffs */
for (i=0;i<16;i++) {
vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i];
vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i];
}
/* initial history */
/* always 0 that I've ever seen, but for completeness... */
vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1;
vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2;
vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1;
vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2;
vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
vgmstream->ch[1].streamfile = vgmstream->ch[0].streamfile;
if (!vgmstream->ch[0].streamfile) goto fail;
/* open the file for reading */
for (i=0;i<2;i++) {
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+i*interleave;
}
return vgmstream;
dspm.header_spacing = 0x60;
dspm.start_offset = dspm.header_offset + 0x60 * dspm.channel_count;
dspm.interleave = read_32bitBE(0x0c, streamFile);
dspm.meta_type = meta_IDSP_TT;
return init_vgmstream_dsp_common(streamFile, &dspm);
fail:
/* clean up anything we may have opened */
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,5 +1,5 @@
#ifndef _OPUS_CAPCOM_STREAMFILE_H_
#define _OPUS_CAPCOM_STREAMFILE_H_
#ifndef _OPUS_INTERLEAVE_STREAMFILE_H_
#define _OPUS_INTERLEAVE_STREAMFILE_H_
#include "../streamfile.h"
@ -10,7 +10,6 @@ typedef struct {
int skip_frames; /* frames to skip from other streams at points */
/* config */
int version;
int streams;
off_t start_offset; /* pointing to the stream's beginning */
size_t total_size; /* size of the resulting substream */
@ -111,7 +110,7 @@ static size_t opus_interleave_io_size(STREAMFILE *streamfile, opus_interleave_io
}
/* Prepares custom IO for multistream Opus, interleaves 1 packet per stream */
/* Prepares custom IO for multistream, interleaves 1 packet per stream */
static STREAMFILE* setup_opus_interleave_streamfile(STREAMFILE *streamFile, off_t start_offset, int streams) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
opus_interleave_io_data io_data = {0};
@ -143,4 +142,4 @@ fail:
return NULL;
}
#endif /* _OPUS_CAPCOM_STREAMFILE_H_ */
#endif /* _OPUS_INTERLEAVE_STREAMFILE_H_ */

View File

@ -18,8 +18,9 @@ VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) {
* .ss2: demuxed videos (fake?)
* .pcm: Taisho Mononoke Ibunroku (PS2)
* .adx: Armored Core 3 (PS2)
* [no actual extension]: MotoGP (PS2) */
if (!check_extensions(streamFile, "ads,ss2,pcm,adx,"))
* [no actual extension]: MotoGP (PS2)
* .800: Mobile Suit Gundam: The One Year War (PS2) */
if (!check_extensions(streamFile, "ads,ss2,pcm,adx,,800"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x53536864 && /* "SShd" */
@ -197,7 +198,6 @@ VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) {
loop_start_sample = loop_start;
loop_end_sample = loop_end;
is_loop_samples = 1;
VGM_LOG("1\n");
}
}

View File

@ -1,70 +0,0 @@
#include "meta.h"
#include "../coding/coding.h"
/* VPK */
VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag=0;
int channel_count;
off_t start_offset;
int i;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("vpk",filename_extension(filename))) goto fail;
/* check VPK Header */
if (read_32bitBE(0x00,streamFile) != 0x204B5056)
goto fail;
/* check loop */
loop_flag = (read_32bitLE(0x7FC,streamFile)!=0);
channel_count=read_32bitLE(0x14,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = read_32bitLE(0x14,streamFile);
vgmstream->sample_rate = read_32bitLE(0x10,streamFile);
/* Check for Compression Scheme */
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x04,streamFile),vgmstream->channels);
/* Get loop point values */
if(vgmstream->loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x7FC,streamFile);
vgmstream->loop_end_sample = vgmstream->num_samples;
}
vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile)/2;
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_PS2_VPK;
start_offset = (off_t)read_32bitLE(0x08,streamFile);
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=
(off_t)(start_offset+vgmstream->interleave_block_size*i);
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -29,6 +29,7 @@ typedef struct {
char resource_name[255];
int types_count[9];
int subtypes_count[9];
} ubi_bao_header;
static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset);
@ -311,6 +312,8 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) {
;VGM_LOG("BAO types: 10=%i,20=%i,30=%i,40=%i,50=%i,70=%i,80=%i\n",
bao->types_count[1],bao->types_count[2],bao->types_count[3],bao->types_count[4],bao->types_count[5],bao->types_count[7],bao->types_count[8]);
;VGM_LOG("BAO 0x20 subtypes: 01=%i,02=%i,03=%i,04=%i,05=%i,06=%i,07=%i,08=%i\n",
bao->types_count[1],bao->subtypes_count[2],bao->subtypes_count[3],bao->subtypes_count[4],bao->subtypes_count[5],bao->subtypes_count[6],bao->subtypes_count[7],bao->subtypes_count[8]);
if (bao->total_subsongs == 0) {
VGM_LOG("UBI BAO: no streams\n");
@ -321,6 +324,10 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) {
/* get stream pointed by header */
if (bao->is_external) {
off_t offset;
int resources_count;
size_t strings_size;
/* some sounds have a prefetched bit stored internally with the remaining streamed part stored externally */
bao_offset = index_header_size + index_size;
for (i = 0; i < index_entries; i++) {
@ -344,9 +351,8 @@ static int parse_pk_header(ubi_bao_header * bao, STREAMFILE *streamFile) {
}
/* parse resource table, LE (may be empty, or exist even with nothing in the file) */
off_t offset;
int resources_count = read_32bitLE(resources_offset+0x00, streamFile);
size_t strings_size = read_32bitLE(resources_offset+0x04, streamFile);
resources_count = read_32bitLE(resources_offset+0x00, streamFile);
strings_size = read_32bitLE(resources_offset+0x04, streamFile);
offset = resources_offset + 0x04+0x04 + strings_size;
for (i = 0; i < resources_count; i++) {
@ -405,7 +411,7 @@ fail:
/* parse a single BAO (binary audio object) descriptor */
static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
uint32_t bao_version, descriptor_type;
uint32_t bao_version, descriptor_type, descriptor_subtype;
size_t header_size;
int target_subsong = streamFile->stream_index;
@ -426,7 +432,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset)
/* 0x18: null */
/* 0x1c: null */
descriptor_type = read_32bit(offset+0x20, streamFile);
/* 0x28: subtype? usually 0x02/0x01, games may crash if changed */
descriptor_subtype = read_32bit(offset+header_size+0x04, streamFile); /* games may crash if changed */
/* for debugging purposes */
switch(descriptor_type) {
@ -435,18 +441,35 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset)
case 0x30000000: bao->types_count[3]++; break; /* internal stream (in .pk) */
case 0x40000000: bao->types_count[4]++; break; /* package info? */
case 0x50000000: bao->types_count[5]++; break; /* external stream (in .spk) */
case 0x70000000: bao->types_count[7]++; break; /* project info? (sometimes special id 0x7fffffff)*/
case 0x70000000: bao->types_count[7]++; break; /* project info? (sometimes special id 0x7fffffff) */
case 0x80000000: bao->types_count[8]++; break; /* unknown (some id/info?) */
default:
VGM_LOG("UBI BAO: unknown descriptor type at %lx\n", offset);
VGM_LOG("UBI BAO: unknown type %x at %lx (%lx)\n", descriptor_type, offset, offset+0x20);
goto fail;
}
/* only parse headers */
if (descriptor_type != 0x20000000)
return 1;
/* ignore other header subtypes, 0x01=sound header, 0x04=info? (like Ubi .sb0) */
if (read_32bit(offset+header_size+0x04, streamFile) != 0x01)
/* for debugging purposes */
switch(descriptor_subtype) {
case 0x00000001: bao->subtypes_count[1]++; break; /* standard */
case 0x00000002: bao->subtypes_count[2]++; break; /* multilayer??? related to other header BAOs? */
case 0x00000003: bao->subtypes_count[3]++; break; /* related to other header BAOs? */
case 0x00000004: bao->subtypes_count[4]++; break; /* related to other header BAOs? */
case 0x00000005: bao->subtypes_count[5]++; break; /* related to other header BAOs? */
case 0x00000006: bao->subtypes_count[6]++; break; /* some multilayer/table? may contain sounds??? */
case 0x00000007: bao->subtypes_count[7]++; break; /* related to other header BAOs? */
case 0x00000008: bao->subtypes_count[8]++; break; /* ? (almost empty with some unknown value) */
default:
VGM_LOG("UBI BAO: unknown subtype %x at %lx (%lx)\n", descriptor_subtype, offset, offset+header_size+0x04);
goto fail;
}
//;VGM_ASSERT(descriptor_subtype != 0x01, "UBI BAO: subtype %x at %lx (%lx)\n", descriptor_subtype, offset, offset+header_size+0x04);
/* ignore unknown subtypes */
if (descriptor_subtype != 0x01)
return 1;
bao->total_subsongs++;
@ -457,14 +480,13 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset)
/* parse BAO per version. Structure is mostly the same with some extra fields.
* - descriptor id (ignored by game)
* - type (may crash on game startup if changed)
* - subtype (may crash on game startup if changed)
* - stream size
* - stream id, corresponding to an internal (0x30) or external (0x50) stream
* - various flags/config fields
* - channels, ?, sample rate, average bit rate?, samples, full stream_size?, codec, etc
* - subtable entries, subtable size (may contain offsets/ids, cues, etc)
* - extra data per codec (ex. XMA header in some versions) */
//todo skip tables when getting extradata
;VGM_LOG("BAO header at %lx\n", offset);
bao->version = bao_version;
@ -496,14 +518,12 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset)
bao->prefetch_size = read_32bit(offset + header_size + 0x74, streamFile);
//todo use flags?
if (bao->header_codec == 0x09) {
if (bao->codec == RAW_DSP) {
bao->extradata_offset = offset+header_size+0x80; /* mini DSP header */
}
if (bao->header_codec == 0x05 && !bao->is_external) {
if (bao->codec == RAW_XMA1 && !bao->is_external) {
bao->extradata_offset = offset+header_size + 0x7c; /* XMA header */
}
//todo external XMA may use blocked layout + layered layout
break;
@ -559,7 +579,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset)
bao->prefetch_size = read_32bit(offset+header_size+0x84, streamFile);
if (bao->header_codec == 0x04 && !bao->is_external) {
if (bao->codec == RAW_XMA2 && !bao->is_external) {
bao->extradata_offset = offset + header_size + 0x8c; /* XMA header */
}
@ -593,7 +613,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset)
bao->prefetch_size = read_32bit(offset + header_size + 0x78, streamFile);
if (bao->header_codec == 0x04 && !bao->is_external) {
if (bao->codec == RAW_XMA2 && !bao->is_external) {
bao->extradata_offset = offset+header_size + 0x8c; /* XMA header */
}

51
src/meta/vpk.c Normal file
View File

@ -0,0 +1,51 @@
#include "meta.h"
#include "../coding/coding.h"
/* VPK - from SCE America second party devs [God of War (PS2), NBA 08 (PS3)] */
VGMSTREAM * init_vgmstream_ps2_vpk(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int loop_flag, channel_count;
off_t start_offset;
size_t channel_size;
/* checks */
if (!check_extensions(streamFile, "vpk"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x204B5056) /* " KPV" */
goto fail;
/* seems this consistently has 0x10-0x20 extra bytes, landing in garbage frames at the end */
channel_size = read_32bitLE(0x04,streamFile) - 0x20; /* remove for cleaner ends */
start_offset = read_32bitLE(0x08,streamFile);
channel_count = read_32bitLE(0x14,streamFile);
/* 0x18+(per channel): channel config? */
loop_flag = (read_32bitLE(0x7FC,streamFile) != 0); /* used? */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x10,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(channel_size*vgmstream->channels,vgmstream->channels);
if (vgmstream->loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x7FC,streamFile);
vgmstream->loop_end_sample = vgmstream->num_samples;
}
vgmstream->meta_type = meta_VPK;
vgmstream->coding_type = coding_PSX;
vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile) / 2; /* even in >2ch */
vgmstream->layout_type = layout_interleave;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -251,8 +251,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
/* older Wwise (~<2012) */
switch(vorb_size) {
case 0x2C: /* earliest (~2009), ex. UFC Undisputed 2009 (PS3), some EVE Online Apocrypha files? */
case 0x28: /* early (~2009), ex. The Lord of the Rings: Conquest (PC) */
case 0x2C: /* earliest (~2009), [UFC Undisputed 2009 (PS3), some EVE Online Apocrypha (PC)?] */
case 0x28: /* early (~2009) [The Lord of the Rings: Conquest (PC)] */
data_offsets = 0x18;
block_offsets = 0; /* no need, full headers are present */
cfg.header_type = WWV_TYPE_8;
@ -260,21 +260,23 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
cfg.setup_type = WWV_HEADER_TRIAD;
break;
//case 0x32: /* ? */
case 0x34: /* common (2010~2011) */
case 0x32: /* very rare (mid 2011) [Saints Row the 3rd (PC)] */
data_offsets = 0x18;
block_offsets = 0x30;
cfg.header_type = WWV_TYPE_6;
cfg.packet_type = WWV_STANDARD;
cfg.setup_type = WWV_EXTERNAL_CODEBOOKS; /* setup_type will be corrected later */
break;
case 0x2a: /* uncommon (mid 2011), ex. infamous 2 PS3 */
case 0x2a: /* uncommon (mid 2011), [inFamous 2 (PS3)] */
data_offsets = 0x10;
block_offsets = 0x28;
cfg.header_type = WWV_TYPE_2;
cfg.packet_type = WWV_MODIFIED;
cfg.setup_type = WWV_EXTERNAL_CODEBOOKS;
break;
default:
VGM_LOG("WWISE: unknown vorb size 0x%x\n", vorb_size);
goto fail;

View File

@ -381,6 +381,10 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
xwb.loop_start_sample = msd.loop_start_sample;
xwb.loop_end_sample = msd.loop_end_sample;
/* for XWB v22 (and below?) this seems normal [Project Gotham Racing (X360)] */
if (xwb.num_samples == 0)
xwb.num_samples = msd.num_samples;
// todo fix properly (XWB loop_start/end seem to count padding samples while XMA1 RIFF doesn't)
//this doesn't seem ok because can fall within 0 to 512 (ie.- first frame, 384)
//if (xwb.loop_start_sample) xwb.loop_start_sample -= 512;

View File

@ -1095,3 +1095,33 @@ void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size) {
streamFile->get_name(streamFile,filename,size);
strcpy(filename, filename_extension(filename));
}
/* debug util, mainly for custom IO testing */
void dump_streamfile(STREAMFILE *streamFile, const char* out) {
#ifdef VGM_DEBUG_OUTPUT
off_t offset = 0;
FILE *f = NULL;
if (out) {
f = fopen(out,"wb");
if (!f) return;
}
VGM_LOG("dump streamfile, size: %x\n", get_streamfile_size(streamFile));
while (offset < get_streamfile_size(streamFile)) {
uint8_t buffer[0x8000];
size_t read;
read = read_streamfile(buffer,offset,0x8000,streamFile);
if (out)
fwrite(buffer,sizeof(uint8_t),read, f);
else
VGM_LOGB(buffer,read,0);
offset += read;
}
if (out) {
fclose(f);
}
#endif
}

View File

@ -204,4 +204,6 @@ void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size)
void get_streamfile_basename(STREAMFILE *streamFile, char * buffer, size_t size);
void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size);
void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size);
void dump_streamfile(STREAMFILE *streamFile, const char* out);
#endif

View File

@ -141,13 +141,13 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_sat_sap,
init_vgmstream_dc_idvi,
init_vgmstream_ps2_rnd,
init_vgmstream_wii_idsp,
init_vgmstream_idsp_tt,
init_vgmstream_kraw,
init_vgmstream_ps2_omu,
init_vgmstream_ps2_xa2,
init_vgmstream_idsp2,
init_vgmstream_idsp3,
init_vgmstream_idsp4,
init_vgmstream_nub_idsp,
init_vgmstream_idsp_nl,
init_vgmstream_idsp_ie,
init_vgmstream_ngc_ymf,
init_vgmstream_sadl,
init_vgmstream_ps2_ccc,
@ -603,6 +603,12 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
}
#endif
#ifdef VGM_USE_CELT
if (vgmstream->coding_type==coding_CELT_FSB) {
reset_celt_fsb(vgmstream);
}
#endif
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type==coding_FFmpeg) {
reset_ffmpeg(vgmstream);
@ -783,6 +789,13 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
}
#endif
#ifdef VGM_USE_CELT
if (vgmstream->coding_type == coding_CELT_FSB) {
free_celt_fsb(vgmstream->codec_data);
vgmstream->codec_data = NULL;
}
#endif
if (vgmstream->coding_type==coding_ACM) {
free_acm(vgmstream->codec_data);
vgmstream->codec_data = NULL;
@ -1168,6 +1181,10 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
#ifdef VGM_USE_ATRAC9
case coding_ATRAC9:
return 0; /* varies with config data, usually 256 or 1024 */
#endif
#ifdef VGM_USE_CELT
case coding_CELT_FSB:
return 0; /* 512? */
#endif
default:
return 0;
@ -1320,6 +1337,10 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
#ifdef VGM_USE_ATRAC9
case coding_ATRAC9:
return 0; /* varies with config data, usually 0x100-200 */
#endif
#ifdef VGM_USE_CELT
case coding_CELT_FSB:
return 0; /* varies, usually 0x80-100 */
#endif
default: /* Vorbis, MPEG, ACM, etc */
return 0;
@ -1844,6 +1865,14 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do,
vgmstream->channels);
break;
#endif
#ifdef VGM_USE_CELT
case coding_CELT_FSB:
decode_celt_fsb(vgmstream,
buffer+samples_written*vgmstream->channels,
samples_to_do,
vgmstream->channels);
break;
#endif
case coding_ACM:
decode_acm(vgmstream->codec_data,
@ -2091,6 +2120,12 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
}
#endif
#ifdef VGM_USE_CELT
if (vgmstream->coding_type==coding_CELT_FSB) {
seek_celt_fsb(vgmstream, vgmstream->loop_sample);
}
#endif
#ifdef VGM_USE_MPEG
if (vgmstream->coding_type==coding_MPEG_custom ||
vgmstream->coding_type==coding_MPEG_ealayer3 ||

View File

@ -30,6 +30,7 @@ enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */
//#define VGM_USE_MAIATRAC3PLUS
//#define VGM_USE_FFMPEG
//#define VGM_USE_ATRAC9
//#define VGM_USE_CELT
#ifdef VGM_USE_VORBIS
@ -207,6 +208,10 @@ typedef enum {
coding_ATRAC9, /* Sony ATRAC9 (MDCT-based) */
#endif
#ifdef VGM_USE_CELT
coding_CELT_FSB, /* Custom Xiph CELT (MDCT-based) */
#endif
#ifdef VGM_USE_FFMPEG
coding_FFmpeg, /* Formats handled by FFmpeg (ATRAC3, XMA, AC3, etc) */
#endif
@ -284,7 +289,7 @@ typedef enum {
meta_DSP_STR, /* Conan .str files */
meta_DSP_SADB, /* .sad */
meta_DSP_WSI, /* .wsi */
meta_DSP_WII_IDSP, /* .gcm with IDSP header */
meta_IDSP_TT, /* Traveller's Tales games */
meta_DSP_WII_MUS, /* .mus */
meta_DSP_WII_WSD, /* Phantom Brave (WII) */
meta_WII_NDP, /* Vertigo (Wii) */
@ -345,7 +350,7 @@ typedef enum {
meta_PS2_ILD, /* ILD File */
meta_PS2_PNB, /* PsychoNauts Bgm File */
meta_PS2_VAGs, /* VAG Stereo from Kingdom Hearts */
meta_PS2_VPK, /* VPK Audio File */
meta_VPK, /* VPK Audio File */
meta_PS2_BMDX, /* Beatmania thing */
meta_PS2_IVB, /* Langrisser 3 IVB */
meta_PS2_SND, /* some Might & Magics SSND header */
@ -396,7 +401,9 @@ typedef enum {
meta_KRAW, /* Geometry Wars - Galaxies */
meta_PS2_OMU, /* PS2 Int file with Header */
meta_PS2_XA2, /* XG3 Extreme-G Racing */
meta_IDSP, /* Chronicles of Narnia, Soul Calibur Legends, Mario Strikers Charged */
meta_NUB_IDSP, /* Soul Calibur Legends (Wii) */
meta_IDSP_NL, /* Mario Strikers Charged (Wii) */
meta_IDSP_IE, /* Defencer (GC) */
meta_SPT_SPD, /* Various (SPT+SPT DSP) */
meta_ISH_ISD, /* Various (ISH+ISD DSP) */
meta_GSP_GSB, /* Tecmo games (Super Swing Golf 1 & 2, Quamtum Theory) */
@ -1028,8 +1035,6 @@ typedef enum {
ATRAC9_DEFAULT = 0, /* ATRAC9 standard */
ATRAC9_XVAG, /* Sony XVAG: interleaved subsongs, Vita multichannel interleaves 2ch xN superframes */
ATRAC9_KMA9, /* Koei Tecmo KMA9: interleaved subsongs */
//ATRAC9_FSB, /* FMOD FSB: Vita multichannel interleaves 2ch xN superframes */
//ATRAC9_EATRAX, /* EA EATrax: buffered ATRAC9 in SPS blocks (superframes can be split between blocks) */
} atrac9_custom_t;
typedef struct {
@ -1059,6 +1064,11 @@ typedef struct {
} atrac9_codec_data;
#endif
#ifdef VGM_USE_CELT
typedef enum { CELT_0_06_1,CELT_0_11_0} celt_lib_t;
typedef struct celt_codec_data celt_codec_data;
#endif
/* libacm interface */
typedef struct {
ACMStream *file;

View File

@ -71,6 +71,13 @@ ifeq ($(VGM_ENABLE_ATRAC9),1)
TARGET_EXT_LIBS += libatrac9.a
endif
VGM_ENABLE_CELT = 1
ifeq ($(VGM_ENABLE_CELT),1)
CFLAGS += -DVGM_USE_CELT
LDFLAGS += -lcelt-0061 -lcelt-0110
TARGET_EXT_LIBS += libcelt-0061.a libcelt-0110.a
endif
endif #if WIN32
export CFLAGS LDFLAGS

View File

@ -71,7 +71,7 @@
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@ -81,7 +81,7 @@
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;../ext_libs/libcelt-0061.lib;../ext_libs/libcelt-0110.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
@ -108,7 +108,7 @@
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<AdditionalDependencies>../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;../ext_libs/libcelt-0061.lib;../ext_libs/libcelt-0110.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<OptimizeReferences>true</OptimizeReferences>

View File

@ -71,6 +71,13 @@ ifeq ($(VGM_ENABLE_ATRAC9),1)
TARGET_EXT_LIBS += libatrac9.a
endif
VGM_ENABLE_CELT = 1
ifeq ($(VGM_ENABLE_CELT),1)
CFLAGS += -DVGM_USE_CELT
LDFLAGS += -lcelt-0061 -lcelt-0110
TARGET_EXT_LIBS += libcelt-0061.a libcelt-0110.a
endif
endif #if WIN32
export CFLAGS LDFLAGS

View File

@ -62,7 +62,7 @@
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@ -70,7 +70,7 @@
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;../ext_libs/libcelt-0061.lib;../ext_libs/libcelt-0110.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<TargetMachine>MachineX86</TargetMachine>
@ -97,7 +97,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
</ClCompile>
<Link>
<AdditionalDependencies>../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/avcodec.lib;../ext_libs/avformat.lib;../ext_libs/avutil.lib;../ext_libs/swresample.lib;../ext_libs/libatrac9.lib;../ext_libs/libcelt-0061.lib;../ext_libs/libcelt-0110.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<OptimizeReferences>true</OptimizeReferences>