mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-17 23:36:41 +01:00
commit
6ba3b684fb
34
README.md
34
README.md
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
131
ext_includes/celt/celt_fsb.h
Normal file
131
ext_includes/celt/celt_fsb.h
Normal 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 */
|
140
ext_includes/celt/celt_types.h
Normal file
140
ext_includes/celt/celt_types.h
Normal 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 */
|
@ -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
|
||||
|
@ -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" />
|
||||
|
8
ext_libs/libcelt-0061.def
Normal file
8
ext_libs/libcelt-0061.def
Normal 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
BIN
ext_libs/libcelt-0061.dll
Normal file
Binary file not shown.
8
ext_libs/libcelt-0110.def
Normal file
8
ext_libs/libcelt-0110.def
Normal 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
BIN
ext_libs/libcelt-0110.dll
Normal file
Binary file not shown.
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
239
src/coding/celt_fsb_decoder.c
Normal file
239
src/coding/celt_fsb_decoder.c
Normal 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
|
@ -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);
|
||||
|
@ -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"},
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
372
src/meta/fsb.c
372
src/meta/fsb.c
@ -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") )
|
||||
|
498
src/meta/fsb5.c
498
src/meta/fsb5.c
@ -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;
|
||||
}
|
||||
|
201
src/meta/fsb5_interleave_streamfile.h
Normal file
201
src/meta/fsb5_interleave_streamfile.h
Normal 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_ */
|
198
src/meta/fsb_interleave_streamfile.h
Normal file
198
src/meta/fsb_interleave_streamfile.h
Normal 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_ */
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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_ */
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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
51
src/meta/vpk.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 ||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user