mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-29 19:37:30 +01:00
commit
126e3b4113
@ -72,7 +72,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_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
@ -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_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;NDEBUG;_WINDOWS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_ATRAC9;VGM_USE_CELT;NDEBUG;_WINDOWS;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
|
@ -56,15 +56,15 @@ You can also call MSBuild directly in the command line (see the foobar2000 secti
|
||||
### foobar2000 plugin (foo\_input\_vgmstream)
|
||||
Requires MSVC (foobar/SDK only links to MSVC C++ DLLs) and these dependencies:
|
||||
- foobar2000 SDK (2018), in *(vgmstream)/dependencies/foobar/*: http://www.foobar2000.org/SDK
|
||||
- FDK-AAC, in *(vgmstream)/dependencies/fdk-aac/*: https://github.com/kode54/fdk-aac
|
||||
- QAAC, in *(vgmstream)/dependencies/qaac/*: https://github.com/kode54/qaac
|
||||
- (optional) FDK-AAC, in *(vgmstream)/dependencies/fdk-aac/*: https://github.com/kode54/fdk-aac
|
||||
- (optional) QAAC, in *(vgmstream)/dependencies/qaac/*: https://github.com/kode54/qaac
|
||||
- WTL (if needed), in *(vgmstream)/dependencies/WTL/*: http://wtl.sourceforge.net/
|
||||
- may need to install ATL and MFC libraries (can be installed in Visual Studio Installer)
|
||||
|
||||
The following project modifications are required:
|
||||
- For *foobar2000_ATL_helpers* add *../../../WTL/Include* to the compilers's *additional includes*
|
||||
|
||||
FDK-AAC/QAAC can be safely disabled by removing *VGM_USE_MP4V2* and *VGM_USE_FDKAAC* in the compiler/linker options and the project dependencies, as FFmpeg is used instead to support their codecs.
|
||||
FDK-AAC/QAAC can be enabled adding *VGM_USE_MP4V2* and *VGM_USE_FDKAAC* in the compiler/linker options and the project dependencies, otherwise FFmpeg is used instead to support .mp4.
|
||||
|
||||
You can also manually use the command line to compile with MSBuild, if you don't want to touch the .vcxproj files, register VS after trial, get PowerShell dependencies for the build script, or only have VC++/MSBuild tools.
|
||||
|
||||
|
@ -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_VORBIS;VGM_USE_MPEG;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>
|
||||
<PreprocessorDefinitions>WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
@ -97,7 +97,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_VORBIS;VGM_USE_MPEG;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>
|
||||
<PreprocessorDefinitions>WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_ATRAC9;VGM_USE_CELT;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
|
@ -1,29 +1,109 @@
|
||||
#include "coding.h"
|
||||
|
||||
|
||||
/* Circus XPCM mode 2 decoding, verified vs EF.exe (info from foo_adpcm/libpcm and https://github.com/lioncash/ExtractData) */
|
||||
void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i, sample_pos = 0;
|
||||
int32_t hist = stream->adpcm_history1_32;
|
||||
int scale = stream->adpcm_scale;
|
||||
off_t frame_offset = stream->offset; /* frame size is 1 */
|
||||
|
||||
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int8_t code = read_8bit(frame_offset+i,stream->streamfile);
|
||||
|
||||
hist += code << scale;
|
||||
if (code == 0) {
|
||||
if (scale > 0)
|
||||
scale--;
|
||||
}
|
||||
else if (code == 127 || code == -128) {
|
||||
if (scale < 8)
|
||||
scale++;
|
||||
}
|
||||
outbuf[sample_pos] = hist;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist;
|
||||
stream->adpcm_scale = scale;
|
||||
}
|
||||
#include "coding.h"
|
||||
#include "circus_decoder_lib.h"
|
||||
|
||||
|
||||
|
||||
struct circus_codec_data {
|
||||
STREAMFILE* sf;
|
||||
int16_t* buf;
|
||||
int buf_samples_all;
|
||||
circus_handle_t* handle;
|
||||
};
|
||||
|
||||
|
||||
circus_codec_data* init_circus_vq(STREAMFILE* sf, off_t start, uint8_t codec, uint8_t flags) {
|
||||
circus_codec_data* data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(circus_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->sf = reopen_streamfile(sf, 0);
|
||||
data->handle = circus_init(start, codec, flags);
|
||||
if (!data->handle) goto fail;
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_circus_vq(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void decode_circus_vq(circus_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels) {
|
||||
int ok, i, samples_to_get;
|
||||
|
||||
while (samples_to_do > 0) {
|
||||
if (data->buf_samples_all == 0) {
|
||||
ok = circus_decode_frame(data->handle, data->sf, &data->buf, &data->buf_samples_all);
|
||||
if (!ok) goto decode_fail;
|
||||
}
|
||||
|
||||
samples_to_get = data->buf_samples_all / channels;
|
||||
if (samples_to_get > samples_to_do)
|
||||
samples_to_get = samples_to_do;
|
||||
|
||||
for (i = 0; i < samples_to_get * channels; i++) {
|
||||
outbuf[i] = data->buf[i];
|
||||
}
|
||||
|
||||
data->buf += samples_to_get * channels;
|
||||
data->buf_samples_all -= samples_to_get * channels;
|
||||
outbuf += samples_to_get * channels;
|
||||
samples_to_do -= samples_to_get;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
decode_fail:
|
||||
VGM_LOG("CIRCUS: decode error\n");
|
||||
memset(outbuf, 0, samples_to_do * channels * sizeof(sample_t));
|
||||
}
|
||||
|
||||
void reset_circus_vq(circus_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
circus_reset(data->handle);
|
||||
data->buf_samples_all = 0;
|
||||
}
|
||||
|
||||
void seek_circus_vq(circus_codec_data* data, int32_t num_sample) {
|
||||
if (!data) return;
|
||||
|
||||
reset_circus_vq(data);
|
||||
//data->samples_discard = num_sample; //todo (xpcm don't have loop points tho)
|
||||
}
|
||||
|
||||
void free_circus_vq(circus_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
close_streamfile(data->sf);
|
||||
circus_free(data->handle);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
||||
/* Circus XPCM mode 2 decoding, verified vs EF.exe (info from foo_adpcm/libpcm and https://github.com/lioncash/ExtractData) */
|
||||
void decode_circus_adpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i, sample_pos = 0;
|
||||
int32_t hist = stream->adpcm_history1_32;
|
||||
int scale = stream->adpcm_scale;
|
||||
off_t frame_offset = stream->offset; /* frame size is 1 */
|
||||
|
||||
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int8_t code = read_8bit(frame_offset+i,stream->streamfile);
|
||||
|
||||
hist += code << scale;
|
||||
if (code == 0) {
|
||||
if (scale > 0)
|
||||
scale--;
|
||||
}
|
||||
else if (code == 127 || code == -128) {
|
||||
if (scale < 8)
|
||||
scale++;
|
||||
}
|
||||
outbuf[sample_pos] = hist;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist;
|
||||
stream->adpcm_scale = scale;
|
||||
}
|
||||
|
492
src/coding/circus_decoder_lib.c
Normal file
492
src/coding/circus_decoder_lib.c
Normal file
@ -0,0 +1,492 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Decodes Circus's audio codec, reverse engineered from various .exe.
|
||||
*
|
||||
* Some sources identify this codec as VQ (vector quantization), though vector(?)
|
||||
* data isn't actually bitpacked and just compressed using custom LZ or standard zlib.
|
||||
* Channels aren't divided either so decoding results in N-ch interleaved PCM.
|
||||
* It does seem to be using LPC/speech stuff from VQ codecs though.
|
||||
*
|
||||
* Some info from Japanese libpcm.c found in foo_adpcm
|
||||
* https://bitbucket.org/losnoco/foo_adpcm/src/master/foo_oki/source/libpcm/libpcm.cpp
|
||||
*/
|
||||
|
||||
#include "circus_decoder_lib.h"
|
||||
#include "circus_decoder_lib_data.h"
|
||||
|
||||
#include "circus_decoder_lzxpcm.h"
|
||||
|
||||
/* use miniz (API-compatible) to avoid adding external zlib just for this codec
|
||||
* - https://github.com/richgel999/miniz */
|
||||
#include "circus_decoder_miniz.h"
|
||||
//#include "zlib.h"
|
||||
|
||||
|
||||
//#define XPCM_CODEC_PCM 0
|
||||
#define XPCM_CODEC_VQ_LZXPCM 1
|
||||
//#define XPCM_CODEC_ADPCM 2
|
||||
#define XPCM_CODEC_VQ_DEFLATE 3
|
||||
|
||||
/* frame encodes 4096 PCM samples (all channels) = 4096*2 = 0x2000 bytes, re-interleaved then compressed */
|
||||
#define XPCM_FRAME_SIZE (4096 * 2)
|
||||
#define XPCM_FRAME_CODES 4096
|
||||
#define XPCM_FRAME_SAMPLES_ALL 4064
|
||||
#define XPCM_FRAME_OVERLAP_ALL 32
|
||||
#define XPCM_INPUT_SIZE 0x8000
|
||||
|
||||
/* ************************************************************************* */
|
||||
/* DECODE */
|
||||
/* ************************************************************************* */
|
||||
|
||||
struct circus_handle_t {
|
||||
/* config */
|
||||
off_t start;
|
||||
uint8_t codec;
|
||||
uint8_t flags;
|
||||
const int* scales;
|
||||
|
||||
/* temp buffers */
|
||||
uint8_t srcbuf[XPCM_INPUT_SIZE]; /* compressed input data (arbitrary size) */
|
||||
uint8_t decbuf[XPCM_FRAME_SIZE]; /* single decompressed frame */
|
||||
uint8_t intbuf[XPCM_FRAME_SIZE]; /* re-interleaved frame */
|
||||
int32_t invbuf[XPCM_FRAME_CODES]; /* main LPC data (may need less) */
|
||||
int32_t tmpbuf[XPCM_FRAME_CODES]; /* temp LPC data (may need less) */
|
||||
|
||||
/* output samples (original code reuses decbuf though) */
|
||||
int16_t pcmbuf[XPCM_FRAME_SAMPLES_ALL + XPCM_FRAME_OVERLAP_ALL]; /* final output samples and extra overlap samples */
|
||||
|
||||
/* sample filter state */
|
||||
int hist1;
|
||||
int hist2;
|
||||
int frame;
|
||||
|
||||
/* lz/deflate decompression state */
|
||||
lzxpcm_stream_t lstrm;
|
||||
z_stream dstrm;
|
||||
off_t offset;
|
||||
};
|
||||
|
||||
|
||||
static void convert(uint8_t flags, int32_t* invbuf, int16_t* pcmbuf, int* p_hist1, int* p_hist2, int frame) {
|
||||
int i;
|
||||
int sample, hist1, hist2;
|
||||
|
||||
hist1 = *p_hist1;
|
||||
hist2 = *p_hist2;
|
||||
|
||||
/* some ops below would use SHRs (>>), but there is some rounding in the original
|
||||
* ASM that decompiles and I think corresponds do DIVs (right shift and divs of
|
||||
* negative values isn't equivalent). Similarly the filters seem to use CDQ tricks
|
||||
* to simulate s64 ops, but I'm not sure casting is 100% equivalent (sounds ok tho). */
|
||||
|
||||
/* do final filtering and conversion to PCM */
|
||||
for (i = 0; i < XPCM_FRAME_SAMPLES_ALL + XPCM_FRAME_OVERLAP_ALL; i++) {
|
||||
sample = *invbuf++;
|
||||
if (flags & 0x10)
|
||||
sample = (3 * (int64_t)sample / 2) / 1024; //>> 10;
|
||||
else
|
||||
sample = sample / 1024; //>> 10;
|
||||
|
||||
sample = ((27 * (int64_t)sample + 4 * hist1 + hist2) << 11) / 65536; //>> 16
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = sample;
|
||||
|
||||
/* last 32 decoded samples aren't output, but are used next frame to overlap
|
||||
* with beginning samples (filters(?) windowing, not too noticeable though) */
|
||||
if (i < XPCM_FRAME_OVERLAP_ALL && frame > 0) {
|
||||
sample = ((i * (int64_t)sample) + ((XPCM_FRAME_OVERLAP_ALL - i) * pcmbuf[XPCM_FRAME_SAMPLES_ALL + i])) / 32; //>> 5
|
||||
}
|
||||
|
||||
if (sample > 32767)
|
||||
sample = 32767;
|
||||
else if (sample < -32768)
|
||||
sample = -32768;
|
||||
|
||||
pcmbuf[i] = sample;
|
||||
}
|
||||
|
||||
*p_hist1 = hist1;
|
||||
*p_hist2 = hist2;
|
||||
}
|
||||
|
||||
static void transform(int32_t* invbuf, int32_t* tmpbuf) {
|
||||
int lpc1, lpc2, lpc3, lpc4;
|
||||
int step1, step2, step3;
|
||||
int sc1, sc2;
|
||||
|
||||
/* bits were originally configurable (passed arg), but actually called with const 12,
|
||||
* and removed in later games along with superfluous ifs (coefs > 0, bits >= 3, etc) */
|
||||
//const int frame_bits = 12;
|
||||
|
||||
step1 = 4096; /* 1 << 12 */
|
||||
step2 = step1 >> 1;
|
||||
step3 = step2 >> 1;
|
||||
sc1 = 1;
|
||||
|
||||
/* inverse transform of LPC(?) coefs */
|
||||
for (lpc1 = 0; lpc1 < 12 - 2; lpc1++) {
|
||||
int sub1, sub2;
|
||||
int i1, i2, i3, i4;
|
||||
int64_t cos1, sin1, cos2, sin2; /* needs i64 to force 64b ops (avoid overflows) */
|
||||
|
||||
cos1 = (int64_t)sincos_table[sc1 + 1024];
|
||||
sin1 = (int64_t)sincos_table[sc1 + 0];
|
||||
|
||||
i1 = 0;
|
||||
i2 = step2;
|
||||
i3 = step3;
|
||||
i4 = step2 + step3;
|
||||
|
||||
for (lpc2 = 0; lpc2 < 4096; lpc2 += step1) {
|
||||
sub1 = invbuf[i1 + 0] - invbuf[i2 + 0];
|
||||
sub2 = tmpbuf[i1 + 0] - tmpbuf[i2 + 0];
|
||||
invbuf[i1 + 0] += invbuf[i2 + 0];
|
||||
tmpbuf[i1 + 0] += tmpbuf[i2 + 0];
|
||||
invbuf[i2 + 0] = sub1;
|
||||
tmpbuf[i2 + 0] = sub2;
|
||||
|
||||
sub1 = invbuf[i1 + 1] - invbuf[i2 + 1];
|
||||
sub2 = tmpbuf[i1 + 1] - tmpbuf[i2 + 1];
|
||||
invbuf[i1 + 1] += invbuf[i2 + 1];
|
||||
tmpbuf[i1 + 1] += tmpbuf[i2 + 1];
|
||||
invbuf[i2 + 1] = ((sub1 * cos1) >> 12) + ((sub2 * sin1) >> 12);
|
||||
tmpbuf[i2 + 1] = ((sub2 * cos1) >> 12) - ((sub1 * sin1) >> 12);
|
||||
|
||||
sub1 = invbuf[i3 + 0] - invbuf[i4 + 0];
|
||||
sub2 = tmpbuf[i3 + 0] - tmpbuf[i4 + 0];
|
||||
invbuf[i3 + 0] += invbuf[i4 + 0];
|
||||
tmpbuf[i3 + 0] += tmpbuf[i4 + 0];
|
||||
invbuf[i4 + 0] = sub2;
|
||||
tmpbuf[i4 + 0] = -sub1;
|
||||
|
||||
sub1 = invbuf[i3 + 1] - invbuf[i4 + 1];
|
||||
sub2 = tmpbuf[i3 + 1] - tmpbuf[i4 + 1];
|
||||
invbuf[i3 + 1] += invbuf[i4 + 1];
|
||||
tmpbuf[i3 + 1] += tmpbuf[i4 + 1];
|
||||
invbuf[i4 + 1] = ((sub2 * cos1) >> 12) - ((sub1 * sin1) >> 12);
|
||||
tmpbuf[i4 + 1] = -(((sub1 * cos1) >> 12) + ((sub2 * sin1) >> 12));
|
||||
|
||||
i1 += step1;
|
||||
i2 += step1;
|
||||
i3 += step1;
|
||||
i4 += step1;
|
||||
}
|
||||
|
||||
if (step3 > 2) {
|
||||
sc2 = sc1 * 2;
|
||||
|
||||
for (lpc3 = 2; lpc3 < step3; lpc3++) {
|
||||
cos2 = (int64_t)sincos_table[sc2 + 1024];
|
||||
sin2 = (int64_t)sincos_table[sc2 + 0];
|
||||
sc2 += sc1;
|
||||
|
||||
i1 = 0 + lpc3;
|
||||
i2 = step2 + lpc3;
|
||||
i3 = step3 + lpc3;
|
||||
i4 = step2 + step3 + lpc3;
|
||||
|
||||
for (lpc4 = 0; lpc4 < 4096; lpc4 += step1) {
|
||||
sub1 = invbuf[i1] - invbuf[i2];
|
||||
sub2 = tmpbuf[i1] - tmpbuf[i2];
|
||||
invbuf[i1] += invbuf[i2];
|
||||
tmpbuf[i1] += tmpbuf[i2];
|
||||
invbuf[i2] = ((sub1 * cos2) >> 12) + ((sub2 * sin2) >> 12);
|
||||
tmpbuf[i2] = ((sub2 * cos2) >> 12) - ((sub1 * sin2) >> 12);
|
||||
|
||||
sub1 = invbuf[i3] - invbuf[i4];
|
||||
sub2 = tmpbuf[i3] - tmpbuf[i4];
|
||||
invbuf[i3] += invbuf[i4];
|
||||
tmpbuf[i3] += tmpbuf[i4];
|
||||
invbuf[i4] = ((sub2 * cos2) >> 12) - ((sub1 * sin2) >> 12);
|
||||
tmpbuf[i4] = -(((sub1 * cos2) >> 12) + ((sub2 * sin2) >> 12));
|
||||
|
||||
i1 += step1;
|
||||
i2 += step1;
|
||||
i3 += step1;
|
||||
i4 += step1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
step1 = step2; // step1 >>= 1;
|
||||
step2 = step3; // step2 >>= 1;
|
||||
step3 >>= 1;
|
||||
sc1 *= 2;
|
||||
}
|
||||
|
||||
{
|
||||
int i, j;
|
||||
int sub1, sub2, pow;
|
||||
|
||||
for (i = 0; i < 4096; i += 4) {
|
||||
sub1 = invbuf[i + 0] - invbuf[i + 2];
|
||||
invbuf[i + 0] += invbuf[i + 2];
|
||||
invbuf[i + 2] = sub1;
|
||||
|
||||
sub2 = tmpbuf[i + 0] - tmpbuf[i + 2];
|
||||
tmpbuf[i + 0] += tmpbuf[i + 2];
|
||||
tmpbuf[i + 2] = sub2;
|
||||
|
||||
sub1 = invbuf[i + 3] - invbuf[i + 1];
|
||||
sub2 = tmpbuf[i + 1] - tmpbuf[i + 3];
|
||||
invbuf[i + 1] += invbuf[i + 3];
|
||||
invbuf[i + 3] = sub2;
|
||||
tmpbuf[i + 1] += tmpbuf[i + 3];
|
||||
tmpbuf[i + 3] = sub1;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4096; i += 2) {
|
||||
sub1 = invbuf[i + 0] - invbuf[i + 1];
|
||||
invbuf[i + 0] += invbuf[i + 1];
|
||||
invbuf[i + 1] = sub1;
|
||||
|
||||
sub2 = tmpbuf[i + 0] - tmpbuf[i + 1];
|
||||
tmpbuf[i + 0] += tmpbuf[i + 1];
|
||||
tmpbuf[i + 1] = sub2;
|
||||
}
|
||||
|
||||
for (i = 1, j = 0; i < 4096 - 1; i++) {
|
||||
for (pow = 4096 / 2; pow <= j; pow /= 2) {
|
||||
j -= pow;
|
||||
}
|
||||
j += pow;
|
||||
|
||||
if (i < j) {
|
||||
sub1 = invbuf[j];
|
||||
invbuf[j] = invbuf[i];
|
||||
invbuf[i] = sub1;
|
||||
|
||||
sub2 = tmpbuf[j];
|
||||
tmpbuf[j] = tmpbuf[i];
|
||||
tmpbuf[i] = sub2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void scale(const uint8_t* intbuf, const int* scales, int32_t* invbuf, int32_t* tmpbuf) {
|
||||
int i, j;
|
||||
|
||||
/* reinterleave and scale intbuf into invbuf and tmpbuf */
|
||||
for (i = 0, j = 0; i < 4096 / 2; i++, j += 16) {
|
||||
int scale, qv1, qv2;
|
||||
|
||||
scale = scales[j / 4096];
|
||||
|
||||
qv1 = (intbuf[i*4 + 0] << 0) | (intbuf[i*4 + 1] << 8); /* get_u16le */
|
||||
qv2 = (intbuf[i*4 + 2] << 0) | (intbuf[i*4 + 3] << 8); /* get_u16le */
|
||||
|
||||
/* lowest bit is short of "positive" flag, or rather: even=0..-32767, odd=1..32768
|
||||
* (originally done through a LUT init at runtime with all 65536 indexes) */
|
||||
qv1 = (qv1 & 1) ? (qv1 >> 1) + 1 : -(qv1 >> 1);
|
||||
qv2 = (qv2 & 1) ? (qv2 >> 1) + 1 : -(qv2 >> 1);
|
||||
|
||||
invbuf[i] = scale * qv1;
|
||||
tmpbuf[i] = scale * qv2;
|
||||
}
|
||||
|
||||
/* reset rest of invbuf/tmpbuf */
|
||||
for (i = 4096 / 2; i < 4096; i++) {
|
||||
invbuf[i] = 0;
|
||||
tmpbuf[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void interleave(const uint8_t* decbuf, uint8_t* intbuf) {
|
||||
int i, j;
|
||||
|
||||
/* reorder odd decbuf bytes into intbuf */
|
||||
for (i = 0, j = 1; i < 0x1000; i++, j += 2) {
|
||||
intbuf[j] = decbuf[i];
|
||||
}
|
||||
|
||||
/* reorder even decbuf bytes into intbuf */
|
||||
for (i = 0x1000, j = 0; i < 0x1800; i++, j += 4) {
|
||||
uint8_t lo = decbuf[i + 0x800];
|
||||
uint8_t hi = decbuf[i];
|
||||
|
||||
intbuf[j + 0] = (hi & 0xF0) | (lo >> 4);
|
||||
intbuf[j + 2] = (hi << 4) | (lo & 0x0F);
|
||||
}
|
||||
}
|
||||
|
||||
/* ************************************************************ */
|
||||
/* API */
|
||||
/* ************************************************************ */
|
||||
|
||||
circus_handle_t* circus_init(off_t start, uint8_t codec, uint8_t flags) {
|
||||
circus_handle_t* handle = NULL;
|
||||
int scale_index, err;
|
||||
|
||||
handle = malloc(sizeof(circus_handle_t));
|
||||
if (!handle) goto fail;
|
||||
|
||||
handle->start = start;
|
||||
handle->codec = codec; //(config >> 0) & 0xFF;
|
||||
handle->flags = flags; //(config >> 8) & 0xFF;
|
||||
|
||||
scale_index = (handle->flags & 0xF);
|
||||
if (scale_index > 5) goto fail;
|
||||
handle->scales = scale_table[scale_index];
|
||||
|
||||
if (handle->codec == XPCM_CODEC_VQ_DEFLATE) {
|
||||
memset(&handle->dstrm, 0, sizeof(z_stream));
|
||||
err = inflateInit(&handle->dstrm);
|
||||
if (err < 0) goto fail;
|
||||
}
|
||||
|
||||
circus_reset(handle);
|
||||
|
||||
return handle;
|
||||
fail:
|
||||
circus_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void circus_free(circus_handle_t* handle) {
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
if (handle->codec == XPCM_CODEC_VQ_DEFLATE) {
|
||||
inflateEnd(&handle->dstrm);
|
||||
}
|
||||
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void circus_reset(circus_handle_t* handle) {
|
||||
if (!handle)
|
||||
return;
|
||||
handle->hist1 = 0;
|
||||
handle->hist2 = 0;
|
||||
handle->frame = 0;
|
||||
|
||||
if (handle->codec == XPCM_CODEC_VQ_LZXPCM) {
|
||||
lzxpcm_reset(&handle->lstrm);
|
||||
} else if (handle->codec == XPCM_CODEC_VQ_DEFLATE) {
|
||||
inflateReset(&handle->dstrm);
|
||||
}
|
||||
handle->offset = handle->start;
|
||||
}
|
||||
|
||||
static int decompress_frame_lzxpcm(circus_handle_t* handle, STREAMFILE* sf) {
|
||||
int res;
|
||||
|
||||
handle->lstrm.next_out = handle->decbuf;
|
||||
handle->lstrm.avail_out = sizeof(handle->decbuf);
|
||||
handle->lstrm.total_out = 0;
|
||||
do {
|
||||
if (handle->lstrm.avail_in == 0) {
|
||||
handle->lstrm.next_in = handle->srcbuf;
|
||||
handle->lstrm.avail_in = read_streamfile(handle->srcbuf, handle->offset, sizeof(handle->srcbuf), sf);
|
||||
handle->offset += handle->lstrm.avail_in;
|
||||
|
||||
/* EOF (game reserves some extra buf so memset'ing is probably equivalent) */
|
||||
if (handle->lstrm.avail_in == 0) {
|
||||
memset(handle->decbuf + handle->lstrm.total_out, 0, sizeof(handle->decbuf) - handle->dstrm.total_out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
res = lzxpcm_decompress(&handle->lstrm);
|
||||
if (res != LZXPCM_OK)
|
||||
goto fail;
|
||||
}
|
||||
while(handle->lstrm.avail_out != 0);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decompress_frame_deflate(circus_handle_t* handle, STREAMFILE* sf) {
|
||||
int res;
|
||||
|
||||
handle->dstrm.next_out = handle->decbuf;
|
||||
handle->dstrm.avail_out = sizeof(handle->decbuf);
|
||||
handle->dstrm.total_out = 0;
|
||||
do {
|
||||
if (handle->dstrm.avail_in == 0) {
|
||||
handle->dstrm.next_in = handle->srcbuf;
|
||||
handle->dstrm.avail_in = read_streamfile(handle->srcbuf, handle->offset, sizeof(handle->srcbuf), sf);
|
||||
handle->offset += handle->dstrm.avail_in;
|
||||
|
||||
/* EOF (game reserves some extra buf so memset'ing is probably equivalent) */
|
||||
if (handle->dstrm.avail_in == 0) {
|
||||
memset(handle->decbuf + handle->dstrm.total_out, 0, sizeof(handle->decbuf) - handle->dstrm.total_out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
res = inflate(&handle->dstrm, Z_NO_FLUSH);
|
||||
if (res != Z_OK && res != Z_STREAM_END)
|
||||
goto fail;
|
||||
}
|
||||
while(handle->dstrm.avail_out != 0);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef XPCM_ALT
|
||||
/* original code uses zlib 1.2.1 to decompress the full stream into memory */
|
||||
static int deflate_decompress_full(uint8_t* dst, size_t dst_size, const uint8_t* src, size_t src_size) {
|
||||
int err;
|
||||
z_stream strm = {0};
|
||||
strm.next_in = src;
|
||||
strm.avail_in = src_size;
|
||||
strm.next_out = dst;
|
||||
strm.avail_out = dst_size;
|
||||
|
||||
err = inflateInit(&strm);
|
||||
if (err < 0) {
|
||||
//printf("inflateInit error: %i\n", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = inflate(&strm, Z_FINISH);
|
||||
if (err < 0) {
|
||||
//printf("inflate error: %i\n", err);
|
||||
//return 0;
|
||||
}
|
||||
|
||||
err = inflateEnd(&strm);
|
||||
if (err < 0) {
|
||||
//printf("inflateEnd error: %i\n", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int circus_decode_frame(circus_handle_t* handle, STREAMFILE* sf, int16_t** p_buf, int* p_buf_samples_all) {
|
||||
int ok;
|
||||
|
||||
if (handle->codec == XPCM_CODEC_VQ_LZXPCM) {
|
||||
ok = decompress_frame_lzxpcm(handle, sf);
|
||||
} else if (handle->codec == XPCM_CODEC_VQ_DEFLATE) {
|
||||
ok = decompress_frame_deflate(handle, sf);
|
||||
} else {
|
||||
ok = 0;
|
||||
}
|
||||
if (!ok)
|
||||
goto fail;
|
||||
|
||||
interleave(handle->decbuf, handle->intbuf);
|
||||
scale(handle->intbuf, handle->scales, handle->invbuf, handle->tmpbuf);
|
||||
transform(handle->invbuf, handle->tmpbuf);
|
||||
convert(handle->flags, handle->invbuf, handle->pcmbuf, &handle->hist1, &handle->hist2, handle->frame);
|
||||
handle->frame++;
|
||||
|
||||
*p_buf = handle->pcmbuf;
|
||||
*p_buf_samples_all = XPCM_FRAME_SAMPLES_ALL;
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
16
src/coding/circus_decoder_lib.h
Normal file
16
src/coding/circus_decoder_lib.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef _CIRCUS_DECODER_LIB_H_
|
||||
#define _CIRCUS_DECODER_LIB_H_
|
||||
|
||||
#include "../streamfile.h"
|
||||
|
||||
typedef struct circus_handle_t circus_handle_t;
|
||||
|
||||
circus_handle_t* circus_init(off_t start, uint8_t codec, uint8_t flags);
|
||||
|
||||
void circus_free(circus_handle_t* handle);
|
||||
|
||||
void circus_reset(circus_handle_t* handle);
|
||||
|
||||
int circus_decode_frame(circus_handle_t* handle, STREAMFILE* sf, int16_t** p_buf, int* p_buf_samples_all);
|
||||
|
||||
#endif
|
347
src/coding/circus_decoder_lib_data.h
Normal file
347
src/coding/circus_decoder_lib_data.h
Normal file
@ -0,0 +1,347 @@
|
||||
#ifndef _CIRCUS_DECODER_LIB_DATA_H_
|
||||
#define _CIRCUS_DECODER_LIB_DATA_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* sin-cosine LUT, seems generated with: */
|
||||
/* table[i] = round( sin(2.0 * PI * i / 1024.0) * 1000.0 ) */
|
||||
static const int sincos_table[1024*5 + 1] = {
|
||||
0, 6, 12, 18, 25, 31, 37, 43, 50, 56, 62, 69, 75, 81, 87, 94,
|
||||
100, 106, 113, 119, 125, 131, 138, 144, 150, 157, 163, 169, 175, 182, 188, 194,
|
||||
200, 207, 213, 219, 226, 232, 238, 244, 251, 257, 263, 269, 276, 282, 288, 295,
|
||||
301, 307, 313, 320, 326, 332, 338, 345, 351, 357, 363, 370, 376, 382, 388, 395,
|
||||
401, 407, 413, 420, 426, 432, 438, 445, 451, 457, 463, 470, 476, 482, 488, 495,
|
||||
501, 507, 513, 520, 526, 532, 538, 545, 551, 557, 563, 569, 576, 582, 588, 594,
|
||||
601, 607, 613, 619, 625, 632, 638, 644, 650, 656, 663, 669, 675, 681, 687, 694,
|
||||
700, 706, 712, 718, 725, 731, 737, 743, 749, 755, 762, 768, 774, 780, 786, 792,
|
||||
799, 805, 811, 817, 823, 829, 836, 842, 848, 854, 860, 866, 872, 879, 885, 891,
|
||||
897, 903, 909, 915, 921, 928, 934, 940, 946, 952, 958, 964, 970, 976, 983, 989,
|
||||
995, 1001, 1007, 1013, 1019, 1025, 1031, 1037, 1043, 1050, 1056, 1062, 1068, 1074, 1080, 1086,
|
||||
1092, 1098, 1104, 1110, 1116, 1122, 1128, 1134, 1140, 1146, 1152, 1158, 1164, 1170, 1176, 1182,
|
||||
1189, 1195, 1201, 1207, 1213, 1219, 1225, 1231, 1237, 1243, 1248, 1254, 1260, 1266, 1272, 1278,
|
||||
1284, 1290, 1296, 1302, 1308, 1314, 1320, 1326, 1332, 1338, 1344, 1350, 1356, 1362, 1368, 1373,
|
||||
1379, 1385, 1391, 1397, 1403, 1409, 1415, 1421, 1427, 1433, 1438, 1444, 1450, 1456, 1462, 1468,
|
||||
1474, 1479, 1485, 1491, 1497, 1503, 1509, 1515, 1520, 1526, 1532, 1538, 1544, 1550, 1555, 1561,
|
||||
1567, 1573, 1579, 1584, 1590, 1596, 1602, 1608, 1613, 1619, 1625, 1631, 1636, 1642, 1648, 1654,
|
||||
1659, 1665, 1671, 1677, 1682, 1688, 1694, 1699, 1705, 1711, 1717, 1722, 1728, 1734, 1739, 1745,
|
||||
1751, 1756, 1762, 1768, 1773, 1779, 1785, 1790, 1796, 1802, 1807, 1813, 1819, 1824, 1830, 1835,
|
||||
1841, 1847, 1852, 1858, 1864, 1869, 1875, 1880, 1886, 1891, 1897, 1903, 1908, 1914, 1919, 1925,
|
||||
1930, 1936, 1941, 1947, 1952, 1958, 1964, 1969, 1975, 1980, 1986, 1991, 1997, 2002, 2007, 2013,
|
||||
2018, 2024, 2029, 2035, 2040, 2046, 2051, 2057, 2062, 2067, 2073, 2078, 2084, 2089, 2094, 2100,
|
||||
2105, 2111, 2116, 2121, 2127, 2132, 2138, 2143, 2148, 2154, 2159, 2164, 2170, 2175, 2180, 2186,
|
||||
2191, 2196, 2201, 2207, 2212, 2217, 2223, 2228, 2233, 2238, 2244, 2249, 2254, 2259, 2265, 2270,
|
||||
2275, 2280, 2286, 2291, 2296, 2301, 2306, 2312, 2317, 2322, 2327, 2332, 2337, 2343, 2348, 2353,
|
||||
2358, 2363, 2368, 2373, 2379, 2384, 2389, 2394, 2399, 2404, 2409, 2414, 2419, 2424, 2429, 2434,
|
||||
2439, 2445, 2450, 2455, 2460, 2465, 2470, 2475, 2480, 2485, 2490, 2495, 2500, 2505, 2510, 2515,
|
||||
2519, 2524, 2529, 2534, 2539, 2544, 2549, 2554, 2559, 2564, 2569, 2574, 2578, 2583, 2588, 2593,
|
||||
2598, 2603, 2608, 2613, 2617, 2622, 2627, 2632, 2637, 2641, 2646, 2651, 2656, 2661, 2665, 2670,
|
||||
2675, 2680, 2684, 2689, 2694, 2699, 2703, 2708, 2713, 2717, 2722, 2727, 2732, 2736, 2741, 2746,
|
||||
2750, 2755, 2760, 2764, 2769, 2773, 2778, 2783, 2787, 2792, 2796, 2801, 2806, 2810, 2815, 2819,
|
||||
2824, 2828, 2833, 2837, 2842, 2847, 2851, 2856, 2860, 2865, 2869, 2874, 2878, 2882, 2887, 2891,
|
||||
2896, 2900, 2905, 2909, 2914, 2918, 2922, 2927, 2931, 2936, 2940, 2944, 2949, 2953, 2957, 2962,
|
||||
2966, 2970, 2975, 2979, 2983, 2988, 2992, 2996, 3000, 3005, 3009, 3013, 3018, 3022, 3026, 3030,
|
||||
3034, 3039, 3043, 3047, 3051, 3055, 3060, 3064, 3068, 3072, 3076, 3080, 3085, 3089, 3093, 3097,
|
||||
3101, 3105, 3109, 3113, 3117, 3121, 3126, 3130, 3134, 3138, 3142, 3146, 3150, 3154, 3158, 3162,
|
||||
3166, 3170, 3174, 3178, 3182, 3186, 3190, 3193, 3197, 3201, 3205, 3209, 3213, 3217, 3221, 3225,
|
||||
3229, 3232, 3236, 3240, 3244, 3248, 3252, 3255, 3259, 3263, 3267, 3271, 3274, 3278, 3282, 3286,
|
||||
3289, 3293, 3297, 3301, 3304, 3308, 3312, 3315, 3319, 3323, 3326, 3330, 3334, 3337, 3341, 3345,
|
||||
3348, 3352, 3356, 3359, 3363, 3366, 3370, 3373, 3377, 3381, 3384, 3388, 3391, 3395, 3398, 3402,
|
||||
3405, 3409, 3412, 3416, 3419, 3423, 3426, 3429, 3433, 3436, 3440, 3443, 3447, 3450, 3453, 3457,
|
||||
3460, 3463, 3467, 3470, 3473, 3477, 3480, 3483, 3487, 3490, 3493, 3497, 3500, 3503, 3506, 3510,
|
||||
3513, 3516, 3519, 3522, 3526, 3529, 3532, 3535, 3538, 3541, 3545, 3548, 3551, 3554, 3557, 3560,
|
||||
3563, 3566, 3570, 3573, 3576, 3579, 3582, 3585, 3588, 3591, 3594, 3597, 3600, 3603, 3606, 3609,
|
||||
3612, 3615, 3618, 3621, 3624, 3627, 3629, 3632, 3635, 3638, 3641, 3644, 3647, 3650, 3652, 3655,
|
||||
3658, 3661, 3664, 3667, 3669, 3672, 3675, 3678, 3680, 3683, 3686, 3689, 3691, 3694, 3697, 3700,
|
||||
3702, 3705, 3708, 3710, 3713, 3716, 3718, 3721, 3723, 3726, 3729, 3731, 3734, 3736, 3739, 3742,
|
||||
3744, 3747, 3749, 3752, 3754, 3757, 3759, 3762, 3764, 3767, 3769, 3772, 3774, 3776, 3779, 3781,
|
||||
3784, 3786, 3789, 3791, 3793, 3796, 3798, 3800, 3803, 3805, 3807, 3810, 3812, 3814, 3816, 3819,
|
||||
3821, 3823, 3826, 3828, 3830, 3832, 3834, 3837, 3839, 3841, 3843, 3845, 3848, 3850, 3852, 3854,
|
||||
3856, 3858, 3860, 3862, 3864, 3867, 3869, 3871, 3873, 3875, 3877, 3879, 3881, 3883, 3885, 3887,
|
||||
3889, 3891, 3893, 3895, 3897, 3899, 3900, 3902, 3904, 3906, 3908, 3910, 3912, 3914, 3915, 3917,
|
||||
3919, 3921, 3923, 3925, 3926, 3928, 3930, 3932, 3933, 3935, 3937, 3939, 3940, 3942, 3944, 3945,
|
||||
3947, 3949, 3950, 3952, 3954, 3955, 3957, 3959, 3960, 3962, 3963, 3965, 3967, 3968, 3970, 3971,
|
||||
3973, 3974, 3976, 3977, 3979, 3980, 3982, 3983, 3985, 3986, 3988, 3989, 3990, 3992, 3993, 3995,
|
||||
3996, 3997, 3999, 4000, 4001, 4003, 4004, 4005, 4007, 4008, 4009, 4011, 4012, 4013, 4014, 4016,
|
||||
4017, 4018, 4019, 4020, 4022, 4023, 4024, 4025, 4026, 4027, 4029, 4030, 4031, 4032, 4033, 4034,
|
||||
4035, 4036, 4037, 4038, 4039, 4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048, 4049, 4050,
|
||||
4051, 4052, 4053, 4054, 4055, 4056, 4057, 4057, 4058, 4059, 4060, 4061, 4062, 4062, 4063, 4064,
|
||||
4065, 4065, 4066, 4067, 4068, 4068, 4069, 4070, 4071, 4071, 4072, 4073, 4073, 4074, 4075, 4075,
|
||||
4076, 4076, 4077, 4078, 4078, 4079, 4079, 4080, 4080, 4081, 4081, 4082, 4082, 4083, 4083, 4084,
|
||||
4084, 4085, 4085, 4086, 4086, 4087, 4087, 4087, 4088, 4088, 4089, 4089, 4089, 4090, 4090, 4090,
|
||||
4091, 4091, 4091, 4091, 4092, 4092, 4092, 4092, 4093, 4093, 4093, 4093, 4094, 4094, 4094, 4094,
|
||||
4094, 4094, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095,
|
||||
|
||||
4096, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4094,
|
||||
4094, 4094, 4094, 4094, 4094, 4093, 4093, 4093, 4093, 4092, 4092, 4092, 4092, 4091, 4091, 4091,
|
||||
4091, 4090, 4090, 4090, 4089, 4089, 4089, 4088, 4088, 4087, 4087, 4087, 4086, 4086, 4085, 4085,
|
||||
4084, 4084, 4083, 4083, 4082, 4082, 4081, 4081, 4080, 4080, 4079, 4079, 4078, 4078, 4077, 4076,
|
||||
4076, 4075, 4075, 4074, 4073, 4073, 4072, 4071, 4071, 4070, 4069, 4068, 4068, 4067, 4066, 4065,
|
||||
4065, 4064, 4063, 4062, 4062, 4061, 4060, 4059, 4058, 4057, 4057, 4056, 4055, 4054, 4053, 4052,
|
||||
4051, 4050, 4049, 4048, 4047, 4046, 4045, 4044, 4043, 4042, 4041, 4040, 4039, 4038, 4037, 4036,
|
||||
4035, 4034, 4033, 4032, 4031, 4030, 4029, 4027, 4026, 4025, 4024, 4023, 4022, 4020, 4019, 4018,
|
||||
4017, 4016, 4014, 4013, 4012, 4011, 4009, 4008, 4007, 4005, 4004, 4003, 4001, 4000, 3999, 3997,
|
||||
3996, 3995, 3993, 3992, 3990, 3989, 3988, 3986, 3985, 3983, 3982, 3980, 3979, 3977, 3976, 3974,
|
||||
3973, 3971, 3970, 3968, 3967, 3965, 3963, 3962, 3960, 3959, 3957, 3955, 3954, 3952, 3950, 3949,
|
||||
3947, 3945, 3944, 3942, 3940, 3939, 3937, 3935, 3933, 3932, 3930, 3928, 3926, 3925, 3923, 3921,
|
||||
3919, 3917, 3915, 3914, 3912, 3910, 3908, 3906, 3904, 3902, 3900, 3899, 3897, 3895, 3893, 3891,
|
||||
3889, 3887, 3885, 3883, 3881, 3879, 3877, 3875, 3873, 3871, 3869, 3867, 3864, 3862, 3860, 3858,
|
||||
3856, 3854, 3852, 3850, 3848, 3845, 3843, 3841, 3839, 3837, 3834, 3832, 3830, 3828, 3826, 3823,
|
||||
3821, 3819, 3816, 3814, 3812, 3810, 3807, 3805, 3803, 3800, 3798, 3796, 3793, 3791, 3789, 3786,
|
||||
3784, 3781, 3779, 3776, 3774, 3772, 3769, 3767, 3764, 3762, 3759, 3757, 3754, 3752, 3749, 3747,
|
||||
3744, 3742, 3739, 3736, 3734, 3731, 3729, 3726, 3723, 3721, 3718, 3716, 3713, 3710, 3708, 3705,
|
||||
3702, 3700, 3697, 3694, 3691, 3689, 3686, 3683, 3680, 3678, 3675, 3672, 3669, 3667, 3664, 3661,
|
||||
3658, 3655, 3652, 3650, 3647, 3644, 3641, 3638, 3635, 3632, 3629, 3627, 3624, 3621, 3618, 3615,
|
||||
3612, 3609, 3606, 3603, 3600, 3597, 3594, 3591, 3588, 3585, 3582, 3579, 3576, 3573, 3570, 3566,
|
||||
3563, 3560, 3557, 3554, 3551, 3548, 3545, 3541, 3538, 3535, 3532, 3529, 3526, 3522, 3519, 3516,
|
||||
3513, 3510, 3506, 3503, 3500, 3497, 3493, 3490, 3487, 3483, 3480, 3477, 3473, 3470, 3467, 3463,
|
||||
3460, 3457, 3453, 3450, 3447, 3443, 3440, 3436, 3433, 3429, 3426, 3423, 3419, 3416, 3412, 3409,
|
||||
3405, 3402, 3398, 3395, 3391, 3388, 3384, 3381, 3377, 3373, 3370, 3366, 3363, 3359, 3356, 3352,
|
||||
3348, 3345, 3341, 3337, 3334, 3330, 3326, 3323, 3319, 3315, 3312, 3308, 3304, 3301, 3297, 3293,
|
||||
3289, 3286, 3282, 3278, 3274, 3271, 3267, 3263, 3259, 3255, 3252, 3248, 3244, 3240, 3236, 3232,
|
||||
3229, 3225, 3221, 3217, 3213, 3209, 3205, 3201, 3197, 3193, 3190, 3186, 3182, 3178, 3174, 3170,
|
||||
3166, 3162, 3158, 3154, 3150, 3146, 3142, 3138, 3134, 3130, 3126, 3121, 3117, 3113, 3109, 3105,
|
||||
3101, 3097, 3093, 3089, 3085, 3080, 3076, 3072, 3068, 3064, 3060, 3055, 3051, 3047, 3043, 3039,
|
||||
3034, 3030, 3026, 3022, 3018, 3013, 3009, 3005, 3000, 2996, 2992, 2988, 2983, 2979, 2975, 2970,
|
||||
2966, 2962, 2957, 2953, 2949, 2944, 2940, 2936, 2931, 2927, 2922, 2918, 2914, 2909, 2905, 2900,
|
||||
2896, 2891, 2887, 2882, 2878, 2874, 2869, 2865, 2860, 2856, 2851, 2847, 2842, 2837, 2833, 2828,
|
||||
2824, 2819, 2815, 2810, 2806, 2801, 2796, 2792, 2787, 2783, 2778, 2773, 2769, 2764, 2760, 2755,
|
||||
2750, 2746, 2741, 2736, 2732, 2727, 2722, 2717, 2713, 2708, 2703, 2699, 2694, 2689, 2684, 2680,
|
||||
2675, 2670, 2665, 2661, 2656, 2651, 2646, 2641, 2637, 2632, 2627, 2622, 2617, 2613, 2608, 2603,
|
||||
2598, 2593, 2588, 2583, 2578, 2574, 2569, 2564, 2559, 2554, 2549, 2544, 2539, 2534, 2529, 2524,
|
||||
2519, 2515, 2510, 2505, 2500, 2495, 2490, 2485, 2480, 2475, 2470, 2465, 2460, 2455, 2450, 2445,
|
||||
2439, 2434, 2429, 2424, 2419, 2414, 2409, 2404, 2399, 2394, 2389, 2384, 2379, 2373, 2368, 2363,
|
||||
2358, 2353, 2348, 2343, 2337, 2332, 2327, 2322, 2317, 2312, 2306, 2301, 2296, 2291, 2286, 2280,
|
||||
2275, 2270, 2265, 2259, 2254, 2249, 2244, 2238, 2233, 2228, 2223, 2217, 2212, 2207, 2201, 2196,
|
||||
2191, 2186, 2180, 2175, 2170, 2164, 2159, 2154, 2148, 2143, 2138, 2132, 2127, 2121, 2116, 2111,
|
||||
2105, 2100, 2094, 2089, 2084, 2078, 2073, 2067, 2062, 2057, 2051, 2046, 2040, 2035, 2029, 2024,
|
||||
2018, 2013, 2007, 2002, 1997, 1991, 1986, 1980, 1975, 1969, 1964, 1958, 1952, 1947, 1941, 1936,
|
||||
1930, 1925, 1919, 1914, 1908, 1903, 1897, 1891, 1886, 1880, 1875, 1869, 1864, 1858, 1852, 1847,
|
||||
1841, 1835, 1830, 1824, 1819, 1813, 1807, 1802, 1796, 1790, 1785, 1779, 1773, 1768, 1762, 1756,
|
||||
1751, 1745, 1739, 1734, 1728, 1722, 1717, 1711, 1705, 1699, 1694, 1688, 1682, 1677, 1671, 1665,
|
||||
1659, 1654, 1648, 1642, 1636, 1631, 1625, 1619, 1613, 1608, 1602, 1596, 1590, 1584, 1579, 1573,
|
||||
1567, 1561, 1555, 1550, 1544, 1538, 1532, 1526, 1520, 1515, 1509, 1503, 1497, 1491, 1485, 1479,
|
||||
1474, 1468, 1462, 1456, 1450, 1444, 1438, 1433, 1427, 1421, 1415, 1409, 1403, 1397, 1391, 1385,
|
||||
1379, 1373, 1368, 1362, 1356, 1350, 1344, 1338, 1332, 1326, 1320, 1314, 1308, 1302, 1296, 1290,
|
||||
1284, 1278, 1272, 1266, 1260, 1254, 1248, 1243, 1237, 1231, 1225, 1219, 1213, 1207, 1201, 1195,
|
||||
1189, 1182, 1176, 1170, 1164, 1158, 1152, 1146, 1140, 1134, 1128, 1122, 1116, 1110, 1104, 1098,
|
||||
1092, 1086, 1080, 1074, 1068, 1062, 1056, 1050, 1043, 1037, 1031, 1025, 1019, 1013, 1007, 1001,
|
||||
995, 989, 983, 976, 970, 964, 958, 952, 946, 940, 934, 928, 921, 915, 909, 903,
|
||||
897, 891, 885, 879, 872, 866, 860, 854, 848, 842, 836, 829, 823, 817, 811, 805,
|
||||
799, 792, 786, 780, 774, 768, 762, 755, 749, 743, 737, 731, 725, 718, 712, 706,
|
||||
700, 694, 687, 681, 675, 669, 663, 656, 650, 644, 638, 632, 625, 619, 613, 607,
|
||||
601, 594, 588, 582, 576, 569, 563, 557, 551, 545, 538, 532, 526, 520, 513, 507,
|
||||
501, 495, 488, 482, 476, 470, 463, 457, 451, 445, 438, 432, 426, 420, 413, 407,
|
||||
401, 395, 388, 382, 376, 370, 363, 357, 351, 345, 338, 332, 326, 320, 313, 307,
|
||||
301, 295, 288, 282, 276, 269, 263, 257, 251, 244, 238, 232, 226, 219, 213, 207,
|
||||
200, 194, 188, 182, 175, 169, 163, 157, 150, 144, 138, 131, 125, 119, 113, 106,
|
||||
100, 94, 87, 81, 75, 69, 62, 56, 50, 43, 37, 31, 25, 18, 12, 6,
|
||||
|
||||
0, -6, -12, -18, -25, -31, -37, -43, -50, -56, -62, -69, -75, -81, -87, -94,
|
||||
-100, -106, -113, -119, -125, -131, -138, -144, -150, -157, -163, -169, -175, -182, -188, -194,
|
||||
-200, -207, -213, -219, -226, -232, -238, -244, -251, -257, -263, -269, -276, -282, -288, -295,
|
||||
-301, -307, -313, -320, -326, -332, -338, -345, -351, -357, -363, -370, -376, -382, -388, -395,
|
||||
-401, -407, -413, -420, -426, -432, -438, -445, -451, -457, -463, -470, -476, -482, -488, -495,
|
||||
-501, -507, -513, -520, -526, -532, -538, -545, -551, -557, -563, -569, -576, -582, -588, -594,
|
||||
-601, -607, -613, -619, -625, -632, -638, -644, -650, -656, -663, -669, -675, -681, -687, -694,
|
||||
-700, -706, -712, -718, -725, -731, -737, -743, -749, -755, -762, -768, -774, -780, -786, -792,
|
||||
-799, -805, -811, -817, -823, -829, -836, -842, -848, -854, -860, -866, -872, -879, -885, -891,
|
||||
-897, -903, -909, -915, -921, -928, -934, -940, -946, -952, -958, -964, -970, -976, -983, -989,
|
||||
-995,-1001,-1007,-1013,-1019,-1025,-1031,-1037,-1043,-1050,-1056,-1062,-1068,-1074,-1080,-1086,
|
||||
-1092,-1098,-1104,-1110,-1116,-1122,-1128,-1134,-1140,-1146,-1152,-1158,-1164,-1170,-1176,-1182,
|
||||
-1189,-1195,-1201,-1207,-1213,-1219,-1225,-1231,-1237,-1243,-1248,-1254,-1260,-1266,-1272,-1278,
|
||||
-1284,-1290,-1296,-1302,-1308,-1314,-1320,-1326,-1332,-1338,-1344,-1350,-1356,-1362,-1368,-1373,
|
||||
-1379,-1385,-1391,-1397,-1403,-1409,-1415,-1421,-1427,-1433,-1438,-1444,-1450,-1456,-1462,-1468,
|
||||
-1474,-1479,-1485,-1491,-1497,-1503,-1509,-1515,-1520,-1526,-1532,-1538,-1544,-1550,-1555,-1561,
|
||||
-1567,-1573,-1579,-1584,-1590,-1596,-1602,-1608,-1613,-1619,-1625,-1631,-1636,-1642,-1648,-1654,
|
||||
-1659,-1665,-1671,-1677,-1682,-1688,-1694,-1699,-1705,-1711,-1717,-1722,-1728,-1734,-1739,-1745,
|
||||
-1751,-1756,-1762,-1768,-1773,-1779,-1785,-1790,-1796,-1802,-1807,-1813,-1819,-1824,-1830,-1835,
|
||||
-1841,-1847,-1852,-1858,-1864,-1869,-1875,-1880,-1886,-1891,-1897,-1903,-1908,-1914,-1919,-1925,
|
||||
-1930,-1936,-1941,-1947,-1952,-1958,-1964,-1969,-1975,-1980,-1986,-1991,-1997,-2002,-2007,-2013,
|
||||
-2018,-2024,-2029,-2035,-2040,-2046,-2051,-2057,-2062,-2067,-2073,-2078,-2084,-2089,-2094,-2100,
|
||||
-2105,-2111,-2116,-2121,-2127,-2132,-2138,-2143,-2148,-2154,-2159,-2164,-2170,-2175,-2180,-2186,
|
||||
-2191,-2196,-2201,-2207,-2212,-2217,-2223,-2228,-2233,-2238,-2244,-2249,-2254,-2259,-2265,-2270,
|
||||
-2275,-2280,-2286,-2291,-2296,-2301,-2306,-2312,-2317,-2322,-2327,-2332,-2337,-2343,-2348,-2353,
|
||||
-2358,-2363,-2368,-2373,-2379,-2384,-2389,-2394,-2399,-2404,-2409,-2414,-2419,-2424,-2429,-2434,
|
||||
-2439,-2445,-2450,-2455,-2460,-2465,-2470,-2475,-2480,-2485,-2490,-2495,-2500,-2505,-2510,-2515,
|
||||
-2519,-2524,-2529,-2534,-2539,-2544,-2549,-2554,-2559,-2564,-2569,-2574,-2578,-2583,-2588,-2593,
|
||||
-2598,-2603,-2608,-2613,-2617,-2622,-2627,-2632,-2637,-2641,-2646,-2651,-2656,-2661,-2665,-2670,
|
||||
-2675,-2680,-2684,-2689,-2694,-2699,-2703,-2708,-2713,-2717,-2722,-2727,-2732,-2736,-2741,-2746,
|
||||
-2750,-2755,-2760,-2764,-2769,-2773,-2778,-2783,-2787,-2792,-2796,-2801,-2806,-2810,-2815,-2819,
|
||||
-2824,-2828,-2833,-2837,-2842,-2847,-2851,-2856,-2860,-2865,-2869,-2874,-2878,-2882,-2887,-2891,
|
||||
-2896,-2900,-2905,-2909,-2914,-2918,-2922,-2927,-2931,-2936,-2940,-2944,-2949,-2953,-2957,-2962,
|
||||
-2966,-2970,-2975,-2979,-2983,-2988,-2992,-2996,-3000,-3005,-3009,-3013,-3018,-3022,-3026,-3030,
|
||||
-3034,-3039,-3043,-3047,-3051,-3055,-3060,-3064,-3068,-3072,-3076,-3080,-3085,-3089,-3093,-3097,
|
||||
-3101,-3105,-3109,-3113,-3117,-3121,-3126,-3130,-3134,-3138,-3142,-3146,-3150,-3154,-3158,-3162,
|
||||
-3166,-3170,-3174,-3178,-3182,-3186,-3190,-3193,-3197,-3201,-3205,-3209,-3213,-3217,-3221,-3225,
|
||||
-3229,-3232,-3236,-3240,-3244,-3248,-3252,-3255,-3259,-3263,-3267,-3271,-3274,-3278,-3282,-3286,
|
||||
-3289,-3293,-3297,-3301,-3304,-3308,-3312,-3315,-3319,-3323,-3326,-3330,-3334,-3337,-3341,-3345,
|
||||
-3348,-3352,-3356,-3359,-3363,-3366,-3370,-3373,-3377,-3381,-3384,-3388,-3391,-3395,-3398,-3402,
|
||||
-3405,-3409,-3412,-3416,-3419,-3423,-3426,-3429,-3433,-3436,-3440,-3443,-3447,-3450,-3453,-3457,
|
||||
-3460,-3463,-3467,-3470,-3473,-3477,-3480,-3483,-3487,-3490,-3493,-3497,-3500,-3503,-3506,-3510,
|
||||
-3513,-3516,-3519,-3522,-3526,-3529,-3532,-3535,-3538,-3541,-3545,-3548,-3551,-3554,-3557,-3560,
|
||||
-3563,-3566,-3570,-3573,-3576,-3579,-3582,-3585,-3588,-3591,-3594,-3597,-3600,-3603,-3606,-3609,
|
||||
-3612,-3615,-3618,-3621,-3624,-3627,-3629,-3632,-3635,-3638,-3641,-3644,-3647,-3650,-3652,-3655,
|
||||
-3658,-3661,-3664,-3667,-3669,-3672,-3675,-3678,-3680,-3683,-3686,-3689,-3691,-3694,-3697,-3700,
|
||||
-3702,-3705,-3708,-3710,-3713,-3716,-3718,-3721,-3723,-3726,-3729,-3731,-3734,-3736,-3739,-3742,
|
||||
-3744,-3747,-3749,-3752,-3754,-3757,-3759,-3762,-3764,-3767,-3769,-3772,-3774,-3776,-3779,-3781,
|
||||
-3784,-3786,-3789,-3791,-3793,-3796,-3798,-3800,-3803,-3805,-3807,-3810,-3812,-3814,-3816,-3819,
|
||||
-3821,-3823,-3826,-3828,-3830,-3832,-3834,-3837,-3839,-3841,-3843,-3845,-3848,-3850,-3852,-3854,
|
||||
-3856,-3858,-3860,-3862,-3864,-3867,-3869,-3871,-3873,-3875,-3877,-3879,-3881,-3883,-3885,-3887,
|
||||
-3889,-3891,-3893,-3895,-3897,-3899,-3900,-3902,-3904,-3906,-3908,-3910,-3912,-3914,-3915,-3917,
|
||||
-3919,-3921,-3923,-3925,-3926,-3928,-3930,-3932,-3933,-3935,-3937,-3939,-3940,-3942,-3944,-3945,
|
||||
-3947,-3949,-3950,-3952,-3954,-3955,-3957,-3959,-3960,-3962,-3963,-3965,-3967,-3968,-3970,-3971,
|
||||
-3973,-3974,-3976,-3977,-3979,-3980,-3982,-3983,-3985,-3986,-3988,-3989,-3990,-3992,-3993,-3995,
|
||||
-3996,-3997,-3999,-4000,-4001,-4003,-4004,-4005,-4007,-4008,-4009,-4011,-4012,-4013,-4014,-4016,
|
||||
-4017,-4018,-4019,-4020,-4022,-4023,-4024,-4025,-4026,-4027,-4029,-4030,-4031,-4032,-4033,-4034,
|
||||
-4035,-4036,-4037,-4038,-4039,-4040,-4041,-4042,-4043,-4044,-4045,-4046,-4047,-4048,-4049,-4050,
|
||||
-4051,-4052,-4053,-4054,-4055,-4056,-4057,-4057,-4058,-4059,-4060,-4061,-4062,-4062,-4063,-4064,
|
||||
-4065,-4065,-4066,-4067,-4068,-4068,-4069,-4070,-4071,-4071,-4072,-4073,-4073,-4074,-4075,-4075,
|
||||
-4076,-4076,-4077,-4078,-4078,-4079,-4079,-4080,-4080,-4081,-4081,-4082,-4082,-4083,-4083,-4084,
|
||||
-4084,-4085,-4085,-4086,-4086,-4087,-4087,-4087,-4088,-4088,-4089,-4089,-4089,-4090,-4090,-4090,
|
||||
-4091,-4091,-4091,-4091,-4092,-4092,-4092,-4092,-4093,-4093,-4093,-4093,-4094,-4094,-4094,-4094,
|
||||
-4094,-4094,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,
|
||||
|
||||
-4096,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4095,-4094,
|
||||
-4094,-4094,-4094,-4094,-4094,-4093,-4093,-4093,-4093,-4092,-4092,-4092,-4092,-4091,-4091,-4091,
|
||||
-4091,-4090,-4090,-4090,-4089,-4089,-4089,-4088,-4088,-4087,-4087,-4087,-4086,-4086,-4085,-4085,
|
||||
-4084,-4084,-4083,-4083,-4082,-4082,-4081,-4081,-4080,-4080,-4079,-4079,-4078,-4078,-4077,-4076,
|
||||
-4076,-4075,-4075,-4074,-4073,-4073,-4072,-4071,-4071,-4070,-4069,-4068,-4068,-4067,-4066,-4065,
|
||||
-4065,-4064,-4063,-4062,-4062,-4061,-4060,-4059,-4058,-4057,-4057,-4056,-4055,-4054,-4053,-4052,
|
||||
-4051,-4050,-4049,-4048,-4047,-4046,-4045,-4044,-4043,-4042,-4041,-4040,-4039,-4038,-4037,-4036,
|
||||
-4035,-4034,-4033,-4032,-4031,-4030,-4029,-4027,-4026,-4025,-4024,-4023,-4022,-4020,-4019,-4018,
|
||||
-4017,-4016,-4014,-4013,-4012,-4011,-4009,-4008,-4007,-4005,-4004,-4003,-4001,-4000,-3999,-3997,
|
||||
-3996,-3995,-3993,-3992,-3990,-3989,-3988,-3986,-3985,-3983,-3982,-3980,-3979,-3977,-3976,-3974,
|
||||
-3973,-3971,-3970,-3968,-3967,-3965,-3963,-3962,-3960,-3959,-3957,-3955,-3954,-3952,-3950,-3949,
|
||||
-3947,-3945,-3944,-3942,-3940,-3939,-3937,-3935,-3933,-3932,-3930,-3928,-3926,-3925,-3923,-3921,
|
||||
-3919,-3917,-3915,-3914,-3912,-3910,-3908,-3906,-3904,-3902,-3900,-3899,-3897,-3895,-3893,-3891,
|
||||
-3889,-3887,-3885,-3883,-3881,-3879,-3877,-3875,-3873,-3871,-3869,-3867,-3864,-3862,-3860,-3858,
|
||||
-3856,-3854,-3852,-3850,-3848,-3845,-3843,-3841,-3839,-3837,-3834,-3832,-3830,-3828,-3826,-3823,
|
||||
-3821,-3819,-3816,-3814,-3812,-3810,-3807,-3805,-3803,-3800,-3798,-3796,-3793,-3791,-3789,-3786,
|
||||
-3784,-3781,-3779,-3776,-3774,-3772,-3769,-3767,-3764,-3762,-3759,-3757,-3754,-3752,-3749,-3747,
|
||||
-3744,-3742,-3739,-3736,-3734,-3731,-3729,-3726,-3723,-3721,-3718,-3716,-3713,-3710,-3708,-3705,
|
||||
-3702,-3700,-3697,-3694,-3691,-3689,-3686,-3683,-3680,-3678,-3675,-3672,-3669,-3667,-3664,-3661,
|
||||
-3658,-3655,-3652,-3650,-3647,-3644,-3641,-3638,-3635,-3632,-3629,-3627,-3624,-3621,-3618,-3615,
|
||||
-3612,-3609,-3606,-3603,-3600,-3597,-3594,-3591,-3588,-3585,-3582,-3579,-3576,-3573,-3570,-3566,
|
||||
-3563,-3560,-3557,-3554,-3551,-3548,-3545,-3541,-3538,-3535,-3532,-3529,-3526,-3522,-3519,-3516,
|
||||
-3513,-3510,-3506,-3503,-3500,-3497,-3493,-3490,-3487,-3483,-3480,-3477,-3473,-3470,-3467,-3463,
|
||||
-3460,-3457,-3453,-3450,-3447,-3443,-3440,-3436,-3433,-3429,-3426,-3423,-3419,-3416,-3412,-3409,
|
||||
-3405,-3402,-3398,-3395,-3391,-3388,-3384,-3381,-3377,-3373,-3370,-3366,-3363,-3359,-3356,-3352,
|
||||
-3348,-3345,-3341,-3337,-3334,-3330,-3326,-3323,-3319,-3315,-3312,-3308,-3304,-3301,-3297,-3293,
|
||||
-3289,-3286,-3282,-3278,-3274,-3271,-3267,-3263,-3259,-3255,-3252,-3248,-3244,-3240,-3236,-3232,
|
||||
-3229,-3225,-3221,-3217,-3213,-3209,-3205,-3201,-3197,-3193,-3190,-3186,-3182,-3178,-3174,-3170,
|
||||
-3166,-3162,-3158,-3154,-3150,-3146,-3142,-3138,-3134,-3130,-3126,-3121,-3117,-3113,-3109,-3105,
|
||||
-3101,-3097,-3093,-3089,-3085,-3080,-3076,-3072,-3068,-3064,-3060,-3055,-3051,-3047,-3043,-3039,
|
||||
-3034,-3030,-3026,-3022,-3018,-3013,-3009,-3005,-3000,-2996,-2992,-2988,-2983,-2979,-2975,-2970,
|
||||
-2966,-2962,-2957,-2953,-2949,-2944,-2940,-2936,-2931,-2927,-2922,-2918,-2914,-2909,-2905,-2900,
|
||||
-2896,-2891,-2887,-2882,-2878,-2874,-2869,-2865,-2860,-2856,-2851,-2847,-2842,-2837,-2833,-2828,
|
||||
-2824,-2819,-2815,-2810,-2806,-2801,-2796,-2792,-2787,-2783,-2778,-2773,-2769,-2764,-2760,-2755,
|
||||
-2750,-2746,-2741,-2736,-2732,-2727,-2722,-2717,-2713,-2708,-2703,-2699,-2694,-2689,-2684,-2680,
|
||||
-2675,-2670,-2665,-2661,-2656,-2651,-2646,-2641,-2637,-2632,-2627,-2622,-2617,-2613,-2608,-2603,
|
||||
-2598,-2593,-2588,-2583,-2578,-2574,-2569,-2564,-2559,-2554,-2549,-2544,-2539,-2534,-2529,-2524,
|
||||
-2519,-2515,-2510,-2505,-2500,-2495,-2490,-2485,-2480,-2475,-2470,-2465,-2460,-2455,-2450,-2445,
|
||||
-2439,-2434,-2429,-2424,-2419,-2414,-2409,-2404,-2399,-2394,-2389,-2384,-2379,-2373,-2368,-2363,
|
||||
-2358,-2353,-2348,-2343,-2337,-2332,-2327,-2322,-2317,-2312,-2306,-2301,-2296,-2291,-2286,-2280,
|
||||
-2275,-2270,-2265,-2259,-2254,-2249,-2244,-2238,-2233,-2228,-2223,-2217,-2212,-2207,-2201,-2196,
|
||||
-2191,-2186,-2180,-2175,-2170,-2164,-2159,-2154,-2148,-2143,-2138,-2132,-2127,-2121,-2116,-2111,
|
||||
-2105,-2100,-2094,-2089,-2084,-2078,-2073,-2067,-2062,-2057,-2051,-2046,-2040,-2035,-2029,-2024,
|
||||
-2018,-2013,-2007,-2002,-1997,-1991,-1986,-1980,-1975,-1969,-1964,-1958,-1952,-1947,-1941,-1936,
|
||||
-1930,-1925,-1919,-1914,-1908,-1903,-1897,-1891,-1886,-1880,-1875,-1869,-1864,-1858,-1852,-1847,
|
||||
-1841,-1835,-1830,-1824,-1819,-1813,-1807,-1802,-1796,-1790,-1785,-1779,-1773,-1768,-1762,-1756,
|
||||
-1751,-1745,-1739,-1734,-1728,-1722,-1717,-1711,-1705,-1699,-1694,-1688,-1682,-1677,-1671,-1665,
|
||||
-1659,-1654,-1648,-1642,-1636,-1631,-1625,-1619,-1613,-1608,-1602,-1596,-1590,-1584,-1579,-1573,
|
||||
-1567,-1561,-1555,-1550,-1544,-1538,-1532,-1526,-1520,-1515,-1509,-1503,-1497,-1491,-1485,-1479,
|
||||
-1474,-1468,-1462,-1456,-1450,-1444,-1438,-1433,-1427,-1421,-1415,-1409,-1403,-1397,-1391,-1385,
|
||||
-1379,-1373,-1368,-1362,-1356,-1350,-1344,-1338,-1332,-1326,-1320,-1314,-1308,-1302,-1296,-1290,
|
||||
-1284,-1278,-1272,-1266,-1260,-1254,-1248,-1243,-1237,-1231,-1225,-1219,-1213,-1207,-1201,-1195,
|
||||
-1189,-1182,-1176,-1170,-1164,-1158,-1152,-1146,-1140,-1134,-1128,-1122,-1116,-1110,-1104,-1098,
|
||||
-1092,-1086,-1080,-1074,-1068,-1062,-1056,-1050,-1043,-1037,-1031,-1025,-1019,-1013,-1007,-1001,
|
||||
-995, -989, -983, -976, -970, -964, -958, -952, -946, -940, -934, -928, -921, -915, -909, -903,
|
||||
-897, -891, -885, -879, -872, -866, -860, -854, -848, -842, -836, -829, -823, -817, -811, -805,
|
||||
-799, -792, -786, -780, -774, -768, -762, -755, -749, -743, -737, -731, -725, -718, -712, -706,
|
||||
-700, -694, -687, -681, -675, -669, -663, -656, -650, -644, -638, -632, -625, -619, -613, -607,
|
||||
-601, -594, -588, -582, -576, -569, -563, -557, -551, -545, -538, -532, -526, -520, -513, -507,
|
||||
-501, -495, -488, -482, -476, -470, -463, -457, -451, -445, -438, -432, -426, -420, -413, -407,
|
||||
-401, -395, -388, -382, -376, -370, -363, -357, -351, -345, -338, -332, -326, -320, -313, -307,
|
||||
-301, -295, -288, -282, -276, -269, -263, -257, -251, -244, -238, -232, -226, -219, -213, -207,
|
||||
-200, -194, -188, -182, -175, -169, -163, -157, -150, -144, -138, -131, -125, -119, -113, -106,
|
||||
-100, -94, -87, -81, -75, -69, -62, -56, -50, -43, -37, -31, -25, -18, -12, -6,
|
||||
|
||||
0, 6, 12, 18, 25, 31, 37, 43, 50, 56, 62, 69, 75, 81, 87, 94,
|
||||
100, 106, 113, 119, 125, 131, 138, 144, 150, 157, 163, 169, 175, 182, 188, 194,
|
||||
200, 207, 213, 219, 226, 232, 238, 244, 251, 257, 263, 269, 276, 282, 288, 295,
|
||||
301, 307, 313, 320, 326, 332, 338, 345, 351, 357, 363, 370, 376, 382, 388, 395,
|
||||
401, 407, 413, 420, 426, 432, 438, 445, 451, 457, 463, 470, 476, 482, 488, 495,
|
||||
501, 507, 513, 520, 526, 532, 538, 545, 551, 557, 563, 569, 576, 582, 588, 594,
|
||||
601, 607, 613, 619, 625, 632, 638, 644, 650, 656, 663, 669, 675, 681, 687, 694,
|
||||
700, 706, 712, 718, 725, 731, 737, 743, 749, 755, 762, 768, 774, 780, 786, 792,
|
||||
799, 805, 811, 817, 823, 829, 836, 842, 848, 854, 860, 866, 872, 879, 885, 891,
|
||||
897, 903, 909, 915, 921, 928, 934, 940, 946, 952, 958, 964, 970, 976, 983, 989,
|
||||
995, 1001, 1007, 1013, 1019, 1025, 1031, 1037, 1043, 1050, 1056, 1062, 1068, 1074, 1080, 1086,
|
||||
1092, 1098, 1104, 1110, 1116, 1122, 1128, 1134, 1140, 1146, 1152, 1158, 1164, 1170, 1176, 1182,
|
||||
1189, 1195, 1201, 1207, 1213, 1219, 1225, 1231, 1237, 1243, 1248, 1254, 1260, 1266, 1272, 1278,
|
||||
1284, 1290, 1296, 1302, 1308, 1314, 1320, 1326, 1332, 1338, 1344, 1350, 1356, 1362, 1368, 1373,
|
||||
1379, 1385, 1391, 1397, 1403, 1409, 1415, 1421, 1427, 1433, 1438, 1444, 1450, 1456, 1462, 1468,
|
||||
1474, 1479, 1485, 1491, 1497, 1503, 1509, 1515, 1520, 1526, 1532, 1538, 1544, 1550, 1555, 1561,
|
||||
1567, 1573, 1579, 1584, 1590, 1596, 1602, 1608, 1613, 1619, 1625, 1631, 1636, 1642, 1648, 1654,
|
||||
1659, 1665, 1671, 1677, 1682, 1688, 1694, 1699, 1705, 1711, 1717, 1722, 1728, 1734, 1739, 1745,
|
||||
1751, 1756, 1762, 1768, 1773, 1779, 1785, 1790, 1796, 1802, 1807, 1813, 1819, 1824, 1830, 1835,
|
||||
1841, 1847, 1852, 1858, 1864, 1869, 1875, 1880, 1886, 1891, 1897, 1903, 1908, 1914, 1919, 1925,
|
||||
1930, 1936, 1941, 1947, 1952, 1958, 1964, 1969, 1975, 1980, 1986, 1991, 1997, 2002, 2007, 2013,
|
||||
2018, 2024, 2029, 2035, 2040, 2046, 2051, 2057, 2062, 2067, 2073, 2078, 2084, 2089, 2094, 2100,
|
||||
2105, 2111, 2116, 2121, 2127, 2132, 2138, 2143, 2148, 2154, 2159, 2164, 2170, 2175, 2180, 2186,
|
||||
2191, 2196, 2201, 2207, 2212, 2217, 2223, 2228, 2233, 2238, 2244, 2249, 2254, 2259, 2265, 2270,
|
||||
2275, 2280, 2286, 2291, 2296, 2301, 2306, 2312, 2317, 2322, 2327, 2332, 2337, 2343, 2348, 2353,
|
||||
2358, 2363, 2368, 2373, 2379, 2384, 2389, 2394, 2399, 2404, 2409, 2414, 2419, 2424, 2429, 2434,
|
||||
2439, 2445, 2450, 2455, 2460, 2465, 2470, 2475, 2480, 2485, 2490, 2495, 2500, 2505, 2510, 2515,
|
||||
2519, 2524, 2529, 2534, 2539, 2544, 2549, 2554, 2559, 2564, 2569, 2574, 2578, 2583, 2588, 2593,
|
||||
2598, 2603, 2608, 2613, 2617, 2622, 2627, 2632, 2637, 2641, 2646, 2651, 2656, 2661, 2665, 2670,
|
||||
2675, 2680, 2684, 2689, 2694, 2699, 2703, 2708, 2713, 2717, 2722, 2727, 2732, 2736, 2741, 2746,
|
||||
2750, 2755, 2760, 2764, 2769, 2773, 2778, 2783, 2787, 2792, 2796, 2801, 2806, 2810, 2815, 2819,
|
||||
2824, 2828, 2833, 2837, 2842, 2847, 2851, 2856, 2860, 2865, 2869, 2874, 2878, 2882, 2887, 2891,
|
||||
2896, 2900, 2905, 2909, 2914, 2918, 2922, 2927, 2931, 2936, 2940, 2944, 2949, 2953, 2957, 2962,
|
||||
2966, 2970, 2975, 2979, 2983, 2988, 2992, 2996, 3000, 3005, 3009, 3013, 3018, 3022, 3026, 3030,
|
||||
3034, 3039, 3043, 3047, 3051, 3055, 3060, 3064, 3068, 3072, 3076, 3080, 3085, 3089, 3093, 3097,
|
||||
3101, 3105, 3109, 3113, 3117, 3121, 3126, 3130, 3134, 3138, 3142, 3146, 3150, 3154, 3158, 3162,
|
||||
3166, 3170, 3174, 3178, 3182, 3186, 3190, 3193, 3197, 3201, 3205, 3209, 3213, 3217, 3221, 3225,
|
||||
3229, 3232, 3236, 3240, 3244, 3248, 3252, 3255, 3259, 3263, 3267, 3271, 3274, 3278, 3282, 3286,
|
||||
3289, 3293, 3297, 3301, 3304, 3308, 3312, 3315, 3319, 3323, 3326, 3330, 3334, 3337, 3341, 3345,
|
||||
3348, 3352, 3356, 3359, 3363, 3366, 3370, 3373, 3377, 3381, 3384, 3388, 3391, 3395, 3398, 3402,
|
||||
3405, 3409, 3412, 3416, 3419, 3423, 3426, 3429, 3433, 3436, 3440, 3443, 3447, 3450, 3453, 3457,
|
||||
3460, 3463, 3467, 3470, 3473, 3477, 3480, 3483, 3487, 3490, 3493, 3497, 3500, 3503, 3506, 3510,
|
||||
3513, 3516, 3519, 3522, 3526, 3529, 3532, 3535, 3538, 3541, 3545, 3548, 3551, 3554, 3557, 3560,
|
||||
3563, 3566, 3570, 3573, 3576, 3579, 3582, 3585, 3588, 3591, 3594, 3597, 3600, 3603, 3606, 3609,
|
||||
3612, 3615, 3618, 3621, 3624, 3627, 3629, 3632, 3635, 3638, 3641, 3644, 3647, 3650, 3652, 3655,
|
||||
3658, 3661, 3664, 3667, 3669, 3672, 3675, 3678, 3680, 3683, 3686, 3689, 3691, 3694, 3697, 3700,
|
||||
3702, 3705, 3708, 3710, 3713, 3716, 3718, 3721, 3723, 3726, 3729, 3731, 3734, 3736, 3739, 3742,
|
||||
3744, 3747, 3749, 3752, 3754, 3757, 3759, 3762, 3764, 3767, 3769, 3772, 3774, 3776, 3779, 3781,
|
||||
3784, 3786, 3789, 3791, 3793, 3796, 3798, 3800, 3803, 3805, 3807, 3810, 3812, 3814, 3816, 3819,
|
||||
3821, 3823, 3826, 3828, 3830, 3832, 3834, 3837, 3839, 3841, 3843, 3845, 3848, 3850, 3852, 3854,
|
||||
3856, 3858, 3860, 3862, 3864, 3867, 3869, 3871, 3873, 3875, 3877, 3879, 3881, 3883, 3885, 3887,
|
||||
3889, 3891, 3893, 3895, 3897, 3899, 3900, 3902, 3904, 3906, 3908, 3910, 3912, 3914, 3915, 3917,
|
||||
3919, 3921, 3923, 3925, 3926, 3928, 3930, 3932, 3933, 3935, 3937, 3939, 3940, 3942, 3944, 3945,
|
||||
3947, 3949, 3950, 3952, 3954, 3955, 3957, 3959, 3960, 3962, 3963, 3965, 3967, 3968, 3970, 3971,
|
||||
3973, 3974, 3976, 3977, 3979, 3980, 3982, 3983, 3985, 3986, 3988, 3989, 3990, 3992, 3993, 3995,
|
||||
3996, 3997, 3999, 4000, 4001, 4003, 4004, 4005, 4007, 4008, 4009, 4011, 4012, 4013, 4014, 4016,
|
||||
4017, 4018, 4019, 4020, 4022, 4023, 4024, 4025, 4026, 4027, 4029, 4030, 4031, 4032, 4033, 4034,
|
||||
4035, 4036, 4037, 4038, 4039, 4040, 4041, 4042, 4043, 4044, 4045, 4046, 4047, 4048, 4049, 4050,
|
||||
4051, 4052, 4053, 4054, 4055, 4056, 4057, 4057, 4058, 4059, 4060, 4061, 4062, 4062, 4063, 4064,
|
||||
4065, 4065, 4066, 4067, 4068, 4068, 4069, 4070, 4071, 4071, 4072, 4073, 4073, 4074, 4075, 4075,
|
||||
4076, 4076, 4077, 4078, 4078, 4079, 4079, 4080, 4080, 4081, 4081, 4082, 4082, 4083, 4083, 4084,
|
||||
4084, 4085, 4085, 4086, 4086, 4087, 4087, 4087, 4088, 4088, 4089, 4089, 4089, 4090, 4090, 4090,
|
||||
4091, 4091, 4091, 4091, 4092, 4092, 4092, 4092, 4093, 4093, 4093, 4093, 4094, 4094, 4094, 4094,
|
||||
4094, 4094, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095,
|
||||
|
||||
4096,
|
||||
};
|
||||
|
||||
/* code can't actually access 2nd index >8 though */
|
||||
static const int scale_table[5][8*2] = {
|
||||
{ 2048, 2048, 4096, 4096, 8192, 8192,16384,16384, 1, 1, 1, 1, 1, 1, 1, 1, },
|
||||
{ 2048, 2048, 2048, 4096, 4096, 4096, 8192,16384, 1, 1, 1, 1, 1, 1, 1, 1, },
|
||||
{ 2048, 2048, 2048, 2048, 2048, 4096, 4096, 8192, 1, 1, 1, 1, 1, 1, 1, 1, },
|
||||
{ 1024, 1024, 1024, 2048, 2048, 2048, 2048, 2048, 1, 1, 1, 1, 1, 1, 1, 1, },
|
||||
{ 1365, 1365, 2048, 2730, 2730, 4096, 4096, 4096, 1, 1, 1, 1, 1, 1, 1, 1, },
|
||||
};
|
||||
|
||||
#endif
|
297
src/coding/circus_decoder_lzxpcm.h
Normal file
297
src/coding/circus_decoder_lzxpcm.h
Normal file
@ -0,0 +1,297 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Decompresses Circus's custom LZ used in XPCM as a machine state for streaming,
|
||||
* that may break during any step. Original code decompress at once the full thing
|
||||
* into memory so it's simpler. */
|
||||
|
||||
#define LZXPCM_OK 0
|
||||
#define LZXPCM_ERROR -1
|
||||
#define LZXPCM_WINDOW_SIZE (1 << 16)
|
||||
|
||||
|
||||
typedef enum {
|
||||
READ_FLAGS,
|
||||
COPY_LITERAL,
|
||||
READ_TOKEN,
|
||||
PARSE_TOKEN,
|
||||
SET_MATCH,
|
||||
COPY_MATCH
|
||||
} lzxpcm_state_t;
|
||||
|
||||
typedef struct {
|
||||
lzxpcm_state_t state;
|
||||
|
||||
uint32_t flags;
|
||||
uint8_t token;
|
||||
int values_pos;
|
||||
int offset_pos;
|
||||
int match_len;
|
||||
int match_pos;
|
||||
|
||||
int window_pos;
|
||||
uint8_t window[LZXPCM_WINDOW_SIZE];
|
||||
} lzxpcm_context_t;
|
||||
|
||||
typedef struct {
|
||||
lzxpcm_context_t ctx;
|
||||
|
||||
uint8_t *next_out; /* next bytes to write (reassign when avail is 0) */
|
||||
int avail_out; /* bytes available at next_out */
|
||||
int total_out; /* written bytes, for reference (set to 0 per call if needed) */
|
||||
|
||||
const uint8_t *next_in; /* next bytes to read (reassign when avail is 0) */
|
||||
int avail_in; /* bytes available at next_in */
|
||||
int total_in; /* read bytes, for reference (set to 0 per call if needed) */
|
||||
} lzxpcm_stream_t;
|
||||
|
||||
|
||||
static void lzxpcm_reset(lzxpcm_stream_t* strm) {
|
||||
memset(strm, 0, sizeof(lzxpcm_stream_t));
|
||||
}
|
||||
|
||||
/* Decompress src into dst, returning a code and number of bytes used. Caller must handle
|
||||
* stop (when no more input data or all data has been decompressed) as LZXPCM has no end marker. */
|
||||
static int lzxpcm_decompress(lzxpcm_stream_t* strm) {
|
||||
lzxpcm_context_t* ctx = &strm->ctx;
|
||||
uint8_t* dst = strm->next_out;
|
||||
const uint8_t* src = strm->next_in;
|
||||
int dst_size = strm->avail_out;
|
||||
int src_size = strm->avail_in;
|
||||
int dst_pos = 0;
|
||||
int src_pos = 0;
|
||||
uint8_t next_val;
|
||||
|
||||
|
||||
while (1) {
|
||||
/* mostly linear state machine, but it may break anytime when reaching dst or src
|
||||
* end, and resume from same state in next call */
|
||||
switch(ctx->state) {
|
||||
|
||||
case READ_FLAGS:
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
|
||||
ctx->flags >>= 1;
|
||||
|
||||
if ((ctx->flags & 0x0100) == 0) {
|
||||
ctx->flags = 0xFF00 | src[src_pos++];
|
||||
}
|
||||
|
||||
if (ctx->flags & 1)
|
||||
ctx->state = COPY_LITERAL;
|
||||
else
|
||||
ctx->state = READ_TOKEN;
|
||||
break;
|
||||
|
||||
case COPY_LITERAL:
|
||||
if (src_pos >= src_size || dst_pos >= dst_size)
|
||||
goto buffer_end;
|
||||
next_val = src[src_pos++];
|
||||
|
||||
dst[dst_pos++] = next_val;
|
||||
|
||||
ctx->window[ctx->window_pos++] = next_val;
|
||||
if (ctx->window_pos == LZXPCM_WINDOW_SIZE)
|
||||
ctx->window_pos = 0;
|
||||
|
||||
ctx->state = READ_FLAGS;
|
||||
break;
|
||||
|
||||
case READ_TOKEN:
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->token = src[src_pos++];
|
||||
|
||||
ctx->values_pos = 0;
|
||||
|
||||
ctx->state = PARSE_TOKEN;
|
||||
break;
|
||||
|
||||
case PARSE_TOKEN:
|
||||
if (ctx->token >= 0xC0) {
|
||||
ctx->match_len = ((ctx->token >> 2) & 0x0F) + 4; /* 6b */
|
||||
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos = src[src_pos++]; /* upper 2b + lower 8b */
|
||||
ctx->offset_pos |= ((ctx->token & 3) << 8);
|
||||
|
||||
}
|
||||
else if (ctx->token >= 0x80) {
|
||||
ctx->match_len = ((ctx->token >> 5) & 3) + 2; /* 2b */
|
||||
|
||||
ctx->offset_pos = ctx->token & 0x1F; /* 5b */
|
||||
if (ctx->offset_pos == 0) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos = src[src_pos++];
|
||||
}
|
||||
}
|
||||
else if (ctx->token == 0x7F) {
|
||||
if (ctx->values_pos == 0) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->match_len = (src[src_pos++] << 0u);
|
||||
ctx->values_pos++;
|
||||
}
|
||||
|
||||
if (ctx->values_pos == 1) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->match_len |= (src[src_pos++] << 8u);
|
||||
ctx->match_len += 2;
|
||||
ctx->values_pos++;
|
||||
}
|
||||
|
||||
if (ctx->values_pos == 2) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos = (src[src_pos++] << 0u);
|
||||
ctx->values_pos++;
|
||||
}
|
||||
|
||||
if (ctx->values_pos == 3) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos |= (src[src_pos++] << 8u);
|
||||
ctx->values_pos++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx->match_len = ctx->token + 4;
|
||||
|
||||
if (ctx->values_pos == 0) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos = (src[src_pos++] << 0u);
|
||||
ctx->values_pos++;
|
||||
}
|
||||
|
||||
if (ctx->values_pos == 1) {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos |= (src[src_pos++] << 8u);
|
||||
ctx->values_pos++;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->state = SET_MATCH;
|
||||
break;
|
||||
|
||||
case SET_MATCH:
|
||||
ctx->match_pos = ctx->window_pos - ctx->offset_pos;
|
||||
if (ctx->match_pos < 0) /* circular buffer so negative is from window end */
|
||||
ctx->match_pos = LZXPCM_WINDOW_SIZE + ctx->match_pos;
|
||||
|
||||
ctx->state = COPY_MATCH;
|
||||
break;
|
||||
|
||||
case COPY_MATCH:
|
||||
while (ctx->match_len > 0) {
|
||||
if (dst_pos >= dst_size)
|
||||
goto buffer_end;
|
||||
|
||||
next_val = ctx->window[ctx->match_pos++];
|
||||
if (ctx->match_pos == LZXPCM_WINDOW_SIZE)
|
||||
ctx->match_pos = 0;
|
||||
|
||||
dst[dst_pos++] = next_val;
|
||||
|
||||
ctx->window[ctx->window_pos++] = next_val;
|
||||
if (ctx->window_pos == LZXPCM_WINDOW_SIZE)
|
||||
ctx->window_pos = 0;
|
||||
|
||||
ctx->match_len--;
|
||||
};
|
||||
|
||||
ctx->state = READ_FLAGS;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
buffer_end:
|
||||
strm->next_out += dst_pos;
|
||||
strm->next_in += src_pos;
|
||||
strm->avail_out -= dst_pos;
|
||||
strm->avail_in -= src_pos;
|
||||
strm->total_out += dst_pos;
|
||||
strm->total_in += src_pos;
|
||||
|
||||
return LZXPCM_OK;
|
||||
fail:
|
||||
return LZXPCM_ERROR;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
/* non-streamed form that XPCM originally uses, assumes buffers are big enough */
|
||||
static int lzxpcm_decompress_full(uint8_t* dst, size_t dst_size, const uint8_t* src, size_t src_size) {
|
||||
int src_pos = 0;
|
||||
int dst_pos = 0;
|
||||
uint32_t flags = 0;
|
||||
|
||||
|
||||
while (src_pos < src_size && dst_pos < dst_size) {
|
||||
flags >>= 1;
|
||||
|
||||
if ((flags & 0x0100) == 0) {
|
||||
flags = 0xFF00 | src[src_pos++];
|
||||
}
|
||||
|
||||
if (flags & 1) {
|
||||
/* uncompressed byte per bit */
|
||||
dst[dst_pos++] = src[src_pos++];
|
||||
}
|
||||
else {
|
||||
/* compressed data */
|
||||
uint32_t length;
|
||||
uint32_t offset;
|
||||
const uint32_t token = src[src_pos++];
|
||||
|
||||
if (token >= 0xC0) {
|
||||
length = ((token >> 2) & 0x0F) + 4; /* 6b */
|
||||
|
||||
offset = ((token & 3) << 8) | src[src_pos++]; /* upper 2b + lower 8b */
|
||||
}
|
||||
else if (token >= 0x80) {
|
||||
length = ((token >> 5) & 3) + 2; /* 2b */
|
||||
|
||||
offset = token & 0x1F; /* 5b */
|
||||
if (offset == 0) {
|
||||
offset = src[src_pos++];
|
||||
}
|
||||
}
|
||||
else if (token == 0x7F) {
|
||||
length = (uint16_t)(src[src_pos] | src[src_pos+1] << 8u) + 2;
|
||||
src_pos += 2;
|
||||
|
||||
offset = (uint16_t)(src[src_pos] | src[src_pos+1] << 8u);
|
||||
src_pos += 2;
|
||||
}
|
||||
else {
|
||||
length = token + 4;
|
||||
|
||||
offset = (uint16_t)(src[src_pos] | src[src_pos+1] << 8u);
|
||||
src_pos += 2;
|
||||
}
|
||||
|
||||
if (dst_pos + length > dst_size) {
|
||||
length = dst_size - dst_pos;
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
dst[dst_pos] = dst[dst_pos - offset];
|
||||
dst_pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
7657
src/coding/circus_decoder_miniz.c
Normal file
7657
src/coding/circus_decoder_miniz.c
Normal file
File diff suppressed because it is too large
Load Diff
1346
src/coding/circus_decoder_miniz.h
Normal file
1346
src/coding/circus_decoder_miniz.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -178,7 +178,13 @@ void decode_xmd(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing,
|
||||
void decode_derf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* circus_decoder */
|
||||
void decode_circus_adpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
typedef struct circus_codec_data circus_codec_data;
|
||||
circus_codec_data* init_circus_vq(STREAMFILE* sf, off_t start, uint8_t codec, uint8_t flags);
|
||||
void decode_circus_vq(circus_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_circus_vq(circus_codec_data* data);
|
||||
void seek_circus_vq(circus_codec_data* data, int32_t num_sample);
|
||||
void free_circus_vq(circus_codec_data* data);
|
||||
void decode_circus_adpcm(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
|
||||
/* oki_decoder */
|
||||
void decode_pcfx(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int mode);
|
||||
|
@ -1,145 +1,152 @@
|
||||
#include "vorbis_custom_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* DEFS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size);
|
||||
static int build_header_comment(uint8_t * buf, size_t bufsize);
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* EXTERNAL API */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/**
|
||||
* VID1 removes the Ogg layer and uses a block layout with custom packet headers.
|
||||
*
|
||||
* Info from hcs's vid1_2ogg: https://github.com/hcs64/vgm_ripping/tree/master/demux/vid1_2ogg
|
||||
*/
|
||||
int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) {
|
||||
off_t offset = start_offset;
|
||||
size_t packet_size = 0;
|
||||
|
||||
/* read header packets (id/setup), each with an VID1 header */
|
||||
|
||||
/* normal identificacion packet */
|
||||
get_packet_header(streamFile, &offset, &packet_size);
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
offset += packet_size;
|
||||
|
||||
/* generate comment packet */
|
||||
data->op.bytes = build_header_comment(data->buffer, data->buffer_size);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */
|
||||
|
||||
/* normal setup packet */
|
||||
get_packet_header(streamFile, &offset, &packet_size);
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
offset += packet_size;
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) {
|
||||
size_t bytes;
|
||||
|
||||
|
||||
/* test block start */
|
||||
if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x4652414D && /* "FRAM" */
|
||||
read_32bitBE(stream->offset + 0x20,stream->streamfile) == 0x41554444) { /* "AUDD" */
|
||||
data->block_offset = stream->offset;
|
||||
data->block_size = read_32bitBE(stream->offset + 0x2c,stream->streamfile);
|
||||
stream->offset += 0x34; /* actual start, rest is chunk sizes and maybe granule info */
|
||||
}
|
||||
|
||||
|
||||
/* get packet info the VID1 header */
|
||||
get_packet_header(stream->streamfile, &stream->offset, (uint32_t*)&data->op.bytes);
|
||||
if (data->op.bytes == 0 || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */
|
||||
|
||||
/* read raw block */
|
||||
bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile);
|
||||
stream->offset += data->op.bytes;
|
||||
if (bytes != data->op.bytes) goto fail; /* wrong packet? */
|
||||
|
||||
//todo: sometimes there are short packets like 01be590000 and Vorbis complains and skips, no idea
|
||||
|
||||
/* test block end (weird size calc but seems ok) */
|
||||
if ((stream->offset - (data->block_offset + 0x34)) >= (data->block_size - 0x06)) {
|
||||
stream->offset = data->block_offset + read_32bitBE(data->block_offset + 0x04,stream->streamfile);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* INTERNAL HELPERS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int build_header_comment(uint8_t * buf, size_t bufsize) {
|
||||
int bytes = 0x19;
|
||||
|
||||
if (bytes > bufsize) return 0;
|
||||
|
||||
put_8bit (buf+0x00, 0x03); /* packet_type (comments) */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
put_32bitLE(buf+0x07, 0x09); /* vendor_length */
|
||||
memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */
|
||||
put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */
|
||||
put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* read header in Vorbis bitpacking format */
|
||||
static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size) {
|
||||
uint8_t ibuf[0x04]; /* header buffer */
|
||||
size_t ibufsize = 0x04; /* header ~max */
|
||||
vgm_bitstream ib = {0};
|
||||
uint32_t size_bits;
|
||||
|
||||
|
||||
if (read_streamfile(ibuf,(*offset),ibufsize, streamFile) != ibufsize)
|
||||
goto fail;
|
||||
ib.buf = ibuf;
|
||||
ib.bufsize = ibufsize;
|
||||
ib.b_off = 0;
|
||||
ib.mode = BITSTREAM_VORBIS;
|
||||
|
||||
/* read using Vorbis weird LSF */
|
||||
r_bits(&ib, 4,&size_bits);
|
||||
r_bits(&ib, (size_bits+1),(uint32_t*)size);
|
||||
|
||||
/* special meaning, seen in silent frames */
|
||||
if (size_bits == 0 && *size == 0 && (uint8_t)read_8bit(*offset, streamFile)==0x80) {
|
||||
*size = 0x01;
|
||||
}
|
||||
|
||||
/* pad and convert to byte offset */
|
||||
if (ib.b_off % 8)
|
||||
ib.b_off += 8 - (ib.b_off % 8);
|
||||
*offset += (ib.b_off/8);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#include "vorbis_custom_decoder.h"
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* DEFS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size);
|
||||
static int build_header_comment(uint8_t * buf, size_t bufsize);
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* EXTERNAL API */
|
||||
/* **************************************************************************** */
|
||||
|
||||
/**
|
||||
* VID1 removes the Ogg layer and uses a block layout with custom packet headers.
|
||||
*
|
||||
* Info from hcs's vid1_2ogg: https://github.com/hcs64/vgm_ripping/tree/master/demux/vid1_2ogg
|
||||
*/
|
||||
int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) {
|
||||
off_t offset = start_offset;
|
||||
size_t packet_size = 0;
|
||||
|
||||
/* read header packets (id/setup), each with an VID1 header */
|
||||
|
||||
/* normal identificacion packet */
|
||||
get_packet_header(streamFile, &offset, &packet_size);
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
||||
offset += packet_size;
|
||||
|
||||
/* generate comment packet */
|
||||
data->op.bytes = build_header_comment(data->buffer, data->buffer_size);
|
||||
if (!data->op.bytes) goto fail;
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */
|
||||
|
||||
/* normal setup packet */
|
||||
get_packet_header(streamFile, &offset, &packet_size);
|
||||
if (packet_size > data->buffer_size) goto fail;
|
||||
data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile);
|
||||
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
||||
offset += packet_size;
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) {
|
||||
size_t bytes;
|
||||
|
||||
|
||||
/* test block start */
|
||||
if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x4652414D) { /* "FRAM" */
|
||||
stream->offset += 0x20;
|
||||
|
||||
if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x56494444) { /* "VIDD"*/
|
||||
stream->offset += read_32bitBE(stream->offset + 0x04, stream->streamfile);
|
||||
}
|
||||
|
||||
if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x41554444) { /* "AUDD" */
|
||||
data->block_offset = stream->offset;
|
||||
data->block_size = read_32bitBE(stream->offset + 0x0c,stream->streamfile);
|
||||
stream->offset += 0x14; /* actual start, rest is chunk sizes and maybe granule info */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* get packet info the VID1 header */
|
||||
get_packet_header(stream->streamfile, &stream->offset, (uint32_t*)&data->op.bytes);
|
||||
if (data->op.bytes == 0 || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */
|
||||
|
||||
/* read raw block */
|
||||
bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile);
|
||||
stream->offset += data->op.bytes;
|
||||
if (bytes != data->op.bytes) goto fail; /* wrong packet? */
|
||||
|
||||
//todo: sometimes there are short packets like 01be590000 and Vorbis complains and skips, no idea
|
||||
|
||||
/* test block end (weird size calc but seems ok) */
|
||||
if ((stream->offset - (data->block_offset + 0x14)) >= (data->block_size - 0x06)) {
|
||||
stream->offset = data->block_offset + read_32bitBE(data->block_offset + 0x04,stream->streamfile);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* **************************************************************************** */
|
||||
/* INTERNAL HELPERS */
|
||||
/* **************************************************************************** */
|
||||
|
||||
static int build_header_comment(uint8_t * buf, size_t bufsize) {
|
||||
int bytes = 0x19;
|
||||
|
||||
if (bytes > bufsize) return 0;
|
||||
|
||||
put_8bit (buf+0x00, 0x03); /* packet_type (comments) */
|
||||
memcpy (buf+0x01, "vorbis", 6); /* id */
|
||||
put_32bitLE(buf+0x07, 0x09); /* vendor_length */
|
||||
memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */
|
||||
put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */
|
||||
put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* read header in Vorbis bitpacking format */
|
||||
static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size) {
|
||||
uint8_t ibuf[0x04]; /* header buffer */
|
||||
size_t ibufsize = 0x04; /* header ~max */
|
||||
vgm_bitstream ib = {0};
|
||||
uint32_t size_bits;
|
||||
|
||||
|
||||
if (read_streamfile(ibuf,(*offset),ibufsize, streamFile) != ibufsize)
|
||||
goto fail;
|
||||
ib.buf = ibuf;
|
||||
ib.bufsize = ibufsize;
|
||||
ib.b_off = 0;
|
||||
ib.mode = BITSTREAM_VORBIS;
|
||||
|
||||
/* read using Vorbis weird LSF */
|
||||
r_bits(&ib, 4,&size_bits);
|
||||
r_bits(&ib, (size_bits+1),(uint32_t*)size);
|
||||
|
||||
/* special meaning, seen in silent frames */
|
||||
if (size_bits == 0 && *size == 0 && (uint8_t)read_8bit(*offset, streamFile)==0x80) {
|
||||
*size = 0x01;
|
||||
}
|
||||
|
||||
/* pad and convert to byte offset */
|
||||
if (ib.b_off % 8)
|
||||
ib.b_off += 8 - (ib.b_off % 8);
|
||||
*offset += (ib.b_off/8);
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -297,6 +297,8 @@ static const char* extension_list[] = {
|
||||
"mihb",
|
||||
"mnstr",
|
||||
"mogg",
|
||||
//"m4a", //common
|
||||
//"m4v", //common
|
||||
//"mp+", //common [Moonshine Runners (PC)]
|
||||
//"mp2", //common
|
||||
//"mp3", //common
|
||||
@ -514,6 +516,7 @@ static const char* extension_list[] = {
|
||||
"vgm", //txth/reserved [Maximo (PS2)]
|
||||
"vgs",
|
||||
"vgv",
|
||||
"vid",
|
||||
"vig",
|
||||
"vis",
|
||||
"vms",
|
||||
@ -608,6 +611,8 @@ static const char* common_extension_list[] = {
|
||||
"aiff", //common
|
||||
"bin", //common
|
||||
"flac", //common
|
||||
"m4a", //common
|
||||
"m4v", //common
|
||||
"mp+", //common [Moonshine Runners (PC)]
|
||||
"mp2", //common
|
||||
"mp3", //common
|
||||
@ -758,7 +763,7 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_UBI_ADPCM, "Ubisoft 4/6-bit ADPCM"},
|
||||
|
||||
{coding_EA_MT, "Electronic Arts MicroTalk"},
|
||||
|
||||
{coding_CIRCUS_VQ, "Circus VQ"},
|
||||
{coding_RELIC, "Relic Codec"},
|
||||
{coding_CRI_HCA, "CRI HCA"},
|
||||
|
||||
@ -841,6 +846,7 @@ static const layout_info layout_info_list[] = {
|
||||
{layout_blocked_h4m, "blocked (H4M)"},
|
||||
{layout_blocked_xa_aiff, "blocked (XA AIFF)"},
|
||||
{layout_blocked_vs_square, "blocked (Square VS)"},
|
||||
{layout_blocked_vid1, "blocked (VID1)"},
|
||||
};
|
||||
|
||||
static const meta_info meta_info_list[] = {
|
||||
@ -1171,7 +1177,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_VXN, "Gameloft VXN header"},
|
||||
{meta_EA_SNR_SNS, "Electronic Arts SNR+SNS header"},
|
||||
{meta_EA_SPS, "Electronic Arts SPS header"},
|
||||
{meta_NGC_VID1, "Neversoft VID1 header"},
|
||||
{meta_VID1, "Factor 5 VID1 header"},
|
||||
{meta_PC_FLX, "Ultima IX .FLX header"},
|
||||
{meta_MOGG, "Harmonix Music Systems MOGG Vorbis"},
|
||||
{meta_OGG_VORBIS, "Ogg Vorbis header"},
|
||||
|
@ -205,6 +205,9 @@ void block_update(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
case layout_blocked_vs_square:
|
||||
block_update_vs_square(block_offset,vgmstream);
|
||||
break;
|
||||
case layout_blocked_vid1:
|
||||
block_update_vid1(block_offset,vgmstream);
|
||||
break;
|
||||
default: /* not a blocked layout */
|
||||
break;
|
||||
}
|
||||
|
61
src/layout/blocked_vid1.c
Normal file
61
src/layout/blocked_vid1.c
Normal file
@ -0,0 +1,61 @@
|
||||
#include "layout.h"
|
||||
#include "../vgmstream.h"
|
||||
|
||||
/* blocks with video and audio */
|
||||
void block_update_vid1(off_t block_offset, VGMSTREAM* vgmstream) {
|
||||
STREAMFILE* sf = vgmstream->ch[0].streamfile;
|
||||
int ch;
|
||||
int channels = vgmstream->channels;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_u32be : read_u32le;
|
||||
size_t audd_size = 0, data_size = 0;
|
||||
|
||||
|
||||
if (read_u32(block_offset + 0x00, sf) != 0x4652414D) { /* "FRAM" */
|
||||
/* signal EOF, as files ends with padding */
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
vgmstream->next_block_offset = block_offset;
|
||||
vgmstream->current_block_size = 0;
|
||||
vgmstream->current_block_samples = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
block_offset += 0x20;
|
||||
if (read_u32(block_offset + 0x00, sf) == 0x56494444) { /* "VIDD"*/
|
||||
block_offset += read_u32(block_offset + 0x04, sf);
|
||||
}
|
||||
|
||||
if (read_u32(block_offset + 0x00, sf) == 0x41554444) { /* "AUDD" */
|
||||
audd_size = read_u32(block_offset + 0x04, sf);
|
||||
data_size = read_u32(block_offset + 0x0c, sf);
|
||||
}
|
||||
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
vgmstream->next_block_offset = block_offset + audd_size;
|
||||
vgmstream->current_block_size = (data_size / channels);
|
||||
vgmstream->current_block_samples = 0;
|
||||
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
off_t interleave, head_size;
|
||||
|
||||
switch(vgmstream->coding_type) {
|
||||
case coding_PCM16_int:
|
||||
interleave = 0x02 * ch;
|
||||
head_size = 0x10;
|
||||
break;
|
||||
case coding_XBOX_IMA:
|
||||
interleave = 0x00;
|
||||
head_size = 0x10;
|
||||
break;
|
||||
case coding_NGC_DSP:
|
||||
interleave = (data_size / channels) * ch;
|
||||
head_size = 0x20;
|
||||
break;
|
||||
default:
|
||||
interleave = 0;
|
||||
head_size = 0x10;
|
||||
break;
|
||||
}
|
||||
|
||||
vgmstream->ch[ch].offset = block_offset + head_size + interleave;
|
||||
}
|
||||
}
|
@ -46,6 +46,7 @@ void block_update_sthd(off_t block_offset, VGMSTREAM * vgmstream);
|
||||
void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream);
|
||||
void block_update_xa_aiff(off_t block_offset, VGMSTREAM * vgmstream);
|
||||
void block_update_vs_square(off_t block_offset, VGMSTREAM * vgmstream);
|
||||
void block_update_vid1(off_t block_offset, VGMSTREAM* vgmstream);
|
||||
|
||||
/* other layouts */
|
||||
void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream);
|
||||
|
@ -281,7 +281,7 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\fsb5_interleave_streamfile.h"
|
||||
RelativePath=".\meta\fsb5_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
@ -328,6 +328,14 @@
|
||||
RelativePath=".\meta\xavs_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\xnb_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\xnb_lz4mg.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\riff_ogg_streamfile.h"
|
||||
>
|
||||
@ -357,7 +365,7 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ubi_lyn_ogg_streamfile.h"
|
||||
RelativePath=".\meta\ubi_lyn_streamfile.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
@ -536,6 +544,10 @@
|
||||
RelativePath=".\meta\bik.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\bkhd.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\bmp_konami.c"
|
||||
>
|
||||
@ -995,7 +1007,7 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ngc_vid1.c"
|
||||
RelativePath=".\meta\vid1.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
@ -1890,6 +1902,22 @@
|
||||
RelativePath=".\coding\acm_decoder_libacm.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\circus_decoder_lib.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\circus_decoder_lib_data.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\circus_decoder_lzxpcm.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\circus_decoder_miniz.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\coding.h"
|
||||
>
|
||||
@ -1966,6 +1994,14 @@
|
||||
RelativePath=".\coding\circus_decoder.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\circus_decoder_lib.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\circus_decoder_miniz.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\coding\coding_utils.c"
|
||||
>
|
||||
@ -2242,6 +2278,10 @@
|
||||
RelativePath=".\layout\blocked_vgs.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\layout\blocked_vid1.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\layout\blocked_vs_square.c"
|
||||
>
|
||||
|
@ -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_VORBIS;VGM_USE_MPEG;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>
|
||||
<PreprocessorDefinitions>WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_G719;VGM_USE_ATRAC9;VGM_USE_CELT;USE_ALLOCA;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
@ -72,7 +72,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_VORBIS;VGM_USE_MPEG;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>
|
||||
<PreprocessorDefinitions>_WIN32_WINNT=0x501;WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_G719;VGM_USE_ATRAC9;VGM_USE_CELT;USE_ALLOCA;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
@ -112,13 +112,15 @@
|
||||
<ClInclude Include="meta\ea_eaac_opus_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\fsb5_streamfile.h" />
|
||||
<ClInclude Include="meta\jstm_streamfile.h" />
|
||||
<ClInclude Include="meta\kma9_streamfile.h" />
|
||||
<ClInclude Include="meta\lrmd_streamfile.h" />
|
||||
<ClInclude Include="meta\ppst_streamfile.h" />
|
||||
<ClInclude Include="meta\vsv_streamfile.h" />
|
||||
<ClInclude Include="meta\xavs_streamfile.h" />
|
||||
<ClInclude Include="meta\xnb_streamfile.h" />
|
||||
<ClInclude Include="meta\xnb_lz4mg.h" />
|
||||
<ClInclude Include="meta\mta2_streamfile.h" />
|
||||
<ClInclude Include="meta\mzrt_streamfile.h" />
|
||||
<ClInclude Include="meta\nus3bank_streamfile.h" />
|
||||
@ -131,12 +133,16 @@
|
||||
<ClInclude Include="meta\txth_streamfile.h" />
|
||||
<ClInclude Include="meta\ubi_bao_streamfile.h" />
|
||||
<ClInclude Include="meta\ubi_sb_streamfile.h" />
|
||||
<ClInclude Include="meta\ubi_lyn_ogg_streamfile.h" />
|
||||
<ClInclude Include="meta\ubi_lyn_streamfile.h" />
|
||||
<ClInclude Include="meta\meta.h" />
|
||||
<ClInclude Include="meta\hca_keys.h" />
|
||||
<ClInclude Include="meta\hca_keys_awb.h" />
|
||||
<ClInclude Include="meta\fsb_keys.h" />
|
||||
<ClInclude Include="coding\acm_decoder_libacm.h" />
|
||||
<ClInclude Include="coding\circus_decoder_lib.h" />
|
||||
<ClInclude Include="coding\circus_decoder_lib_data.h" />
|
||||
<ClInclude Include="coding\circus_decoder_lzxpcm.h" />
|
||||
<ClInclude Include="coding\circus_decoder_miniz.h" />
|
||||
<ClInclude Include="coding\coding.h" />
|
||||
<ClInclude Include="coding\ea_mt_decoder_utk.h" />
|
||||
<ClInclude Include="coding\g7221_decoder_aes.h" />
|
||||
@ -152,6 +158,8 @@
|
||||
<ClCompile Include="coding\atrac9_decoder.c" />
|
||||
<ClCompile Include="coding\celt_fsb_decoder.c" />
|
||||
<ClCompile Include="coding\circus_decoder.c" />
|
||||
<ClCompile Include="coding\circus_decoder_lib.c" />
|
||||
<ClCompile Include="coding\circus_decoder_miniz.c" />
|
||||
<ClCompile Include="coding\coding_utils.c" />
|
||||
<ClCompile Include="coding\ffmpeg_decoder.c" />
|
||||
<ClCompile Include="coding\ffmpeg_decoder_utils.c" />
|
||||
@ -271,6 +279,7 @@
|
||||
<ClCompile Include="meta\baf.c" />
|
||||
<ClCompile Include="meta\bgw.c" />
|
||||
<ClCompile Include="meta\bik.c" />
|
||||
<ClCompile Include="meta\bkhd.c" />
|
||||
<ClCompile Include="meta\bmp_konami.c" />
|
||||
<ClCompile Include="meta\bnk_sony.c" />
|
||||
<ClCompile Include="meta\bnsf.c" />
|
||||
@ -365,7 +374,7 @@
|
||||
<ClCompile Include="meta\ngc_tydsp.c" />
|
||||
<ClCompile Include="meta\ngc_ymf.c" />
|
||||
<ClCompile Include="meta\ngc_ulw.c" />
|
||||
<ClCompile Include="meta\ngc_vid1.c" />
|
||||
<ClCompile Include="meta\vid1.c" />
|
||||
<ClCompile Include="meta\nus3audio.c" />
|
||||
<ClCompile Include="meta\nus3bank.c" />
|
||||
<ClCompile Include="meta\nwa.c" />
|
||||
@ -606,6 +615,7 @@
|
||||
<ClCompile Include="layout\blocked_awc.c" />
|
||||
<ClCompile Include="layout\blocked_ea_1snh.c" />
|
||||
<ClCompile Include="layout\blocked_vgs.c" />
|
||||
<ClCompile Include="layout\blocked_vid1.c" />
|
||||
<ClCompile Include="layout\blocked_vs_square.c" />
|
||||
<ClCompile Include="layout\blocked_vawx.c" />
|
||||
<ClCompile Include="layout\blocked_xvag.c" />
|
||||
|
@ -107,7 +107,7 @@
|
||||
<ClInclude Include="meta\fsb_interleave_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\fsb5_interleave_streamfile.h">
|
||||
<ClInclude Include="meta\fsb5_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\jstm_streamfile.h">
|
||||
@ -143,6 +143,12 @@
|
||||
<ClInclude Include="meta\xavs_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\xnb_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\xnb_lz4mg.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\riff_ogg_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -164,7 +170,7 @@
|
||||
<ClInclude Include="meta\ubi_sb_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\ubi_lyn_ogg_streamfile.h">
|
||||
<ClInclude Include="meta\ubi_lyn_streamfile.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\meta.h">
|
||||
@ -182,6 +188,18 @@
|
||||
<ClInclude Include="coding\acm_decoder_libacm.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\circus_decoder_lib.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\circus_decoder_lib_data.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\circus_decoder_lzxpcm.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\circus_decoder_miniz.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="coding\coding.h">
|
||||
<Filter>coding\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -628,7 +646,7 @@
|
||||
<ClCompile Include="meta\ngc_ulw.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ngc_vid1.c">
|
||||
<ClCompile Include="meta\vid1.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\nus3audio.c">
|
||||
@ -1336,6 +1354,9 @@
|
||||
<ClCompile Include="layout\blocked_vgs.c">
|
||||
<Filter>layout\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="layout\blocked_vid1.c">
|
||||
<Filter>layout\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="layout\blocked_vs_square.c">
|
||||
<Filter>layout\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1594,6 +1615,12 @@
|
||||
<ClCompile Include="coding\circus_decoder.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\circus_decoder_lib.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="coding\circus_decoder_miniz.c">
|
||||
<Filter>coding\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\bcstm.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -1654,6 +1681,9 @@
|
||||
<ClCompile Include="meta\bik.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\bkhd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\bmp_konami.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -25,16 +25,15 @@ typedef struct {
|
||||
static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc);
|
||||
|
||||
|
||||
/* AWC - from RAGE (Rockstar Advanced Game Engine) audio (Red Dead Redemption, Max Payne 3, GTA5) */
|
||||
/* AWC - from RAGE (Rockstar Advanced Game Engine) audio [Red Dead Redemption, Max Payne 3, GTA5 (multi)] */
|
||||
VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
awc_header awc = {0};
|
||||
|
||||
/* check extension */
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"awc"))
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
if (!parse_awc_header(streamFile, &awc))
|
||||
goto fail;
|
||||
|
||||
@ -51,7 +50,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
|
||||
|
||||
|
||||
switch(awc.codec) {
|
||||
case 0x01: /* PCM (PC/PS3) [sfx, rarely] */
|
||||
case 0x00: /* PCM (PC) sfx, very rare, lower sample rates? [Max Payne 3 (PC)] */
|
||||
case 0x01: /* PCM (PC/PS3) sfx, rarely */
|
||||
if (awc.is_music) goto fail; /* blocked_awc needs to be prepared */
|
||||
vgmstream->coding_type = awc.big_endian ? coding_PCM16BE : coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
@ -294,17 +294,22 @@ static int parse_awc_header(STREAMFILE* streamFile, awc_header* awc) {
|
||||
sample_rate = (uint16_t)read_16bit(offset + 0x0c + 0x10*ch + 0x0a,streamFile);
|
||||
codec = read_8bit(offset + 0x0c + 0x10*ch + 0x0c, streamFile);
|
||||
|
||||
/* validate as all channels should repeat this (when channels is even and > 2 seems
|
||||
* it's stereo pairs, and num_samples can vary slightly but no matter) */
|
||||
/* validate channels differences */
|
||||
if ((awc->num_samples && !(awc->num_samples >= num_samples - 10 && awc->num_samples <= num_samples + 10)) ||
|
||||
(awc->sample_rate && awc->sample_rate != sample_rate) ||
|
||||
(awc->codec && awc->codec != codec)) {
|
||||
VGM_LOG("AWC: found header diffs in channel %i, ns=%i vs %i, sr=%i vs %i, c=%i vs %i\n",
|
||||
ch, awc->num_samples, num_samples, awc->sample_rate, sample_rate, awc->codec, codec);
|
||||
//goto fail; //todo some Max Payne 3 cutscene channels have huge sample diffs
|
||||
(awc->sample_rate && awc->sample_rate != sample_rate)) {
|
||||
VGM_LOG("AWC: found header diffs in channel %i, ns=%i vs %i, sr=%i vs %i\n",
|
||||
ch, awc->num_samples, num_samples, awc->sample_rate, sample_rate);
|
||||
/* sometimes (often cutscenes in Max Payne 3 and RDR DLC) channels have bif sample diffs,
|
||||
* probably one stream is simply silent after its samples end */
|
||||
}
|
||||
|
||||
awc->num_samples = num_samples;
|
||||
if ((awc->codec && awc->codec != codec)) {
|
||||
VGM_LOG("AWC: found header diffs in channel %i, c=%i vs %i\n", ch, awc->codec, codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (awc->num_samples < num_samples) /* use biggest channel */
|
||||
awc->num_samples = num_samples;
|
||||
awc->sample_rate = sample_rate;
|
||||
awc->codec = codec;
|
||||
}
|
||||
|
@ -1,245 +1,114 @@
|
||||
#ifndef _AWC_XMA_STREAMFILE_H_
|
||||
#define _AWC_XMA_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
int channel;
|
||||
int channel_count;
|
||||
size_t block_size;
|
||||
off_t stream_offset;
|
||||
size_t stream_size;
|
||||
|
||||
/* state */
|
||||
off_t logical_offset; /* offset that corresponds to physical_offset */
|
||||
off_t physical_offset; /* actual file offset */
|
||||
|
||||
size_t skip_size; /* size to skip from a block start to reach data start */
|
||||
size_t data_size; /* logical size of the block */
|
||||
|
||||
size_t logical_size;
|
||||
} awc_xma_io_data;
|
||||
|
||||
|
||||
static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, awc_xma_io_data *data);
|
||||
static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t next_offset, size_t repeat_samples);
|
||||
static size_t get_block_skip_count(STREAMFILE *streamFile, off_t offset, int channel);
|
||||
|
||||
/* Reads plain XMA data of a single stream. Each block has a header and channels have different num_samples/frames.
|
||||
* Channel data is separate within the block (first all frames of ch0, then ch1, etc), padded, and sometimes
|
||||
* the last few frames of a channel are repeated in the new block (marked with the "discard samples" field). */
|
||||
static size_t awc_xma_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, awc_xma_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
size_t frame_size = 0x800;
|
||||
|
||||
/* ignore bad reads */
|
||||
if (offset < 0 || offset > data->logical_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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->logical_offset = 0x00;
|
||||
data->physical_offset = data->stream_offset;
|
||||
data->data_size = 0;
|
||||
}
|
||||
|
||||
/* read blocks, one at a time */
|
||||
while (length > 0) {
|
||||
|
||||
/* ignore EOF */
|
||||
if (data->logical_offset >= data->logical_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* process new block */
|
||||
if (data->data_size == 0) {
|
||||
size_t header_size = get_block_header_size(streamfile, data->physical_offset, data);
|
||||
/* header table entries = frames... I hope */
|
||||
size_t others_size = get_block_skip_count(streamfile, data->physical_offset, data->channel) * frame_size;
|
||||
//size_t skip_size = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x00, streamfile) * frame_size;
|
||||
size_t data_size = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x04, streamfile) * frame_size;
|
||||
size_t repeat_samples = read_32bitBE(data->physical_offset + 0x10*data->channel + 0x08, streamfile);
|
||||
size_t repeat_size = 0;
|
||||
|
||||
|
||||
/* if there are repeat samples current block repeats some frames from last block, find out size */
|
||||
if (repeat_samples) {
|
||||
off_t data_offset = data->physical_offset + header_size + others_size;
|
||||
repeat_size = get_repeated_data_size(streamfile, data_offset, repeat_samples);
|
||||
}
|
||||
|
||||
data->skip_size = header_size + others_size + repeat_size;
|
||||
data->data_size = data_size - repeat_size;
|
||||
}
|
||||
|
||||
/* move to next block */
|
||||
if (offset >= data->logical_offset + data->data_size) {
|
||||
data->physical_offset += data->block_size;
|
||||
data->logical_offset += data->data_size;
|
||||
data->data_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
{
|
||||
size_t bytes_consumed, bytes_done, to_read;
|
||||
|
||||
bytes_consumed = offset - data->logical_offset;
|
||||
|
||||
to_read = data->data_size - bytes_consumed;
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
|
||||
|
||||
offset += bytes_done;
|
||||
total_read += bytes_done;
|
||||
length -= bytes_done;
|
||||
dest += bytes_done;
|
||||
|
||||
if (bytes_done != to_read || bytes_done == 0) {
|
||||
break; /* error/EOF */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t awc_xma_io_size(STREAMFILE *streamfile, awc_xma_io_data* data) {
|
||||
off_t physical_offset, max_physical_offset;
|
||||
size_t frame_size = 0x800;
|
||||
size_t logical_size = 0;
|
||||
|
||||
if (data->logical_size)
|
||||
return data->logical_size;
|
||||
|
||||
physical_offset = data->stream_offset;
|
||||
max_physical_offset = data->stream_offset + data->stream_size;
|
||||
|
||||
/* get size of the logical stream */
|
||||
while (physical_offset < max_physical_offset) {
|
||||
size_t header_size = get_block_header_size(streamfile, physical_offset, data);
|
||||
/* header table entries = frames... I hope */
|
||||
size_t skip_size = get_block_skip_count(streamfile, physical_offset, data->channel) * frame_size;
|
||||
//size_t skip_size = read_32bitBE(physical_offset + 0x10*data->channel + 0x00, streamfile) * frame_size;
|
||||
size_t data_size = read_32bitBE(physical_offset + 0x10*data->channel + 0x04, streamfile) * frame_size;
|
||||
size_t repeat_samples = read_32bitBE(physical_offset + 0x10*data->channel + 0x08, streamfile);
|
||||
size_t repeat_size = 0;
|
||||
|
||||
/* if there are repeat samples current block repeats some frames from last block, find out size */
|
||||
if (repeat_samples) {
|
||||
off_t data_offset = physical_offset + header_size + skip_size;
|
||||
repeat_size = get_repeated_data_size(streamfile, data_offset, repeat_samples);
|
||||
}
|
||||
|
||||
logical_size += data_size - repeat_size;
|
||||
physical_offset += data->block_size;
|
||||
}
|
||||
|
||||
data->logical_size = logical_size;
|
||||
return data->logical_size;
|
||||
}
|
||||
|
||||
|
||||
/* Prepares custom IO for AWC XMA, which is interleaved XMA in AWC blocks */
|
||||
static STREAMFILE* setup_awc_xma_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, size_t block_size, int channel_count, int channel) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
awc_xma_io_data io_data = {0};
|
||||
size_t io_data_size = sizeof(awc_xma_io_data);
|
||||
|
||||
io_data.channel = channel;
|
||||
io_data.channel_count = channel_count;
|
||||
io_data.stream_offset = stream_offset;
|
||||
io_data.stream_size = stream_size;
|
||||
io_data.block_size = block_size;
|
||||
io_data.physical_offset = stream_offset;
|
||||
io_data.logical_size = awc_xma_io_size(streamFile, &io_data); /* force init */
|
||||
|
||||
if (io_data.logical_size > io_data.stream_size) {
|
||||
VGM_LOG("AWC XMA: wrong logical size\n");
|
||||
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, awc_xma_io_read,awc_xma_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;
|
||||
}
|
||||
|
||||
/* block header size, aligned/padded to 0x800 */
|
||||
static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, awc_xma_io_data *data) {
|
||||
size_t header_size = 0;
|
||||
int i;
|
||||
int entries = data->channel_count;
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
header_size += 0x10;
|
||||
header_size += read_32bitBE(offset + 0x10*i + 0x04, streamFile) * 0x04; /* entries in the table */
|
||||
}
|
||||
|
||||
if (header_size % 0x800) /* padded */
|
||||
header_size += 0x800 - (header_size % 0x800);
|
||||
|
||||
return header_size;
|
||||
}
|
||||
|
||||
|
||||
/* find data that repeats in the beginning of a new block at the end of last block */
|
||||
static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t next_offset, size_t repeat_samples) {
|
||||
const size_t frame_size = 0x800;
|
||||
const size_t samples_per_subframe = 512;
|
||||
size_t samples_this_frame;
|
||||
uint8_t subframes;
|
||||
|
||||
//todo: fix this
|
||||
/* Repeat samples are the number of decoded samples to discard, but in this streamfile we can't do that.
|
||||
* Also XMA is VBR, and may encode silent frames with up to 63 subframes yet we may have few repeat samples.
|
||||
* We could find out how many subframes of 512 samples to skip, then adjust the XMA frame header, though
|
||||
* output will be slightly off since subframes are related.
|
||||
*
|
||||
* For now just skip a full frame depending on the number of subframes vs repeat samples.
|
||||
* Most files work ok-ish but channels may desync slightly. */
|
||||
|
||||
subframes = ((uint8_t)read_8bit(next_offset,streamFile) >> 2) & 0x3F; /* peek into frame header */
|
||||
samples_this_frame = subframes*samples_per_subframe;
|
||||
if (repeat_samples >= (int)(samples_this_frame*0.13)) { /* skip mosts */
|
||||
return frame_size;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* header has a skip value, but somehow it's sometimes bigger than expected (WHY!?!?) so just sum all */
|
||||
static size_t get_block_skip_count(STREAMFILE *streamFile, off_t offset, int channel) {
|
||||
size_t skip_count = 0;
|
||||
int i;
|
||||
|
||||
//skip_size = read_32bitBE(offset + 0x10*channel + 0x00, streamFile); /* wrong! */
|
||||
for (i = 0; i < channel; i++) {
|
||||
skip_count += read_32bitBE(offset + 0x10*i + 0x04, streamFile); /* number of frames of this channel */
|
||||
}
|
||||
|
||||
return skip_count;
|
||||
}
|
||||
|
||||
|
||||
#endif /* _AWC_XMA_STREAMFILE_H_ */
|
||||
#ifndef _AWC_XMA_STREAMFILE_H_
|
||||
#define _AWC_XMA_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
|
||||
static size_t get_block_header_size(STREAMFILE* sf, off_t offset, int channels);
|
||||
static size_t get_repeated_data_size(STREAMFILE* sf, off_t next_offset, size_t repeat_samples);
|
||||
static size_t get_block_skip_count(STREAMFILE* sf, off_t offset, int channel);
|
||||
|
||||
static void block_callback(STREAMFILE *sf, deblock_io_data* data) {
|
||||
const size_t frame_size = 0x800;
|
||||
int channel = data->cfg.track_number;
|
||||
int channels = data->cfg.track_count;
|
||||
|
||||
/* Blocks have a header then data per channel, each with a different num_samples/frames,
|
||||
* separate (first all frames of ch0, then ch1, etc), padded, and sometimes the last few
|
||||
* frames of a channel are repeated in the new block (marked with "repeat samples"). */
|
||||
size_t header_size = get_block_header_size(sf, data->physical_offset, channels);
|
||||
/* header table entries = frames... I hope */
|
||||
size_t others_size = get_block_skip_count(sf, data->physical_offset, channel) * frame_size;
|
||||
//size_t skip_size = read_u32be(data->physical_offset + 0x10*channel + 0x00, sf) * frame_size;
|
||||
size_t data_size = read_u32be(data->physical_offset + 0x10*channel + 0x04, sf) * frame_size;
|
||||
size_t repeat_samples = read_u32be(data->physical_offset + 0x10*channel + 0x08, sf);
|
||||
size_t repeat_size = 0;
|
||||
|
||||
data->block_size = data->cfg.chunk_size;
|
||||
|
||||
/* if there are repeat samples current block repeats some frames from last block, find out size */
|
||||
if (repeat_samples) {
|
||||
off_t data_offset = data->physical_offset + header_size + others_size;
|
||||
repeat_size = get_repeated_data_size(sf, data_offset, repeat_samples);
|
||||
}
|
||||
|
||||
data->skip_size = header_size + others_size + repeat_size;
|
||||
data->data_size = data_size - repeat_size;
|
||||
}
|
||||
|
||||
/* block header size, aligned/padded to 0x800 */
|
||||
static size_t get_block_header_size(STREAMFILE* sf, off_t offset, int channels) {
|
||||
size_t header_size = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
header_size += 0x10;
|
||||
header_size += read_u32be(offset + 0x10*i + 0x04, sf) * 0x04; /* entries in the table */
|
||||
}
|
||||
|
||||
if (header_size % 0x800) /* padded */
|
||||
header_size += 0x800 - (header_size % 0x800);
|
||||
|
||||
return header_size;
|
||||
}
|
||||
|
||||
/* find data that repeats in the beginning of a new block at the end of last block */
|
||||
static size_t get_repeated_data_size(STREAMFILE* sf, off_t next_offset, size_t repeat_samples) {
|
||||
const size_t frame_size = 0x800;
|
||||
const size_t samples_per_subframe = 512;
|
||||
size_t samples_this_frame;
|
||||
uint8_t subframes;
|
||||
|
||||
//todo: fix this
|
||||
/* Repeat samples are the number of decoded samples to discard, but in this streamfile we can't do that.
|
||||
* Also XMA is VBR, and may encode silent frames with up to 63 subframes yet we may have few repeat samples.
|
||||
* We could find out how many subframes of 512 samples to skip, then adjust the XMA frame header, though
|
||||
* output will be slightly off since subframes are related.
|
||||
*
|
||||
* For now just skip a full frame depending on the number of subframes vs repeat samples.
|
||||
* Most files work ok-ish but channels may desync slightly. */
|
||||
|
||||
subframes = ((uint8_t)read_8bit(next_offset,sf) >> 2) & 0x3F; /* peek into frame header */
|
||||
samples_this_frame = subframes*samples_per_subframe;
|
||||
if (repeat_samples >= (int)(samples_this_frame*0.13)) { /* skip mosts */
|
||||
return frame_size;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* header has a skip value, but somehow it's sometimes bigger than expected (WHY!?!?) so just sum all */
|
||||
static size_t get_block_skip_count(STREAMFILE* sf, off_t offset, int channel) {
|
||||
size_t skip_count = 0;
|
||||
int i;
|
||||
|
||||
//skip_size = read_u32be(offset + 0x10*channel + 0x00, sf); /* wrong! */
|
||||
for (i = 0; i < channel; i++) {
|
||||
skip_count += read_u32be(offset + 0x10*i + 0x04, sf); /* number of frames of this channel */
|
||||
}
|
||||
|
||||
return skip_count;
|
||||
}
|
||||
|
||||
|
||||
/* Deblocks interleaved XMA in AWC blocks */
|
||||
static STREAMFILE* setup_awc_xma_streamfile(STREAMFILE *sf, off_t stream_offset, size_t stream_size, size_t block_size, int channel_count, int channel) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
|
||||
cfg.track_number = channel;
|
||||
cfg.track_count = channel_count;
|
||||
cfg.stream_start = stream_offset;
|
||||
cfg.stream_size = stream_size;
|
||||
cfg.chunk_size = block_size;
|
||||
//cfg.physical_offset = stream_offset;
|
||||
//cfg.logical_size = awc_xma_io_size(sf, &cfg); /* force init */
|
||||
cfg.block_callback = block_callback;
|
||||
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
|
||||
//new_sf = open_buffer_streamfile_f(new_sf, 0);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _AWC_XMA_STREAMFILE_H_ */
|
||||
|
62
src/meta/bkhd.c
Normal file
62
src/meta/bkhd.c
Normal file
@ -0,0 +1,62 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* BKHD - Wwise soundbank container */
|
||||
VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
off_t subfile_offset, didx_offset, data_offset, offset;
|
||||
size_t subfile_size, didx_size;
|
||||
uint32_t subfile_id;
|
||||
int big_endian;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*);
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"bnk"))
|
||||
goto fail;
|
||||
if (read_u32be(0x00, sf) != 0x424B4844) /* "BKHD" */
|
||||
goto fail;
|
||||
big_endian = guess_endianness32bit(0x04, sf);
|
||||
read_u32 = big_endian ? read_u32be : read_u32le;
|
||||
|
||||
/* Wwise banks have event/track/sequence/etc info in the HIRC chunk, as well
|
||||
* as other chunks, and may have a DIDX index to memory .wem in DATA.
|
||||
* We support the internal .wem mainly for quick tests, as the HIRC is
|
||||
* complex and better handled with TXTP (some info from Nicknine's script) */
|
||||
|
||||
/* unlike RIFF first chunk follows chunk rules */
|
||||
if (!find_chunk(sf, 0x44494458, 0x00,0, &didx_offset, &didx_size, big_endian, 0)) /* "DIDX" */
|
||||
goto fail;
|
||||
if (!find_chunk(sf, 0x44415441, 0x00,0, &data_offset, NULL, big_endian, 0)) /* "DATA" */
|
||||
goto fail;
|
||||
|
||||
total_subsongs = didx_size / 0x0c;
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
offset = didx_offset + (target_subsong - 1) * 0x0c;
|
||||
subfile_id = read_u32(offset + 0x00, sf);
|
||||
subfile_offset = read_u32(offset + 0x04, sf) + data_offset;
|
||||
subfile_size = read_u32(offset + 0x08, sf);
|
||||
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "wem");
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_wwise(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%i", subfile_id);
|
||||
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
337
src/meta/cstr.c
337
src/meta/cstr.c
@ -1,299 +1,106 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* .dsp w/ Cstr header, seen in Star Fox Assault and Donkey Konga */
|
||||
VGMSTREAM * init_vgmstream_cstr(STREAMFILE *streamFile) {
|
||||
|
||||
/* Cstr - from Namco NuSound v1 games [Mr. Driller (GC), Star Fox Assault (GC), Donkey Konga (GC)] */
|
||||
VGMSTREAM * init_vgmstream_cstr(STREAMFILE* sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
int loop_flag;
|
||||
off_t start_offset;
|
||||
off_t first_data;
|
||||
off_t loop_offset;
|
||||
size_t interleave;
|
||||
int loop_adjust;
|
||||
int double_loop_end = 0;
|
||||
size_t interleave, first_skip;
|
||||
int loop_flag, channels, sample_rate;
|
||||
int loop_start, loop_end, num_samples, num_nibbles;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("dsp",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if ((uint32_t)read_32bitBE(0,streamFile)!=0x43737472) /* "Cstr" */
|
||||
goto fail;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"header ok\n");
|
||||
#endif
|
||||
|
||||
if (read_8bit(0x1b,streamFile)==1) {
|
||||
/* mono version, much simpler to handle */
|
||||
/* Only seen in R Racing Evolution radio sfx */
|
||||
|
||||
start_offset = 0x80;
|
||||
loop_flag = read_16bitBE(0x2c,streamFile);
|
||||
|
||||
/* check initial predictor/scale */
|
||||
if (read_16bitBE(0x5e,streamFile) != (uint8_t)read_8bit(start_offset,streamFile))
|
||||
goto fail;
|
||||
|
||||
/* check type==0 and gain==0 */
|
||||
if (read_16bitBE(0x2e,streamFile) || read_16bitBE(0x5c,streamFile))
|
||||
goto fail;
|
||||
|
||||
loop_offset = start_offset+read_32bitBE(0x10,streamFile);
|
||||
if (loop_flag) {
|
||||
if (read_16bitBE(0x64,streamFile) != (uint8_t)read_8bit(loop_offset,streamFile)) goto fail;
|
||||
}
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
|
||||
vgmstream = allocate_vgmstream(1,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->sample_rate = read_32bitBE(0x28,streamFile);
|
||||
vgmstream->num_samples = read_32bitBE(0x20,streamFile);
|
||||
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = dsp_nibbles_to_samples(
|
||||
read_32bitBE(0x30,streamFile));
|
||||
vgmstream->loop_end_sample = dsp_nibbles_to_samples(
|
||||
read_32bitBE(0x34,streamFile))+1;
|
||||
}
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_DSP_CSTR;
|
||||
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<16;i++)
|
||||
vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(0x3c+i*2,streamFile);
|
||||
}
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!vgmstream->ch[0].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[0].channel_start_offset=
|
||||
vgmstream->ch[0].offset=
|
||||
start_offset;
|
||||
|
||||
return vgmstream;
|
||||
} /* end mono */
|
||||
|
||||
interleave = read_16bitBE(0x06,streamFile);
|
||||
start_offset = 0xe0;
|
||||
first_data = start_offset+read_32bitBE(0x0c,streamFile);
|
||||
loop_flag = read_16bitBE(0x2c,streamFile);
|
||||
|
||||
if (!loop_flag) {
|
||||
/* Nonlooped tracks seem to follow no discernable pattern
|
||||
* with where they actually start.
|
||||
* But! with the magic of initial p/s redundancy, we can guess.
|
||||
*/
|
||||
while (first_data<start_offset+0x800 &&
|
||||
(read_16bitBE(0x5e,streamFile) != (uint8_t)read_8bit(first_data,streamFile) ||
|
||||
read_16bitBE(0xbe,streamFile) != (uint8_t)read_8bit(first_data+interleave,streamFile)))
|
||||
first_data+=8;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"guessed first_data at %#x\n",first_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* check initial predictor/scale */
|
||||
if (read_16bitBE(0x5e,streamFile) != (uint8_t)read_8bit(first_data,streamFile))
|
||||
goto fail;
|
||||
if (read_16bitBE(0xbe,streamFile) != (uint8_t)read_8bit(first_data+interleave,streamFile))
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"dsp"))
|
||||
goto fail;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"p/s ok\n");
|
||||
#endif
|
||||
|
||||
/* check type==0 and gain==0 */
|
||||
if (read_16bitBE(0x2e,streamFile) || read_16bitBE(0x5c,streamFile))
|
||||
goto fail;
|
||||
if (read_16bitBE(0x8e,streamFile) || read_16bitBE(0xbc,streamFile))
|
||||
if (read_u32be(0x00,sf) != 0x43737472) /* "Cstr" */
|
||||
goto fail;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"type & gain ok\n");
|
||||
#endif
|
||||
/* 0x04: version (0x0066 = 0.66 as seen in nus_config .txt) */
|
||||
interleave = read_u16be(0x06, sf);
|
||||
/* 0x08: config? (volume/pan/etc?) */
|
||||
first_skip = read_s32be(0x0c, sf); /* first interleaved block has normal size, but starts late */
|
||||
loop_start = read_s32be(0x10, sf);
|
||||
/* 0x14: num samples LE or null in mono files */
|
||||
/* 0x18: sample rate LE or null in mono files */
|
||||
/* 0x01a: always 0x40 */
|
||||
channels = read_u8(0x1b, sf); /* mono only seen in R:Racing Evolution radio sfx */
|
||||
/* 0x1c: always null */
|
||||
|
||||
/* check for loop flag agreement */
|
||||
if (read_16bitBE(0x2c,streamFile) != read_16bitBE(0x8c,streamFile))
|
||||
goto fail;
|
||||
/* next is DSP header, with some oddities:
|
||||
* - loop flag isn't always set vs Cstr's flag (won't have DSP loop_ps/etc)
|
||||
* - loop start nibbles can be wrong even with loop flag set
|
||||
* - wonky loop_ps as a result (other fields agree between channels) */
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop flags agree\n");
|
||||
#endif
|
||||
num_samples = read_s32be(0x20 + 0x00, sf);
|
||||
num_nibbles = read_s32be(0x20 + 0x04, sf);
|
||||
sample_rate = read_s32be(0x20 + 0x08, sf);
|
||||
//loop_flag = read_s16be(0x20 + 0x0c, sf);
|
||||
//loop_start = read_s32be(0x20 + 0x10, sf);
|
||||
loop_end = read_s32be(0x20 + 0x14, sf);
|
||||
|
||||
loop_offset = start_offset+read_32bitBE(0x10,streamFile)*2;
|
||||
if (loop_flag) {
|
||||
int loops_ok=0;
|
||||
/* check loop predictor/scale */
|
||||
/* some fuzz allowed */
|
||||
for (loop_adjust=0;loop_adjust>=-0x10;loop_adjust-=8) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"looking for loop p/s at %#x,%#x\n",loop_offset-interleave+loop_adjust,loop_offset+loop_adjust);
|
||||
#endif
|
||||
if (read_16bitBE(0x64,streamFile) == (uint8_t)read_8bit(loop_offset-interleave+loop_adjust,streamFile) &&
|
||||
read_16bitBE(0xc4,streamFile) == (uint8_t)read_8bit(loop_offset+loop_adjust,streamFile)) {
|
||||
loops_ok=1;
|
||||
loop_flag = (loop_start >= 0);
|
||||
start_offset = 0x20 + 0x60 * channels + first_skip;
|
||||
|
||||
/* nonlooped tracks may not set first skip for no reason, but can be tested with initial p/s */
|
||||
if (!loop_flag && channels == 2 && first_skip == 0) {
|
||||
while (first_skip < 0x800) {
|
||||
if (read_u16be(0x20 + 0x3e, sf) == read_u8(start_offset + first_skip, sf) &&
|
||||
read_u16be(0x20 + 0x60 + 0x3e, sf) == read_u8(start_offset + first_skip + interleave, sf))
|
||||
break;
|
||||
}
|
||||
first_skip += 0x08;
|
||||
}
|
||||
if (!loops_ok)
|
||||
for (loop_adjust=interleave;loop_adjust<=interleave+0x10;loop_adjust+=8) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"looking for loop p/s at %#x,%#x\n",loop_offset-interleave+loop_adjust,loop_offset+loop_adjust);
|
||||
#endif
|
||||
if (read_16bitBE(0x64,streamFile) == (uint8_t)read_8bit(loop_offset-interleave+loop_adjust,streamFile) &&
|
||||
read_16bitBE(0xc4,streamFile) == (uint8_t)read_8bit(loop_offset+loop_adjust,streamFile)) {
|
||||
loops_ok=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!loops_ok) goto fail;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop p/s ok (with %#4x adjust)\n",loop_adjust);
|
||||
#endif
|
||||
|
||||
/* check for agreement */
|
||||
/* loop end (channel 1 & 2 headers) */
|
||||
if (read_32bitBE(0x34,streamFile) != read_32bitBE(0x94,streamFile))
|
||||
goto fail;
|
||||
|
||||
/* Mr. Driller oddity */
|
||||
if (dsp_nibbles_to_samples(read_32bitBE(0x34,streamFile)*2)+1 <= read_32bitBE(0x20,streamFile)) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop end <= half total samples, should be doubled\n");
|
||||
#endif
|
||||
double_loop_end = 1;
|
||||
}
|
||||
|
||||
/* loop start (Cstr header and channel 1 header) */
|
||||
if (read_32bitBE(0x30,streamFile) != read_32bitBE(0x10,streamFile)
|
||||
#if 0
|
||||
/* this particular glitch only true for SFA, though it
|
||||
* seems like something similar happens in Donkey Konga */
|
||||
/* loop start (Cstr, channel 1 & 2 headers) */
|
||||
|| (read_32bitBE(0x0c,streamFile)+read_32bitLE(0x30,streamFile)) !=
|
||||
read_32bitBE(0x90,streamFile)
|
||||
#endif
|
||||
)
|
||||
/* alternatively (Donkey Konga) the header loop is 0x0c+0x10 */
|
||||
if (
|
||||
/* loop start (Cstr header and channel 1 header) */
|
||||
read_32bitBE(0x30,streamFile) != read_32bitBE(0x10,streamFile)+
|
||||
read_32bitBE(0x0c,streamFile))
|
||||
/* further alternatively (Donkey Konga), if we loop back to
|
||||
* the very first frame 0x30 might be 0x00000002 (which
|
||||
* is a *valid* std dsp loop start, imagine that) while 0x10
|
||||
* is 0x00000000 */
|
||||
if (!(read_32bitBE(0x30,streamFile) == 2 &&
|
||||
read_32bitBE(0x10,streamFile) == 0))
|
||||
/* lest there be too few alternatives, in Mr. Driller we
|
||||
* find that [0x30] + [0x0c] + 8 = [0x10]*2 */
|
||||
if (!(double_loop_end &&
|
||||
read_32bitBE(0x30,streamFile) +
|
||||
read_32bitBE(0x0c,streamFile) + 8 ==
|
||||
read_32bitBE(0x10,streamFile)*2)) {
|
||||
/* for Mr.Driller Drill Land, no idea about the above but seems to play and loop ok */
|
||||
VGM_LOG("Cstr: bad loop check ignored\n");
|
||||
//goto fail;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop points agree\n");
|
||||
#endif
|
||||
/* not found */
|
||||
if (first_skip == 0x800)
|
||||
first_skip = 0;
|
||||
}
|
||||
|
||||
/* assure that sample counts, sample rates agree */
|
||||
if (
|
||||
/* sample count (channel 1 & 2 headers) */
|
||||
read_32bitBE(0x20,streamFile) != read_32bitBE(0x80,streamFile) ||
|
||||
/* sample rate (channel 1 & 2 headers) */
|
||||
read_32bitBE(0x28,streamFile) != read_32bitBE(0x88,streamFile) ||
|
||||
/* sample count (Cstr header and channel 1 header) */
|
||||
read_32bitLE(0x14,streamFile) != read_32bitBE(0x20,streamFile) ||
|
||||
/* sample rate (Cstr header and channel 1 header) */
|
||||
(uint16_t)read_16bitLE(0x18,streamFile) != read_32bitBE(0x28,streamFile))
|
||||
goto fail;
|
||||
if (first_skip > 0 && loop_start >= (interleave - first_skip))
|
||||
loop_start = loop_start - (interleave - first_skip);
|
||||
loop_start = loop_start * 2;
|
||||
|
||||
/* Mr. Driller oddity, unreliable loop flag */
|
||||
if (loop_end == num_nibbles) {
|
||||
loop_flag = 0;
|
||||
}
|
||||
|
||||
/* Mr. Driller oddity, half nibbles */
|
||||
if (loop_end * 2 + 1 <= num_nibbles) {
|
||||
loop_end = loop_end * 2;
|
||||
}
|
||||
|
||||
|
||||
/* no loop_ps checks given how buggy the format is */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
|
||||
vgmstream = allocate_vgmstream(2,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->sample_rate = read_32bitBE(0x28,streamFile);
|
||||
/* This is a slight hack to counteract their hack.
|
||||
* All the data is ofset by first_data so that the loop
|
||||
* point occurs at a block boundary. However, I always begin decoding
|
||||
* right after the header, as that is the start of the first block and
|
||||
* my interleave code relies on starting at the beginning of a block.
|
||||
* So we decode a few silent samples at the beginning, and here we make up
|
||||
* for it by lengthening the track by that much.
|
||||
*/
|
||||
vgmstream->num_samples = read_32bitBE(0x20,streamFile) +
|
||||
(first_data-start_offset)/8*14;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = dsp_bytes_to_samples(loop_start, channels);
|
||||
|
||||
if (loop_flag) {
|
||||
off_t loop_start_bytes = loop_offset-start_offset-interleave;
|
||||
vgmstream->loop_start_sample = dsp_nibbles_to_samples((loop_start_bytes/(2*interleave)*interleave+loop_start_bytes%(interleave*2))*2);
|
||||
/*dsp_nibbles_to_samples(loop_start_bytes);*/
|
||||
/*dsp_nibbles_to_samples(read_32bitBE(0x30,streamFile)*2-inter);*/
|
||||
vgmstream->loop_end_sample = dsp_nibbles_to_samples(
|
||||
read_32bitBE(0x34,streamFile))+1;
|
||||
|
||||
if (double_loop_end)
|
||||
vgmstream->loop_end_sample =
|
||||
dsp_nibbles_to_samples(read_32bitBE(0x34,streamFile)*2)+1;
|
||||
|
||||
if (vgmstream->loop_end_sample > vgmstream->num_samples) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop_end_sample > num_samples, adjusting\n");
|
||||
#endif
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
}
|
||||
vgmstream->loop_end_sample = dsp_nibbles_to_samples(loop_end) + 1;
|
||||
/* Donkey Konga 3 oddity, loop/num nibbles not correct vs final samples */
|
||||
if (vgmstream->loop_end_sample > num_samples)
|
||||
vgmstream->loop_end_sample = num_samples;
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
vgmstream->interleave_first_block_size = interleave - first_skip;
|
||||
vgmstream->interleave_first_skip = first_skip;
|
||||
vgmstream->meta_type = meta_DSP_CSTR;
|
||||
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<16;i++)
|
||||
vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(0x3c+i*2,streamFile);
|
||||
for (i=0;i<16;i++)
|
||||
vgmstream->ch[1].adpcm_coef[i]=read_16bitBE(0x9c+i*2,streamFile);
|
||||
}
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<2;i++) {
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=
|
||||
start_offset+interleave*i;
|
||||
}
|
||||
}
|
||||
dsp_read_coefs_be(vgmstream, sf, 0x3c, 0x60);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
1258
src/meta/fsb5.c
1258
src/meta/fsb5.c
File diff suppressed because it is too large
Load Diff
@ -1,201 +0,0 @@
|
||||
#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_ */
|
21
src/meta/fsb5_streamfile.h
Normal file
21
src/meta/fsb5_streamfile.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef _FSB5_STREAMFILE_H_
|
||||
#define _FSB5_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
static STREAMFILE* setup_fsb5_streamfile(STREAMFILE* sf, off_t stream_start, size_t stream_size, int stream_count, int stream_number, size_t interleave) {
|
||||
STREAMFILE* new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
|
||||
cfg.stream_start = stream_start;
|
||||
cfg.stream_size = stream_size;
|
||||
cfg.chunk_size = interleave;
|
||||
cfg.step_start = stream_number;
|
||||
cfg.step_count = stream_count;
|
||||
|
||||
/* setup sf */
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _FSB5_STREAMFILE_H_ */
|
@ -229,6 +229,7 @@ static const hcakey_info hcakey_list[] = {
|
||||
{5047159794308}, // 00000497222AAA84
|
||||
|
||||
// Shin Tennis no Ouji-sama: Rising Beat (iOS/Android) voices?
|
||||
// UNI'S ON AIR (iOS/Android)
|
||||
{4902201417679}, // 0000047561F95FCF
|
||||
|
||||
// Kai-ri-Sei Million Arthur (Vita)
|
||||
|
@ -888,4 +888,6 @@ VGMSTREAM * init_vgmstream_kwb(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_lrmd(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -1,75 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* VID1 - from Neversoft games (Gun, Tony Hawk's American Wasteland GC) */
|
||||
VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, header_offset, header_size;
|
||||
int loop_flag = 0, channel_count;
|
||||
|
||||
|
||||
/* check extension */
|
||||
if (!check_extensions(streamFile,"ogg,logg"))
|
||||
goto fail;
|
||||
|
||||
/* chunked/blocked format containing video or audio frames */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x56494431) /* "VID1" */
|
||||
goto fail;
|
||||
|
||||
/* find actual header start/size in the chunks */
|
||||
{
|
||||
header_offset = read_32bitBE(0x04, streamFile);
|
||||
if (read_32bitBE(header_offset,streamFile) != 0x48454144) /* "HEAD" */
|
||||
goto fail;
|
||||
start_offset = header_offset + read_32bitBE(header_offset + 0x04, streamFile);
|
||||
header_offset += 0x0c;
|
||||
|
||||
/* videos have VIDH before AUDH, and VIDD in blocks, but aren't parsed ATM */
|
||||
|
||||
if (read_32bitBE(header_offset,streamFile) != 0x41554448) /* "AUDH" */
|
||||
goto fail;
|
||||
header_size = read_32bitBE(header_offset + 0x04, streamFile);
|
||||
header_offset += 0x0c;
|
||||
|
||||
if (read_32bitBE(header_offset,streamFile) != 0x56415544) /* "VAUD" (Vorbis audio?) */
|
||||
goto fail;
|
||||
header_offset += 0x04;
|
||||
header_size -= 0x10;
|
||||
|
||||
}
|
||||
channel_count = read_8bit(header_offset + 0x04,streamFile);
|
||||
/* other values unknown, maybe related to vorbis (ex. bitrate/encoding modes) */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitBE(header_offset + 0x00, streamFile);
|
||||
vgmstream->num_samples = read_32bitBE(header_offset + 0x1c, streamFile);
|
||||
vgmstream->meta_type = meta_NGC_VID1;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{
|
||||
vorbis_custom_config cfg = {0};
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->codec_data = init_vorbis_custom(streamFile, header_offset + 0x20, VORBIS_VID1, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
/* open the file for reading */
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -1,44 +1,56 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* NPFS - found in Namco PS2/PSP games [Tekken 5 (PS2), Venus & Braves (PS2), Ridge Racer (PSP)] */
|
||||
VGMSTREAM * init_vgmstream_nps(STREAMFILE *streamFile) {
|
||||
/* NPFS - found in Namco NuSound v1 games [Tekken 5 (PS2), Venus & Braves (PS2), Ridge Racer (PSP)] */
|
||||
VGMSTREAM* init_vgmstream_nps(STREAMFILE* sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
uint32_t channel_size;
|
||||
int loop_flag, channel_count, loop_start, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .nps: referenced extension (ex. Venus & Braves data files)
|
||||
/* .nps: referenced extension (ex. Venus & Braves, Ridge Racer data files)
|
||||
* .npsf: header id (Namco Production Sound File?) */
|
||||
if ( !check_extensions(streamFile,"nps,npsf"))
|
||||
if ( !check_extensions(sf,"nps,npsf"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x4E505346) /* "NPSF" */
|
||||
if (read_u32be(0x00, sf) != 0x4E505346) /* "NPSF" */
|
||||
goto fail;
|
||||
|
||||
loop_flag = (read_32bitLE(0x14,streamFile) != 0xFFFFFFFF);
|
||||
channel_count = read_32bitLE(0x0C,streamFile);
|
||||
start_offset = (off_t)read_32bitLE(0x10,streamFile);
|
||||
/* 0x04: version? (0x00001000 = 1.00?) */
|
||||
channel_size = read_s32le(0x08, sf);
|
||||
channel_count = read_s32le(0x0C, sf);
|
||||
start_offset = read_s32le(0x10, sf); /* interleave? */
|
||||
loop_start = read_s32le(0x14, sf);
|
||||
sample_rate = read_s32le(0x18, sf);
|
||||
/* 0x1c: volume? (0x3e8 = 1000 = max) */
|
||||
/* 0x20: flags? (varies between sound types in a game, but no clear pattern vs other games) */
|
||||
/* 0x24: flag? (0/1) */
|
||||
/* 0x28: null */
|
||||
/* 0x2c: null */
|
||||
/* 0x30: always 0x40 */
|
||||
/* 0x34: name (usually null terminated but may contain garbage) */
|
||||
/* rest: null or 0xFF until start */
|
||||
loop_flag = loop_start != -1;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->channels = read_32bitLE(0x0C,streamFile);
|
||||
vgmstream->sample_rate = read_32bitLE(0x18,streamFile);
|
||||
vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x08,streamFile), 1); /* single channel data */
|
||||
if(vgmstream->loop_flag) {
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitLE(0x08,streamFile), 1);
|
||||
}
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(channel_size, 1);
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x04,streamFile) / 2;
|
||||
vgmstream->interleave_block_size = 0x800;
|
||||
vgmstream->meta_type = meta_NPS;
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, 0x34,streamFile);
|
||||
read_string(vgmstream->stream_name, STREAM_NAME_SIZE, 0x34, sf);
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -172,7 +172,7 @@ VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) {
|
||||
/* 0x30+: extra chunks (0x00: 0x7f, 0x04: num_sample), alt loop starts/regions? */
|
||||
|
||||
if (channel_count == 6) {
|
||||
/* 2ch multistream hacky-hacks, don't try this at home. We'll end up with:
|
||||
/* 2ch multistream hacky-hacks in RE:RE, don't try this at home. We'll end up with:
|
||||
* main vgmstream > N vgmstream layers > substream IO deinterleaver > opus meta > Opus IO transmogrifier (phew) */
|
||||
layered_layout_data* data = NULL;
|
||||
int layers = channel_count / 2;
|
||||
@ -193,11 +193,11 @@ VGMSTREAM * init_vgmstream_opus_capcom(STREAMFILE *streamFile) {
|
||||
|
||||
/* open each layer subfile */
|
||||
for (i = 0; i < layers; i++) {
|
||||
STREAMFILE* temp_streamFile = setup_opus_interleave_streamfile(streamFile, offset+0x28*i, layers);
|
||||
if (!temp_streamFile) goto fail;
|
||||
STREAMFILE* temp_sf = setup_opus_interleave_streamfile(streamFile, offset, i, layers);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
data->layers[i] = init_vgmstream_opus(temp_streamFile, meta_OPUS, 0x00, num_samples,loop_start,loop_end);
|
||||
close_streamfile(temp_streamFile);
|
||||
data->layers[i] = init_vgmstream_opus(temp_sf, meta_OPUS, 0x00, num_samples,loop_start,loop_end);
|
||||
close_streamfile(temp_sf);
|
||||
if (!data->layers[i]) goto fail;
|
||||
}
|
||||
|
||||
|
@ -1,136 +1,47 @@
|
||||
#ifndef _OPUS_INTERLEAVE_STREAMFILE_H_
|
||||
#define _OPUS_INTERLEAVE_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
int streams;
|
||||
off_t stream_offset;
|
||||
|
||||
/* 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 */
|
||||
|
||||
size_t logical_size;
|
||||
} opus_interleave_io_data;
|
||||
|
||||
|
||||
/* Reads skipping other streams */
|
||||
static size_t opus_interleave_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, opus_interleave_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
|
||||
/* ignore bad reads */
|
||||
if (offset < 0 || offset > data->logical_size) {
|
||||
return total_read;
|
||||
}
|
||||
|
||||
/* previous offset: re-start as we can't map logical<>physical offsets (may be VBR) */
|
||||
if (offset < data->logical_offset) {
|
||||
data->physical_offset = data->stream_offset;
|
||||
data->logical_offset = 0x00;
|
||||
data->skip_frames = 0;
|
||||
}
|
||||
|
||||
/* read doing one frame at a time */
|
||||
while (length > 0) {
|
||||
size_t data_size;
|
||||
|
||||
/* ignore EOF */
|
||||
if (data->logical_offset >= data->logical_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* process block (must be read every time since skip frame sizes may vary) */
|
||||
{
|
||||
data_size = read_32bitBE(data->physical_offset,streamfile);
|
||||
if ((uint32_t)data_size == 0x01000080) //todo not ok if offset between 0 and header_size
|
||||
data_size = read_32bitLE(data->physical_offset+0x10,streamfile) + 0x08;
|
||||
else
|
||||
data_size += 0x08;
|
||||
}
|
||||
|
||||
/* skip frames from other streams */
|
||||
if (data->skip_frames) {
|
||||
data->physical_offset += data_size;
|
||||
data->skip_frames--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* move to next block */
|
||||
if (offset >= data->logical_offset + data_size) {
|
||||
data->physical_offset += data_size;
|
||||
data->logical_offset += data_size;
|
||||
data->skip_frames = data->streams - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
{
|
||||
size_t bytes_consumed, bytes_done, to_read;
|
||||
|
||||
bytes_consumed = offset - data->logical_offset;
|
||||
to_read = data_size - bytes_consumed;
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
bytes_done = read_streamfile(dest, data->physical_offset + bytes_consumed, to_read, streamfile);
|
||||
|
||||
offset += bytes_done;
|
||||
total_read += bytes_done;
|
||||
length -= bytes_done;
|
||||
dest += bytes_done;
|
||||
|
||||
if (bytes_done != to_read || bytes_done == 0) {
|
||||
break; /* error/EOF */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t opus_interleave_io_size(STREAMFILE *streamfile, opus_interleave_io_data* data) {
|
||||
off_t info_offset;
|
||||
|
||||
if (data->logical_size)
|
||||
return data->logical_size;
|
||||
|
||||
info_offset = read_32bitLE(data->stream_offset+0x10,streamfile);
|
||||
data->logical_size = (0x08+info_offset) + read_32bitLE(data->stream_offset+info_offset+0x04,streamfile);
|
||||
return data->logical_size;
|
||||
}
|
||||
|
||||
|
||||
/* 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};
|
||||
size_t io_data_size = sizeof(opus_interleave_io_data);
|
||||
|
||||
io_data.stream_offset = start_offset;
|
||||
io_data.streams = streams;
|
||||
io_data.physical_offset = start_offset;
|
||||
io_data.logical_size = opus_interleave_io_size(streamFile, &io_data); /* force init */
|
||||
|
||||
/* 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, opus_interleave_io_read,opus_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 /* _OPUS_INTERLEAVE_STREAMFILE_H_ */
|
||||
#ifndef _OPUS_INTERLEAVE_STREAMFILE_H_
|
||||
#define _OPUS_INTERLEAVE_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
|
||||
static void block_callback(STREAMFILE* sf, deblock_io_data* data) {
|
||||
uint32_t chunk_size = read_u32be(data->physical_offset, sf);
|
||||
|
||||
//todo not ok if offset between 0 and header_size
|
||||
if (chunk_size == 0x01000080) /* header */
|
||||
chunk_size = read_u32le(data->physical_offset + 0x10, sf) + 0x08;
|
||||
else
|
||||
chunk_size += 0x08;
|
||||
data->block_size = chunk_size;
|
||||
data->data_size = data->block_size;
|
||||
}
|
||||
|
||||
/* Deblocks NXOPUS streams that interleave 1 packet per stream */
|
||||
static STREAMFILE* setup_opus_interleave_streamfile(STREAMFILE* sf, off_t start_offset, int stream_number, int stream_count) {
|
||||
STREAMFILE* new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
|
||||
cfg.stream_start = start_offset;
|
||||
cfg.step_start = stream_number;
|
||||
cfg.step_count = stream_count;
|
||||
{
|
||||
int i;
|
||||
off_t offset = start_offset;
|
||||
/* read full size from NXOPUS header N */
|
||||
for (i = 0; i < stream_number + 1; i++) {
|
||||
off_t start = read_s32le(offset + 0x10, sf);
|
||||
off_t size = read_s32le(offset + start + 0x04, sf);
|
||||
|
||||
if (i == stream_number)
|
||||
cfg.logical_size = 0x08 + start + size;
|
||||
offset += 0x08 + start;
|
||||
}
|
||||
}
|
||||
cfg.block_callback = block_callback;
|
||||
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
|
||||
//new_sf = open_buffer_streamfile_f(new_sf, 0);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _OPUS_INTERLEAVE_STREAMFILE_H_ */
|
||||
|
@ -509,20 +509,21 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x4E584246: /* "NXBF" (Namco NUS v1) [R:Racing Evolution (Xbox)] */
|
||||
/* 0x00: "NXBF" again */
|
||||
/* 0x04: always 0x1000? */
|
||||
case 0x4E584246: /* "NXBF" (Namco NuSound v1) [R:Racing Evolution (Xbox)] */
|
||||
/* very similar to NUS's NPSF, but not quite like Cstr */
|
||||
/* 0x00: "NXBF" id */
|
||||
/* 0x04: version? (0x00001000 = 1.00?) */
|
||||
/* 0x08: data size */
|
||||
/* 0x0c: channels */
|
||||
/* 0x10: null */
|
||||
loop_start_nxbf = read_32bitLE(current_chunk + 0x08 + 0x14, streamFile);
|
||||
/* 0x18: sample rate */
|
||||
/* 0x1c: volume? */
|
||||
/* 0x1c: volume? (0x3e8 = 1000 = max) */
|
||||
/* 0x20: type/flags? */
|
||||
/* 0x24: codec? */
|
||||
/* 0x24: flag? */
|
||||
/* 0x28: null */
|
||||
/* 0x2c: null */
|
||||
/* 0x30: type/flags? */
|
||||
/* 0x30: always 0x40 */
|
||||
loop_flag = (loop_start_nxbf >= 0);
|
||||
break;
|
||||
|
||||
|
@ -1,86 +1,67 @@
|
||||
#ifndef _SQEX_SEAD_STREAMFILE_H_
|
||||
#define _SQEX_SEAD_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
static STREAMFILE* setup_sqex_sead_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start);
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t header_size;
|
||||
size_t key_start;
|
||||
} sqex_sead_decryption_data;
|
||||
|
||||
|
||||
static size_t sqex_sead_decryption_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, sqex_sead_decryption_data* data) {
|
||||
/* Found in FFXII_TZA.exe (same key in SCD Ogg V3) */
|
||||
static const uint8_t encryption_key[0x100] = {
|
||||
0x3A,0x32,0x32,0x32,0x03,0x7E,0x12,0xF7,0xB2,0xE2,0xA2,0x67,0x32,0x32,0x22,0x32, // 00-0F
|
||||
0x32,0x52,0x16,0x1B,0x3C,0xA1,0x54,0x7B,0x1B,0x97,0xA6,0x93,0x1A,0x4B,0xAA,0xA6, // 10-1F
|
||||
0x7A,0x7B,0x1B,0x97,0xA6,0xF7,0x02,0xBB,0xAA,0xA6,0xBB,0xF7,0x2A,0x51,0xBE,0x03, // 20-2F
|
||||
0xF4,0x2A,0x51,0xBE,0x03,0xF4,0x2A,0x51,0xBE,0x12,0x06,0x56,0x27,0x32,0x32,0x36, // 30-3F
|
||||
0x32,0xB2,0x1A,0x3B,0xBC,0x91,0xD4,0x7B,0x58,0xFC,0x0B,0x55,0x2A,0x15,0xBC,0x40, // 40-4F
|
||||
0x92,0x0B,0x5B,0x7C,0x0A,0x95,0x12,0x35,0xB8,0x63,0xD2,0x0B,0x3B,0xF0,0xC7,0x14, // 50-5F
|
||||
0x51,0x5C,0x94,0x86,0x94,0x59,0x5C,0xFC,0x1B,0x17,0x3A,0x3F,0x6B,0x37,0x32,0x32, // 60-6F
|
||||
0x30,0x32,0x72,0x7A,0x13,0xB7,0x26,0x60,0x7A,0x13,0xB7,0x26,0x50,0xBA,0x13,0xB4, // 70-7F
|
||||
0x2A,0x50,0xBA,0x13,0xB5,0x2E,0x40,0xFA,0x13,0x95,0xAE,0x40,0x38,0x18,0x9A,0x92, // 80-8F
|
||||
0xB0,0x38,0x00,0xFA,0x12,0xB1,0x7E,0x00,0xDB,0x96,0xA1,0x7C,0x08,0xDB,0x9A,0x91, // 90-9F
|
||||
0xBC,0x08,0xD8,0x1A,0x86,0xE2,0x70,0x39,0x1F,0x86,0xE0,0x78,0x7E,0x03,0xE7,0x64, // A0-AF
|
||||
0x51,0x9C,0x8F,0x34,0x6F,0x4E,0x41,0xFC,0x0B,0xD5,0xAE,0x41,0xFC,0x0B,0xD5,0xAE, // B0-BF
|
||||
0x41,0xFC,0x3B,0x70,0x71,0x64,0x33,0x32,0x12,0x32,0x32,0x36,0x70,0x34,0x2B,0x56, // C0-CF
|
||||
0x22,0x70,0x3A,0x13,0xB7,0x26,0x60,0xBA,0x1B,0x94,0xAA,0x40,0x38,0x00,0xFA,0xB2, // D0-DF
|
||||
0xE2,0xA2,0x67,0x32,0x32,0x12,0x32,0xB2,0x32,0x32,0x32,0x32,0x75,0xA3,0x26,0x7B, // E0-EF
|
||||
0x83,0x26,0xF9,0x83,0x2E,0xFF,0xE3,0x16,0x7D,0xC0,0x1E,0x63,0x21,0x07,0xE3,0x01, // F0-FF
|
||||
};
|
||||
size_t bytes_read;
|
||||
off_t encrypted_offset = data->header_size;
|
||||
int i;
|
||||
|
||||
bytes_read = streamfile->read(streamfile, dest, offset, length);
|
||||
|
||||
/* decrypt data (xor) */
|
||||
if (offset >= encrypted_offset) {
|
||||
for (i = 0; i < bytes_read; i++) {
|
||||
dest[i] ^= encryption_key[(data->key_start + (offset - encrypted_offset) + i) % 0x100];
|
||||
}
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
/* decrypts subfile if neccessary */
|
||||
static STREAMFILE* setup_sqex_sead_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
if (encryption) {
|
||||
sqex_sead_decryption_data io_data = {0};
|
||||
size_t io_data_size = sizeof(sqex_sead_decryption_data);
|
||||
|
||||
io_data.header_size = header_size;
|
||||
io_data.key_start = key_start;
|
||||
|
||||
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, sqex_sead_decryption_read,NULL);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
}
|
||||
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"hca");
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _SQEX_SEAD_STREAMFILE_H_ */
|
||||
#ifndef _SQEX_SEAD_STREAMFILE_H_
|
||||
#define _SQEX_SEAD_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t start;
|
||||
size_t key_start;
|
||||
} sqex_sead_io_data;
|
||||
|
||||
|
||||
static size_t sqex_sead_io_read(STREAMFILE *sf, uint8_t *dest, off_t offset, size_t length, sqex_sead_io_data* data) {
|
||||
/* Found in FFXII_TZA.exe (same key in SCD Ogg V3) */
|
||||
static const uint8_t key[0x100] = {
|
||||
0x3A,0x32,0x32,0x32,0x03,0x7E,0x12,0xF7,0xB2,0xE2,0xA2,0x67,0x32,0x32,0x22,0x32, // 00-0F
|
||||
0x32,0x52,0x16,0x1B,0x3C,0xA1,0x54,0x7B,0x1B,0x97,0xA6,0x93,0x1A,0x4B,0xAA,0xA6, // 10-1F
|
||||
0x7A,0x7B,0x1B,0x97,0xA6,0xF7,0x02,0xBB,0xAA,0xA6,0xBB,0xF7,0x2A,0x51,0xBE,0x03, // 20-2F
|
||||
0xF4,0x2A,0x51,0xBE,0x03,0xF4,0x2A,0x51,0xBE,0x12,0x06,0x56,0x27,0x32,0x32,0x36, // 30-3F
|
||||
0x32,0xB2,0x1A,0x3B,0xBC,0x91,0xD4,0x7B,0x58,0xFC,0x0B,0x55,0x2A,0x15,0xBC,0x40, // 40-4F
|
||||
0x92,0x0B,0x5B,0x7C,0x0A,0x95,0x12,0x35,0xB8,0x63,0xD2,0x0B,0x3B,0xF0,0xC7,0x14, // 50-5F
|
||||
0x51,0x5C,0x94,0x86,0x94,0x59,0x5C,0xFC,0x1B,0x17,0x3A,0x3F,0x6B,0x37,0x32,0x32, // 60-6F
|
||||
0x30,0x32,0x72,0x7A,0x13,0xB7,0x26,0x60,0x7A,0x13,0xB7,0x26,0x50,0xBA,0x13,0xB4, // 70-7F
|
||||
0x2A,0x50,0xBA,0x13,0xB5,0x2E,0x40,0xFA,0x13,0x95,0xAE,0x40,0x38,0x18,0x9A,0x92, // 80-8F
|
||||
0xB0,0x38,0x00,0xFA,0x12,0xB1,0x7E,0x00,0xDB,0x96,0xA1,0x7C,0x08,0xDB,0x9A,0x91, // 90-9F
|
||||
0xBC,0x08,0xD8,0x1A,0x86,0xE2,0x70,0x39,0x1F,0x86,0xE0,0x78,0x7E,0x03,0xE7,0x64, // A0-AF
|
||||
0x51,0x9C,0x8F,0x34,0x6F,0x4E,0x41,0xFC,0x0B,0xD5,0xAE,0x41,0xFC,0x0B,0xD5,0xAE, // B0-BF
|
||||
0x41,0xFC,0x3B,0x70,0x71,0x64,0x33,0x32,0x12,0x32,0x32,0x36,0x70,0x34,0x2B,0x56, // C0-CF
|
||||
0x22,0x70,0x3A,0x13,0xB7,0x26,0x60,0xBA,0x1B,0x94,0xAA,0x40,0x38,0x00,0xFA,0xB2, // D0-DF
|
||||
0xE2,0xA2,0x67,0x32,0x32,0x12,0x32,0xB2,0x32,0x32,0x32,0x32,0x75,0xA3,0x26,0x7B, // E0-EF
|
||||
0x83,0x26,0xF9,0x83,0x2E,0xFF,0xE3,0x16,0x7D,0xC0,0x1E,0x63,0x21,0x07,0xE3,0x01, // F0-FF
|
||||
};
|
||||
int i;
|
||||
size_t bytes = read_streamfile(dest, offset, length, sf);
|
||||
|
||||
/* decrypt data (xor) */
|
||||
//if (offset >= data->start) {
|
||||
for (i = 0; i < bytes; i++) {
|
||||
if (offset + i >= data->start) { //todo recheck
|
||||
dest[i] ^= key[(data->key_start + (offset - data->start) + i) % sizeof(key)];
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* decrypts subfile if needed */
|
||||
static STREAMFILE* setup_sqex_sead_streamfile(STREAMFILE* sf, off_t subfile_offset, size_t subfile_size, int encryption, size_t header_size, size_t key_start) {
|
||||
STREAMFILE* new_sf = NULL;
|
||||
|
||||
/* setup sf */
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_clamp_streamfile_f(new_sf, subfile_offset, subfile_size);
|
||||
if (encryption) {
|
||||
sqex_sead_io_data io_data = {0};
|
||||
|
||||
io_data.start = header_size;
|
||||
io_data.key_start = key_start;
|
||||
|
||||
new_sf = open_io_streamfile_f(new_sf, &io_data, sizeof(sqex_sead_io_data), sqex_sead_io_read, NULL);
|
||||
}
|
||||
|
||||
new_sf = open_fakename_streamfile_f(new_sf, NULL, "hca");
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _SQEX_SEAD_STREAMFILE_H_ */
|
||||
|
@ -1,9 +1,8 @@
|
||||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "ubi_lyn_ogg_streamfile.h"
|
||||
#include "ubi_lyn_streamfile.h"
|
||||
|
||||
static STREAMFILE* setup_lyn_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size);
|
||||
|
||||
/* LyN RIFF - from Ubisoft LyN engine games [Red Steel 2 (Wii), Adventures of Tintin (Multi), From Dust (Multi), Just Dance 3/4 (multi)] */
|
||||
VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
|
||||
@ -111,7 +110,7 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x3157: { /* Ogg (PC), interleaved 1ch */
|
||||
size_t interleave_size, stride_size;
|
||||
size_t interleave_size;
|
||||
layered_layout_data* data = NULL;
|
||||
int i;
|
||||
|
||||
@ -119,7 +118,6 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
|
||||
interleave_size = read_32bitLE(start_offset+0x04,streamFile);
|
||||
stride_size = interleave_size * channel_count;
|
||||
/* interleave is adjusted so there is no smaller last block, it seems */
|
||||
|
||||
vgmstream->coding_type = coding_OGG_VORBIS;
|
||||
@ -132,15 +130,15 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
|
||||
|
||||
/* open each layer subfile */
|
||||
for (i = 0; i < channel_count; i++) {
|
||||
STREAMFILE* temp_streamFile = NULL;
|
||||
size_t total_size = read_32bitLE(start_offset+0x08 + 0x04*i,streamFile);
|
||||
off_t layer_offset = start_offset+0x08 + 0x04*channel_count + interleave_size*i;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
size_t logical_size = read_32bitLE(start_offset+0x08 + 0x04*i,streamFile);
|
||||
off_t layer_offset = start_offset + 0x08 + 0x04*channel_count; //+ interleave_size*i;
|
||||
|
||||
temp_streamFile = setup_lyn_ogg_streamfile(streamFile, layer_offset, interleave_size, stride_size, total_size);
|
||||
if (!temp_streamFile) goto fail;
|
||||
temp_sf = setup_ubi_lyn_streamfile(streamFile, layer_offset, interleave_size, i, channel_count, logical_size);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
data->layers[i] = init_vgmstream_ogg_vorbis(temp_streamFile);
|
||||
close_streamfile(temp_streamFile);
|
||||
data->layers[i] = init_vgmstream_ogg_vorbis(temp_sf);
|
||||
close_streamfile(temp_sf);
|
||||
if (!data->layers[i]) goto fail;
|
||||
|
||||
/* could validate between layers, meh */
|
||||
@ -193,10 +191,8 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
|
||||
off_t chunk_offset;
|
||||
size_t chunk_size, seek_size;
|
||||
|
||||
if (read_32bitLE(start_offset+0x00,streamFile) != 3) /* id? */
|
||||
goto fail;
|
||||
|
||||
/* skip standard XMA header + seek table */
|
||||
/* 0x00: version? no apparent differences (0x1=Just Dance 4, 0x3=others) */
|
||||
chunk_offset = start_offset + 0x04 + 0x04;
|
||||
chunk_size = read_32bitLE(start_offset + 0x04, streamFile);
|
||||
seek_size = read_32bitLE(chunk_offset+chunk_size, streamFile);
|
||||
@ -231,7 +227,7 @@ fail:
|
||||
/* LyN RIFF in containers */
|
||||
VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
STREAMFILE *temp_sf = NULL;
|
||||
off_t subfile_offset;
|
||||
size_t subfile_size;
|
||||
|
||||
@ -261,35 +257,16 @@ VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *streamFile) {
|
||||
|
||||
subfile_size = read_32bitLE(subfile_offset+0x04,streamFile) + 0x04+0x04;
|
||||
|
||||
temp_streamFile = setup_lyn_streamfile(streamFile, subfile_offset,subfile_size);
|
||||
if (!temp_streamFile) goto fail;
|
||||
temp_sf = setup_subfile_streamfile(streamFile, subfile_offset, subfile_size, NULL);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_ubi_lyn(temp_streamFile);
|
||||
vgmstream = init_vgmstream_ubi_lyn(temp_sf);
|
||||
|
||||
close_streamfile(temp_streamFile);
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static STREAMFILE* setup_lyn_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,92 +0,0 @@
|
||||
#ifndef _LYN_OGG_STREAMFILE_H_
|
||||
#define _LYN_OGG_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
off_t start_physical_offset; /* interleaved data start, for this substream */
|
||||
size_t interleave_block_size; /* max size that can be read before encountering other substreams */
|
||||
size_t stride_size; /* step size between interleave blocks (interleave*channels) */
|
||||
size_t total_size; /* final size of the deinterleaved substream */
|
||||
} lyn_ogg_io_data;
|
||||
|
||||
|
||||
/* Handles deinterleaving of complete files, skipping portions or other substreams. */
|
||||
static size_t lyn_ogg_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, lyn_ogg_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
|
||||
while (length > 0) {
|
||||
size_t to_read;
|
||||
size_t length_available;
|
||||
off_t block_num;
|
||||
off_t intrablock_offset;
|
||||
off_t physical_offset;
|
||||
|
||||
block_num = offset / data->interleave_block_size;
|
||||
intrablock_offset = offset % data->interleave_block_size;
|
||||
physical_offset = data->start_physical_offset + block_num*data->stride_size + intrablock_offset;
|
||||
length_available = data->interleave_block_size - intrablock_offset;
|
||||
|
||||
if (length < length_available) {
|
||||
to_read = length;
|
||||
}
|
||||
else {
|
||||
to_read = length_available;
|
||||
}
|
||||
|
||||
if (to_read > 0) {
|
||||
size_t bytes_read;
|
||||
|
||||
bytes_read = read_streamfile(dest, physical_offset, to_read, streamfile);
|
||||
total_read += bytes_read;
|
||||
|
||||
if (bytes_read != to_read) {
|
||||
return total_read;
|
||||
}
|
||||
|
||||
dest += bytes_read;
|
||||
offset += bytes_read;
|
||||
length -= bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t lyn_ogg_io_size(STREAMFILE *streamfile, lyn_ogg_io_data* data) {
|
||||
return data->total_size;
|
||||
}
|
||||
|
||||
|
||||
static STREAMFILE* setup_lyn_ogg_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t interleave_block_size, size_t stride_size, size_t total_size) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
lyn_ogg_io_data io_data = {0};
|
||||
size_t io_data_size = sizeof(lyn_ogg_io_data);
|
||||
|
||||
io_data.start_physical_offset = start_offset;
|
||||
io_data.interleave_block_size = interleave_block_size;
|
||||
io_data.stride_size = stride_size;
|
||||
io_data.total_size = total_size;
|
||||
|
||||
|
||||
/* 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, lyn_ogg_io_read,lyn_ogg_io_size);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"ogg");
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
return temp_streamFile;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _LYN_OGG_STREAMFILE_H_ */
|
23
src/meta/ubi_lyn_streamfile.h
Normal file
23
src/meta/ubi_lyn_streamfile.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef _UBI_LYN_STREAMFILE_H_
|
||||
#define _UBI_LYN_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
/* Deinterleaves LYN streams */
|
||||
static STREAMFILE* setup_ubi_lyn_streamfile(STREAMFILE* sf, off_t stream_offset, size_t interleave_size, int stream_number, int stream_count, size_t logical_size) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
VGM_LOG("so=%lx, chu=%x, n=%i, c=%i, lo=%x\n", stream_offset, interleave_size, stream_number, stream_count, logical_size);
|
||||
cfg.stream_start = stream_offset;
|
||||
cfg.chunk_size = interleave_size;
|
||||
cfg.step_start = stream_number;
|
||||
cfg.step_count = stream_count;
|
||||
cfg.logical_size = logical_size;
|
||||
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
|
||||
//new_sf = open_buffer_streamfile_f(new_sf, 0);
|
||||
new_sf = open_fakename_streamfile_f(new_sf, NULL, "ogg");
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _UBI_LYN_STREAMFILE_H_ */
|
@ -2903,8 +2903,12 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Red Steel (2006)(Wii)-bank */
|
||||
if (sb->version == 0x00180006 && sb->platform == UBI_WII) {
|
||||
/* Red Steel (2006)(Wii)-bank 0x00180006 */
|
||||
/* Splinter Cell: Double Agent (2006)(Wii)-map 0x00180007 */
|
||||
/* Open Season (2006)(Wii)-map 0x00180008 */
|
||||
if ((sb->version == 0x00180006 && sb->platform == UBI_WII) ||
|
||||
(sb->version == 0x00180007 && sb->platform == UBI_WII) ||
|
||||
(sb->version == 0x00180008 && sb->platform == UBI_WII)) {
|
||||
config_sb_entry(sb, 0x68, 0x6c);
|
||||
|
||||
config_sb_audio_fs(sb, 0x28, 0x2c, 0x30);
|
||||
@ -2969,12 +2973,14 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
||||
/* My Word Coach (2007)(Wii)-bank 0x00190002 */
|
||||
/* Prince of Persia: Rival Swords (2007)(Wii)-bank 0x00190003 */
|
||||
/* Rainbow Six Vegas (2007)(PS3)-bank 0x00190005 */
|
||||
/* Surf's Up (2007)(Wii)-bank 0x00190005 */
|
||||
/* Surf's Up (2007)(PS3)-bank 0x00190005 */
|
||||
/* Surf's Up (2007)(X360)-bank 0x00190005 */
|
||||
/* Splinter Cell: Double Agent (2007)(PS3)-map 0x00190005 */
|
||||
if ((sb->version == 0x00190002 && sb->platform == UBI_X360) ||
|
||||
(sb->version == 0x00190002 && sb->platform == UBI_WII) ||
|
||||
(sb->version == 0x00190003 && sb->platform == UBI_WII) ||
|
||||
(sb->version == 0x00190005 && sb->platform == UBI_WII) ||
|
||||
(sb->version == 0x00190005 && sb->platform == UBI_PS3) ||
|
||||
(sb->version == 0x00190005 && sb->platform == UBI_X360)) {
|
||||
config_sb_entry(sb, 0x68, 0x70);
|
||||
|
135
src/meta/vid1.c
Normal file
135
src/meta/vid1.c
Normal file
@ -0,0 +1,135 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../layout/layout.h"
|
||||
|
||||
|
||||
/* VID1 - Factor 5/DivX format GC/Xbox games [Gun (GC), Tony Hawk's American Wasteland (GC), Enter The Matrix (Xbox)]*/
|
||||
VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE* sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, header_offset;
|
||||
int loop_flag = 0, channels, sample_rate;
|
||||
uint32_t codec;
|
||||
int big_endian;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*);
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .vid: video + (often) audio
|
||||
* .ogg: audio only [Gun (GC)], .logg: for plugins */
|
||||
if (!check_extensions(sf,"vid,ogg,logg"))
|
||||
goto fail;
|
||||
|
||||
/* chunked/blocked format containing video or audio frames */
|
||||
if (read_u32be(0x00, sf) == 0x56494431) { /* "VID1" BE (GC) */
|
||||
big_endian = 1;
|
||||
}
|
||||
else if (read_u32le(0x00,sf) == 0x56494431) { /* "VID1" LE (Xbox) */
|
||||
big_endian = 0;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
read_u32 = big_endian ? read_u32be : read_u32le;
|
||||
|
||||
|
||||
/* find actual header start/size in the chunks (id + size + null) */
|
||||
{
|
||||
off_t offset = read_u32(0x04, sf);
|
||||
|
||||
if (read_u32(offset + 0x00, sf) != 0x48454144) /* "HEAD" */
|
||||
goto fail;
|
||||
start_offset = offset + read_u32(offset + 0x04, sf);
|
||||
offset += 0x0c;
|
||||
|
||||
/* videos have VIDH before AUDH */
|
||||
if (read_u32(offset + 0x00, sf) == 0x56494448) { /* "VIDH" */
|
||||
offset += read_u32(offset + 0x04, sf);
|
||||
}
|
||||
|
||||
if (read_u32(offset, sf) != 0x41554448) /* "AUDH" */
|
||||
goto fail;
|
||||
offset += 0x0c;
|
||||
|
||||
header_offset = offset;
|
||||
codec = read_u32(offset + 0x00, sf);
|
||||
sample_rate = read_u32(offset + 0x04, sf);
|
||||
channels = read_u8 (offset + 0x08, sf);
|
||||
/* 0x09: flag? 0/1 */
|
||||
/* 0x0a+: varies per codec (PCM/X-IMA: nothing, DSP: coefs, Vorbis: bitrate/frame info?) + padding */
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_VID1;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->codec_endian = big_endian;
|
||||
|
||||
switch(codec) {
|
||||
case 0x50433136: /* "PC16" [Enter the Matrix (Xbox)] */
|
||||
vgmstream->coding_type = coding_PCM16_int;
|
||||
vgmstream->layout_type = layout_blocked_vid1;
|
||||
break;
|
||||
|
||||
case 0x5841504D: /* "XAPM" [Enter the Matrix (Xbox)] */
|
||||
vgmstream->coding_type = coding_XBOX_IMA;
|
||||
vgmstream->layout_type = layout_blocked_vid1;
|
||||
break;
|
||||
|
||||
case 0x4150434D: /* "APCM" [Enter the Matrix (GC)] */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_blocked_vid1;
|
||||
|
||||
dsp_read_coefs_be(vgmstream, sf, header_offset + 0x0a, 0x20);
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x56415544: { /* "VAUD" [Gun (GC)] */
|
||||
vorbis_custom_config cfg = {0};
|
||||
|
||||
vgmstream->num_samples = read_u32(header_offset + 0x20, sf);
|
||||
|
||||
vgmstream->codec_data = init_vorbis_custom(sf, header_offset + 0x24, VORBIS_VID1, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
|
||||
/* calc num_samples as playable data size varies between files/blocks */
|
||||
if (vgmstream->layout_type == layout_blocked_vid1) {
|
||||
int block_samples;
|
||||
|
||||
vgmstream->next_block_offset = start_offset;
|
||||
do {
|
||||
block_update(vgmstream->next_block_offset, vgmstream);
|
||||
if (vgmstream->current_block_samples < 0)
|
||||
break;
|
||||
|
||||
switch(vgmstream->coding_type) {
|
||||
case coding_PCM16_int: block_samples = pcm_bytes_to_samples(vgmstream->current_block_size, 1, 16); break;
|
||||
case coding_XBOX_IMA: block_samples = xbox_ima_bytes_to_samples(vgmstream->current_block_size, 1); break;
|
||||
case coding_NGC_DSP: block_samples = dsp_bytes_to_samples(vgmstream->current_block_size, 1); break;
|
||||
default: goto fail;
|
||||
}
|
||||
vgmstream->num_samples += block_samples;
|
||||
}
|
||||
while (vgmstream->next_block_offset < get_streamfile_size(sf));
|
||||
block_update(start_offset, vgmstream);
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -213,10 +213,12 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ww.codec == PCM || ww.codec == IMA || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX)
|
||||
if (ww.codec == PCM || ww.codec == IMA || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX || ww.codec == OPUS) {
|
||||
ww.truncated = 1; /* only seen those, probably all exist */
|
||||
else
|
||||
} else {
|
||||
VGM_LOG("WWISE: wrong size, maybe truncated\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -605,6 +607,13 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
/* 0x1c: stream size without OggS? */
|
||||
/* 0x20: full samples (without encoder delay) */
|
||||
|
||||
/* OPUS is VBR so this is very approximate percent, meh */
|
||||
if (ww.truncated) {
|
||||
vgmstream->num_samples = (int32_t)(vgmstream->num_samples *
|
||||
(double)(ww.file_size - start_offset) / (double)ww.data_size);
|
||||
ww.data_size = ww.file_size - start_offset;
|
||||
}
|
||||
|
||||
vgmstream->codec_data = init_ffmpeg_offset(streamFile, ww.data_offset,ww.data_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
|
431
src/meta/xnb.c
431
src/meta/xnb.c
@ -1,187 +1,244 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XNB - Microsoft XNA Game Studio 4.0 format */
|
||||
VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, xma_chunk_offset = 0;
|
||||
int loop_flag = 0, channel_count, num_samples = 0, loop_start = 0, loop_end = 0;
|
||||
int big_endian, flags, codec, sample_rate, block_align, bps;
|
||||
size_t data_size;
|
||||
char platform;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile,"xnb"))
|
||||
goto fail;
|
||||
if ((read_32bitBE(0,streamFile) & 0xFFFFFF00) != 0x584E4200) /* "XNB" */
|
||||
goto fail;
|
||||
|
||||
/* XNA Studio platforms: 'w' = Windows, 'm' = Windows Phone 7, 'x' = X360
|
||||
* MonoGame extensions: 'i' = iOS, 'a' = Android, 'X' = MacOSX, 'P' = PS4, 'S' = Switch, etc */
|
||||
platform = read_8bit(0x03,streamFile);
|
||||
big_endian = (platform == 'x');
|
||||
|
||||
if (read_8bit(0x04,streamFile) != 0x04 && /* XNA 3.0? found on Scare Me (XBLIG), no notable diffs */
|
||||
read_8bit(0x04,streamFile) != 0x05) /* XNA 4.0 version */
|
||||
goto fail;
|
||||
|
||||
flags = read_8bit(0x05,streamFile);
|
||||
//if (flags & 0x01) goto fail; /* "HiDef profile" content (no actual difference) */
|
||||
if (flags & 0x80) goto fail; /* compressed with LZX/XMemCompress (at 0x0a is decompressed size) */
|
||||
if (flags & 0x40) goto fail; /* compressed with LZ4, MonoGame extension [ex. Square Heroes (PS4)] */
|
||||
|
||||
/* full size */
|
||||
if (read_32bitLE(0x06,streamFile) != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
|
||||
/* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */
|
||||
{
|
||||
char reader_name[255+1];
|
||||
off_t current_offset = 0x0a;
|
||||
size_t reader_string_len;
|
||||
uint32_t fmt_chunk_size;
|
||||
const char * type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */
|
||||
//const char * type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* just references a companion .wma */
|
||||
|
||||
/* type reader count, accept only one for now */
|
||||
if (read_8bit(current_offset++, streamFile) != 1)
|
||||
goto fail;
|
||||
|
||||
reader_string_len = read_8bit(current_offset++, streamFile); /* doesn't count null */
|
||||
if (reader_string_len > 255) goto fail;
|
||||
|
||||
/* check SoundEffect type string */
|
||||
if (read_string(reader_name,reader_string_len+1,current_offset,streamFile) != reader_string_len)
|
||||
goto fail;
|
||||
if ( strcmp(reader_name, type_sound) != 0 )
|
||||
goto fail;
|
||||
current_offset += reader_string_len + 1;
|
||||
current_offset += 0x04; /* reader version */
|
||||
|
||||
/* shared resource count */
|
||||
if (read_8bit(current_offset++, streamFile) != 1)
|
||||
goto fail;
|
||||
|
||||
/* shared resource: partial "fmt" chunk */
|
||||
fmt_chunk_size = read_32bitLE(current_offset, streamFile);
|
||||
current_offset += 0x04;
|
||||
|
||||
{
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE;
|
||||
|
||||
codec = (uint16_t)read_16bit(current_offset+0x00, streamFile);
|
||||
channel_count = read_16bit(current_offset+0x02, streamFile);
|
||||
sample_rate = read_32bit(current_offset+0x04, streamFile);
|
||||
/* 0x08: byte rate */
|
||||
block_align = read_16bit(current_offset+0x0c, streamFile);
|
||||
bps = read_16bit(current_offset+0x0e, streamFile);
|
||||
|
||||
if (codec == 0x0002) {
|
||||
if (!msadpcm_check_coefs(streamFile, current_offset + 0x14))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (codec == 0x0166) {
|
||||
xma2_parse_fmt_chunk_extra(streamFile, current_offset, &loop_flag, &num_samples, &loop_start, &loop_end, big_endian);
|
||||
xma_chunk_offset = current_offset;
|
||||
}
|
||||
|
||||
if (codec == 0xFFFF) {
|
||||
if (platform != 'S') goto fail;
|
||||
sample_rate = read_32bit(current_offset+fmt_chunk_size+0x04+0x08, streamFile);
|
||||
}
|
||||
}
|
||||
|
||||
current_offset += fmt_chunk_size;
|
||||
|
||||
data_size = read_32bitLE(current_offset, streamFile);
|
||||
current_offset += 0x04;
|
||||
|
||||
start_offset = current_offset;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->meta_type = meta_XNB;
|
||||
|
||||
switch (codec) {
|
||||
case 0x01: /* Dragon's Blade (Android) */
|
||||
/* null in Metagalactic Blitz (PC) */
|
||||
if (!block_align)
|
||||
block_align = (bps == 8 ? 0x01 : 0x02) * channel_count;
|
||||
|
||||
vgmstream->coding_type = bps == 8 ? coding_PCM8_U : coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_align / channel_count;
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bps);
|
||||
break;
|
||||
|
||||
case 0x02: /* White Noise Online (PC) */
|
||||
if (!block_align) goto fail;
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = block_align;
|
||||
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, block_align, channel_count);
|
||||
break;
|
||||
|
||||
case 0x11:
|
||||
if (!block_align) goto fail;
|
||||
vgmstream->coding_type = coding_MS_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = block_align;
|
||||
vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, block_align, channel_count);
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x166: { /* Terraria (X360) */
|
||||
uint8_t buf[0x100];
|
||||
int32_t bytes, block_size, block_count;
|
||||
|
||||
block_size = 0x10000; /* guessed */
|
||||
block_count = data_size / block_size + (data_size % block_size ? 1 : 0);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf,0x100, num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, xma_chunk_offset, 1,1);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case 0xFFFF: /* Eagle Island (Switch) */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = data_size / channel_count;
|
||||
vgmstream->num_samples = read_32bitLE(start_offset + 0x00, streamFile);
|
||||
//vgmstream->num_samples = dsp_bytes_to_samples(data_size - 0x60*channel_count, channel_count);
|
||||
|
||||
dsp_read_coefs(vgmstream,streamFile,start_offset + 0x1c,vgmstream->interleave_block_size,big_endian);
|
||||
dsp_read_hist (vgmstream,streamFile,start_offset + 0x3c,vgmstream->interleave_block_size,big_endian);
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("XNB: unknown codec 0x%x\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "xnb_streamfile.h"
|
||||
|
||||
|
||||
/* XNB - Microsoft XNA Game Studio 4.0 format */
|
||||
VGMSTREAM* init_vgmstream_xnb(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sf_h = NULL;
|
||||
off_t start_offset, offset, xma_chunk_offset = 0;
|
||||
int loop_flag = 0, channel_count, num_samples = 0, loop_start = 0, loop_end = 0;
|
||||
int big_endian, flags, codec, sample_rate, block_align, bps;
|
||||
size_t data_size;
|
||||
char platform;
|
||||
int is_ogg = 0, is_at9 = 0;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"xnb"))
|
||||
goto fail;
|
||||
if ((read_32bitBE(0x00, sf) & 0xFFFFFF00) != 0x584E4200) /* "XNB" */
|
||||
goto fail;
|
||||
|
||||
/* XNA Studio platforms: 'w' = Windows, 'm' = Windows Phone 7, 'x' = X360
|
||||
* MonoGame extensions: 'i' = iOS, 'a' = Android, 'X' = MacOSX, 'P' = PS4, 'S' = Switch, etc */
|
||||
platform = read_u8(0x03, sf);
|
||||
big_endian = (platform == 'x');
|
||||
|
||||
if (read_u8(0x04,sf) != 0x04 && /* XNA 3.0? found on Scare Me (XBLIG), no notable diffs */
|
||||
read_u8(0x04,sf) != 0x05) /* XNA 4.0 version */
|
||||
goto fail;
|
||||
|
||||
flags = read_u8(0x05, sf);
|
||||
//if (flags & 0x01) goto fail; /* "HiDef profile" content (no actual difference) */
|
||||
|
||||
/* full size */
|
||||
if (read_32bitLE(0x06, sf) != get_streamfile_size(sf)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* handle XNB compression as a normal non-compressed stream, normally for graphics
|
||||
* and other formats, but confused devs use it with already-compressed audio like Ogg/ATRAC9 */
|
||||
if ((flags & 0x80) || /* LZX/XMemCompress (not used with audio?) */
|
||||
(flags & 0x40)) { /* LZ4 (MonoGame extension) [Square Heroes (PS4), Little Savior (PC)] */
|
||||
size_t compression_start = 0x0e;
|
||||
size_t compression_size = read_u32le(0x0a, sf);
|
||||
sf_h = setup_xnb_streamfile(sf, flags, compression_start, compression_size);
|
||||
if (!sf_h) goto fail;
|
||||
|
||||
//dump_streamfile(sf_h, 0);
|
||||
offset = 0x0e; /* refers to decompressed stream */
|
||||
}
|
||||
else {
|
||||
sf_h = sf;
|
||||
offset = 0x0a;
|
||||
}
|
||||
|
||||
|
||||
/* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */
|
||||
{
|
||||
char reader_name[255+1];
|
||||
size_t reader_string_len;
|
||||
uint32_t fmt_chunk_size;
|
||||
const char* type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */
|
||||
const char* type_ogg = "SoundEffectFromOggReader"; /* has extra text info after base part */
|
||||
//const char* type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* references a companion .wma */
|
||||
|
||||
/* type reader count, accept only one for now */
|
||||
if (read_u8(offset++, sf_h) != 1)
|
||||
goto fail;
|
||||
|
||||
reader_string_len = read_u8(offset++, sf_h); /* doesn't count null */
|
||||
if (reader_string_len > 255) goto fail;
|
||||
|
||||
/* check SoundEffect type string */
|
||||
if (read_string(reader_name, reader_string_len+1, offset, sf_h) != reader_string_len)
|
||||
goto fail;
|
||||
if (strcmp(reader_name, type_sound) != 0) {
|
||||
is_ogg = strncmp(reader_name, type_ogg, strlen(type_ogg)) == 0;
|
||||
if (!is_ogg)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offset += reader_string_len + 1;
|
||||
offset += 0x04; /* reader version, 0 */
|
||||
|
||||
/* shared resource count */
|
||||
if (read_u8(offset++, sf_h) != 1)
|
||||
goto fail;
|
||||
|
||||
/* shared resource: partial "fmt" chunk */
|
||||
fmt_chunk_size = read_32bitLE(offset, sf_h);
|
||||
offset += 0x04;
|
||||
|
||||
{
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE;
|
||||
|
||||
codec = (uint16_t)read_16bit(offset+0x00, sf_h);
|
||||
channel_count = read_16bit(offset+0x02, sf_h);
|
||||
sample_rate = read_32bit(offset+0x04, sf_h);
|
||||
/* 0x08: byte rate */
|
||||
block_align = read_16bit(offset+0x0c, sf_h);
|
||||
bps = read_16bit(offset+0x0e, sf_h);
|
||||
|
||||
if (codec == 0x0002) {
|
||||
if (!msadpcm_check_coefs(sf_h, offset + 0x14))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (codec == 0x0166) {
|
||||
xma2_parse_fmt_chunk_extra(sf_h, offset, &loop_flag, &num_samples, &loop_start, &loop_end, big_endian);
|
||||
xma_chunk_offset = offset;
|
||||
}
|
||||
|
||||
if (codec == 0xFFFF) {
|
||||
if (platform != 'S') goto fail;
|
||||
sample_rate = read_32bit(offset+fmt_chunk_size+0x04+0x08, sf_h);
|
||||
}
|
||||
|
||||
/* mini-fmt has AT9 stuff then a regular RIFF [Square Heroes (PS4)] */
|
||||
if (codec == 0xFFFE) {
|
||||
is_at9 = 1;
|
||||
}
|
||||
|
||||
/* regular (with loop tags) Ogg poses as PCM [Little Savior (PC)] */
|
||||
}
|
||||
|
||||
offset += fmt_chunk_size;
|
||||
|
||||
data_size = read_32bitLE(offset, sf_h);
|
||||
offset += 0x04;
|
||||
|
||||
start_offset = offset;
|
||||
}
|
||||
|
||||
/* container handling */
|
||||
if (is_ogg || is_at9) {
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
const char* fake_ext = is_ogg ? "ogg" : "at9";
|
||||
|
||||
/* after data_size is loop start + loop length and offset? (same as loop tags), 0 if not enabled */
|
||||
|
||||
temp_sf = setup_subfile_streamfile(sf_h, start_offset, data_size, fake_ext);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
if (is_ogg) {
|
||||
vgmstream = init_vgmstream_ogg_vorbis(temp_sf);
|
||||
} else {
|
||||
vgmstream = init_vgmstream_riff(temp_sf);
|
||||
}
|
||||
close_streamfile(temp_sf);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_XNB;
|
||||
if (sf_h != sf) close_streamfile(sf_h);
|
||||
return vgmstream;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->meta_type = meta_XNB;
|
||||
|
||||
switch (codec) {
|
||||
case 0x01: /* Dragon's Blade (Android) */
|
||||
if (!block_align) /* null in Metagalactic Blitz (PC) */
|
||||
block_align = (bps == 8 ? 0x01 : 0x02) * channel_count;
|
||||
|
||||
vgmstream->coding_type = bps == 8 ? coding_PCM8_U : coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_align / channel_count;
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bps);
|
||||
break;
|
||||
|
||||
case 0x02: /* White Noise Online (PC) */
|
||||
if (!block_align) goto fail;
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = block_align;
|
||||
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, block_align, channel_count);
|
||||
break;
|
||||
|
||||
case 0x11:
|
||||
if (!block_align) goto fail;
|
||||
vgmstream->coding_type = coding_MS_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = block_align;
|
||||
vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, block_align, channel_count);
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x166: { /* Terraria (X360) */
|
||||
uint8_t buf[0x100];
|
||||
int32_t bytes, block_size, block_count;
|
||||
|
||||
block_size = 0x10000; /* guessed */
|
||||
block_count = data_size / block_size + (data_size % block_size ? 1 : 0);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf,0x100, num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sf_h, buf,bytes, start_offset,data_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, sf_h, start_offset,data_size, xma_chunk_offset, 1,1);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case 0xFFFF: /* Eagle Island (Switch) */
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = data_size / channel_count;
|
||||
vgmstream->num_samples = read_32bitLE(start_offset + 0x00, sf_h);
|
||||
//vgmstream->num_samples = dsp_bytes_to_samples(data_size - 0x60*channel_count, channel_count);
|
||||
|
||||
dsp_read_coefs(vgmstream, sf_h, start_offset + 0x1c, vgmstream->interleave_block_size, big_endian);
|
||||
dsp_read_hist (vgmstream, sf_h, start_offset + 0x3c, vgmstream->interleave_block_size, big_endian);
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("XNB: unknown codec 0x%x\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf_h, start_offset))
|
||||
goto fail;
|
||||
|
||||
if (sf_h != sf) close_streamfile(sf_h);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (sf_h != sf) close_streamfile(sf_h);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
266
src/meta/xnb_lz4mg.h
Normal file
266
src/meta/xnb_lz4mg.h
Normal file
@ -0,0 +1,266 @@
|
||||
#ifndef _XNB_LZ4MG_H_
|
||||
#define _XNB_LZ4MG_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Decompresses LZ4 from MonoGame. The original C lib has a lot of modes and configs, but
|
||||
* MonoGame only uses the core 'block' part, which is a fairly simple LZ77 (has one command
|
||||
* to copy literal and window values, with variable copy lengths).
|
||||
*
|
||||
* This is a basic re-implementation (not tuned for performance) for practice/test purposes,
|
||||
* that handles streaming decompression as a state machine since we can run out of src or dst
|
||||
* bytes anytime and LZ4 allows any copy length, with copy window as a circular buffer. Not
|
||||
* sure what's the best/standard way to do it though. Info:
|
||||
* - https://github.com/lz4/lz4
|
||||
* - https://github.com/MonoGame/MonoGame/blob/develop/MonoGame.Framework/Utilities/Lz4Stream/Lz4DecoderStream.cs
|
||||
*/
|
||||
|
||||
#define LZ4MG_OK 0
|
||||
#define LZ4MG_ERROR -1
|
||||
#define LZ4MG_WINDOW_SIZE (1 << 16)
|
||||
#define LZ4MG_WINDOW_BYTES 2
|
||||
#define LZ4MG_MIN_MATCH_LEN 4
|
||||
#define LZ4MG_VARLEN_MARK 15
|
||||
#define LZ4MG_VARLEN_CONTINUE 255
|
||||
|
||||
|
||||
typedef enum {
|
||||
READ_TOKEN,
|
||||
READ_LITERAL,
|
||||
COPY_LITERAL,
|
||||
READ_OFFSET,
|
||||
READ_MATCH,
|
||||
SET_MATCH,
|
||||
COPY_MATCH
|
||||
} lz4mg_state_t;
|
||||
|
||||
typedef struct {
|
||||
lz4mg_state_t state;
|
||||
|
||||
uint8_t token;
|
||||
int literal_len;
|
||||
int offset_cur;
|
||||
int offset_pos;
|
||||
int match_len;
|
||||
int match_pos;
|
||||
|
||||
int window_pos;
|
||||
uint8_t window[LZ4MG_WINDOW_SIZE];
|
||||
} lz4mg_context_t;
|
||||
|
||||
typedef struct {
|
||||
lz4mg_context_t ctx;
|
||||
|
||||
uint8_t *next_out; /* next bytes to write (reassign when avail is 0) */
|
||||
int avail_out; /* bytes available at next_out */
|
||||
int total_out; /* written bytes, for reference (set to 0 per call if needed) */
|
||||
|
||||
const uint8_t *next_in; /* next bytes to read (reassign when avail is 0) */
|
||||
int avail_in; /* bytes available at next_in */
|
||||
int total_in; /* read bytes, for reference (set to 0 per call if needed) */
|
||||
} lz4mg_stream_t;
|
||||
|
||||
static void lz4mg_reset(lz4mg_stream_t* strm) {
|
||||
memset(strm, 0, sizeof(lz4mg_stream_t));
|
||||
}
|
||||
|
||||
/* Decompress src into dst, returning a code and number of bytes used. Caller must handle
|
||||
* stop (when no more input data or all data has been decompressed) as LZ4 has no end markers. */
|
||||
static int lz4mg_decompress(lz4mg_stream_t* strm) {
|
||||
lz4mg_context_t* ctx = &strm->ctx;
|
||||
uint8_t* dst = strm->next_out;
|
||||
const uint8_t* src = strm->next_in;
|
||||
int dst_size = strm->avail_out;
|
||||
int src_size = strm->avail_in;
|
||||
int dst_pos = 0;
|
||||
int src_pos = 0;
|
||||
uint8_t next_len, next_val;
|
||||
|
||||
|
||||
while (1) {
|
||||
/* mostly linear state machine, but it may break anytime when reaching dst or src
|
||||
* end, and resume from same state in next call */
|
||||
switch(ctx->state) {
|
||||
|
||||
case READ_TOKEN:
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->token = src[src_pos++];
|
||||
|
||||
ctx->literal_len = (ctx->token >> 4) & 0xF;
|
||||
if (ctx->literal_len == LZ4MG_VARLEN_MARK)
|
||||
ctx->state = READ_LITERAL;
|
||||
else
|
||||
ctx->state = COPY_LITERAL;
|
||||
break;
|
||||
|
||||
case READ_LITERAL:
|
||||
do {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
next_len = src[src_pos++];
|
||||
ctx->literal_len += next_len;
|
||||
} while (next_len == LZ4MG_VARLEN_CONTINUE);
|
||||
|
||||
ctx->state = COPY_LITERAL;
|
||||
break;
|
||||
|
||||
case COPY_LITERAL:
|
||||
while (ctx->literal_len > 0) { /* may be 0 */
|
||||
if (src_pos >= src_size || dst_pos >= dst_size)
|
||||
goto buffer_end;
|
||||
next_val = src[src_pos++];
|
||||
|
||||
dst[dst_pos++] = next_val;
|
||||
|
||||
ctx->window[ctx->window_pos++] = next_val;
|
||||
if (ctx->window_pos == LZ4MG_WINDOW_SIZE)
|
||||
ctx->window_pos = 0;
|
||||
|
||||
ctx->literal_len--;
|
||||
};
|
||||
|
||||
/* LZ4 is designed to reach EOF with a literal in this state with some empty values */
|
||||
|
||||
ctx->offset_cur = 0;
|
||||
ctx->offset_pos = 0;
|
||||
ctx->state = READ_OFFSET;
|
||||
break;
|
||||
|
||||
case READ_OFFSET:
|
||||
do {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
ctx->offset_pos |= (src[src_pos++] << ctx->offset_cur*8);
|
||||
ctx->offset_cur++;
|
||||
} while (ctx->offset_cur < LZ4MG_WINDOW_BYTES);
|
||||
|
||||
ctx->match_len = (ctx->token & 0xF);
|
||||
if (ctx->match_len == LZ4MG_VARLEN_MARK)
|
||||
ctx->state = READ_MATCH;
|
||||
else
|
||||
ctx->state = SET_MATCH;
|
||||
break;
|
||||
|
||||
case READ_MATCH:
|
||||
do {
|
||||
if (src_pos >= src_size)
|
||||
goto buffer_end;
|
||||
next_len = src[src_pos++];
|
||||
ctx->match_len += next_len;
|
||||
} while (next_len == LZ4MG_VARLEN_CONTINUE);
|
||||
|
||||
ctx->state = SET_MATCH;
|
||||
break;
|
||||
|
||||
case SET_MATCH:
|
||||
ctx->match_len += LZ4MG_MIN_MATCH_LEN;
|
||||
|
||||
ctx->match_pos = ctx->window_pos - ctx->offset_pos;
|
||||
if (ctx->match_pos < 0) /* circular buffer so negative is from window end */
|
||||
ctx->match_pos = LZ4MG_WINDOW_SIZE + ctx->match_pos;
|
||||
|
||||
ctx->state = COPY_MATCH;
|
||||
break;
|
||||
|
||||
case COPY_MATCH:
|
||||
while (ctx->match_len > 0) {
|
||||
if (dst_pos >= dst_size)
|
||||
goto buffer_end;
|
||||
|
||||
next_val = ctx->window[ctx->match_pos++];
|
||||
if (ctx->match_pos == LZ4MG_WINDOW_SIZE)
|
||||
ctx->match_pos = 0;
|
||||
|
||||
dst[dst_pos++] = next_val;
|
||||
|
||||
ctx->window[ctx->window_pos++] = next_val;
|
||||
if (ctx->window_pos == LZ4MG_WINDOW_SIZE)
|
||||
ctx->window_pos = 0;
|
||||
|
||||
ctx->match_len--;
|
||||
};
|
||||
|
||||
ctx->state = READ_TOKEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
buffer_end:
|
||||
strm->next_out += dst_pos;
|
||||
strm->next_in += src_pos;
|
||||
strm->avail_out -= dst_pos;
|
||||
strm->avail_in -= src_pos;
|
||||
strm->total_out += dst_pos;
|
||||
strm->total_in += src_pos;
|
||||
|
||||
return LZ4MG_OK;
|
||||
fail:
|
||||
return LZ4MG_ERROR;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* non-streamed form for reference, assumes buffers are big enough */
|
||||
static void decompress_lz4mg(uint8_t* dst, size_t dst_size, const uint8_t* src, size_t src_size) {
|
||||
size_t src_pos = 0;
|
||||
size_t dst_pos = 0;
|
||||
uint8_t token;
|
||||
int literal_len, match_len, next_len;
|
||||
int match_pos, match_offset;
|
||||
int i;
|
||||
|
||||
while (src_pos < src_size && dst_pos < dst_size) {
|
||||
|
||||
token = src[src_pos++];
|
||||
if (src_pos > src_size)
|
||||
break;
|
||||
|
||||
/* handle literals */
|
||||
literal_len = token >> 4;
|
||||
if (literal_len == 15) {
|
||||
do {
|
||||
next_len = src[src_pos++];
|
||||
literal_len += next_len;
|
||||
if (src_pos > src_size)
|
||||
break;
|
||||
} while (next_len == 255);
|
||||
}
|
||||
|
||||
for (i = 0; i < literal_len; i++) {
|
||||
dst[dst_pos++] = src[src_pos++];
|
||||
}
|
||||
|
||||
/* can happen at EOF */
|
||||
if (dst_pos >= dst_size)
|
||||
break;
|
||||
|
||||
/* handle window matches */
|
||||
match_offset = src[src_pos++];
|
||||
match_offset |= (src[src_pos++] << 8);
|
||||
|
||||
match_len = (token & 0xF);
|
||||
if (match_len == 15) {
|
||||
do {
|
||||
next_len = src[src_pos++];
|
||||
match_len += next_len;
|
||||
if (src_pos > src_size)
|
||||
break;
|
||||
} while (next_len == 255);
|
||||
}
|
||||
match_len += 4; /* min len */
|
||||
|
||||
match_pos = dst_pos - match_offset;
|
||||
for(i = 0; i < match_len; i++) {
|
||||
dst[dst_pos++] = dst[match_pos++]; /* note RLE with short offsets */
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _XNB_LZ4MG_H_ */
|
300
src/meta/xnb_streamfile.h
Normal file
300
src/meta/xnb_streamfile.h
Normal file
@ -0,0 +1,300 @@
|
||||
#ifndef _XNB_STREAMFILE_H_
|
||||
#define _XNB_STREAMFILE_H_
|
||||
|
||||
//#define XNB_ENABLE_LZX 1
|
||||
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
/* lib from https://github.com/sumatrapdfreader/chmlib
|
||||
* which is a cleaned-up version of https://github.com/jedwing/CHMLib */
|
||||
#include "lzx.h"
|
||||
|
||||
#define LZX_XNB_WINDOW_BITS 16
|
||||
#endif
|
||||
|
||||
#include "xnb_lz4mg.h"
|
||||
|
||||
|
||||
#define XNB_TYPE_LZX 1
|
||||
#define XNB_TYPE_LZ4 2
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
int type;
|
||||
off_t compression_start;
|
||||
size_t compression_size;
|
||||
|
||||
/* state */
|
||||
off_t logical_offset; /* offset that corresponds to physical_offset */
|
||||
off_t physical_offset; /* actual file offset */
|
||||
|
||||
size_t block_size; /* current block size */
|
||||
size_t skip_size; /* size to skip from a block start to reach data start */
|
||||
size_t data_size; /* logical size of the block */
|
||||
|
||||
size_t logical_size;
|
||||
|
||||
/* decompression state (dst size min for LZX) */
|
||||
uint8_t dst[0x10000];
|
||||
uint8_t src[0x10000];
|
||||
|
||||
lz4mg_stream_t lz4s;
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
struct lzx_state* lzxs;
|
||||
#endif
|
||||
} xnb_io_data;
|
||||
|
||||
static int xnb_io_init(STREAMFILE* sf, xnb_io_data* data) {
|
||||
/* When a new IO SF is opened, the data struct is malloc'd and cloned. This works
|
||||
* well enough in other cases, but b/c the decompression context works with buf ptrs
|
||||
* the clone will point to the original struct bufs, so we need to force
|
||||
* reset on new open so that new bufs of the clone are used. */
|
||||
data->logical_offset = -1;
|
||||
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
if (data->type == XNB_TYPE_LZX) {
|
||||
data->lzxs = lzx_init(LZX_XNB_WINDOW_BITS);
|
||||
if (!data->lzxs)
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xnb_io_close(STREAMFILE* sf, xnb_io_data* data) {
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
if (data->type == XNB_TYPE_LZX) {
|
||||
lzx_teardown(data->lzxs);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
/* Decompresses LZX used in XNB. Has 16b window and headered blocks (with input size and
|
||||
* optionally output size) and standard LZX inside, probably what's known as XMemCompress.
|
||||
* Info: https://github.com/MonoGame/MonoGame/blob/develop/MonoGame.Framework/Utilities/LzxStream/LzxDecoderStream.cs */
|
||||
static int decompress_lzx_block(STREAMFILE* sf, xnb_io_data* data) {
|
||||
int src_size, dst_size, ret;
|
||||
uint8_t hi, lo;
|
||||
off_t head_size;
|
||||
off_t offset = data->physical_offset;
|
||||
|
||||
|
||||
hi = read_u8(offset + 0x00, sf);
|
||||
if (hi == 0xFF) {
|
||||
hi = read_u8(offset + 0x01, sf);
|
||||
lo = read_u8(offset + 0x02, sf);
|
||||
dst_size = (hi << 8) | lo;
|
||||
|
||||
hi = read_u8(offset + 0x03, sf);
|
||||
lo = read_u8(offset + 0x04, sf);
|
||||
src_size = (hi << 8) | lo;
|
||||
|
||||
head_size = 0x05;
|
||||
}
|
||||
else {
|
||||
dst_size = 0x8000; /* default */
|
||||
|
||||
lo = read_u8(offset + 0x01, sf);
|
||||
src_size = (hi << 8) | lo;
|
||||
|
||||
head_size = 0x02;
|
||||
}
|
||||
|
||||
if (src_size == 0 || dst_size == 0) {
|
||||
VGM_LOG("LZX: EOF\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
read_streamfile(data->src, offset + head_size, src_size, sf);
|
||||
|
||||
ret = lzx_decompress(data->lzxs, data->src, data->dst, src_size, dst_size);
|
||||
if (ret != DECR_OK) {
|
||||
VGM_LOG("LZX: decompression error %i\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->data_size = dst_size;
|
||||
data->block_size = head_size + src_size;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int decompress_lz4_block(STREAMFILE* sf, xnb_io_data* data) {
|
||||
int ret;
|
||||
|
||||
if (data->lz4s.avail_in == 0) {
|
||||
data->lz4s.next_in = data->src;
|
||||
data->lz4s.avail_in = read_streamfile(data->src, data->physical_offset, sizeof(data->src), sf);
|
||||
|
||||
/* shouldn't happen since we have decomp size */
|
||||
if (data->lz4s.avail_in <= 0) {
|
||||
VGM_LOG("XNB: EOF reached\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->block_size = data->lz4s.avail_in; /* physical data (smaller) */
|
||||
}
|
||||
else {
|
||||
data->block_size = 0; /* physical data doesn't move until new read */
|
||||
}
|
||||
|
||||
data->lz4s.total_out = 0;
|
||||
data->lz4s.avail_out = sizeof(data->dst);
|
||||
data->lz4s.next_out = data->dst;
|
||||
|
||||
ret = lz4mg_decompress(&data->lz4s);
|
||||
|
||||
data->data_size = data->lz4s.total_out; /* logical data (bigger) */
|
||||
|
||||
if (ret != LZ4MG_OK) {
|
||||
VGM_LOG("XNB: LZ4 error %i\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t xnb_io_read(STREAMFILE* sf, uint8_t *dest, off_t offset, size_t length, xnb_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
|
||||
/* reset */
|
||||
if (data->logical_offset < 0 || offset < data->logical_offset) {
|
||||
data->physical_offset = 0x00;
|
||||
data->logical_offset = 0x00;
|
||||
data->block_size = 0;
|
||||
data->data_size = 0;
|
||||
data->skip_size = 0;
|
||||
|
||||
switch(data->type) {
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
case XNB_TYPE_LZX: lzx_reset(data->lzxs); break;
|
||||
#endif
|
||||
case XNB_TYPE_LZ4: lz4mg_reset(&data->lz4s); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
/* read blocks, one at a time */
|
||||
while (length > 0) {
|
||||
|
||||
/* ignore EOF */
|
||||
if (offset < 0 || data->logical_offset >= data->logical_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* process new block */
|
||||
if (data->data_size <= 0) {
|
||||
|
||||
if (data->physical_offset < data->compression_start) {
|
||||
/* copy data */
|
||||
int to_read = data->compression_start - data->physical_offset;
|
||||
|
||||
data->data_size = read_streamfile(data->dst, data->physical_offset, to_read, sf);
|
||||
data->block_size = data->data_size;
|
||||
}
|
||||
else {
|
||||
/* decompress data */
|
||||
int ok = 0;
|
||||
|
||||
switch(data->type) {
|
||||
#ifdef XNB_ENABLE_LZX
|
||||
case XNB_TYPE_LZX: ok = decompress_lzx_block(sf, data); break;
|
||||
#endif
|
||||
case XNB_TYPE_LZ4: ok = decompress_lz4_block(sf, data); break;
|
||||
default: break;
|
||||
}
|
||||
if (!ok) {
|
||||
VGM_LOG("XNB: decompression error\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* move to next block */
|
||||
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
|
||||
data->physical_offset += data->block_size;
|
||||
data->logical_offset += data->data_size;
|
||||
data->data_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read/copy block data */
|
||||
{
|
||||
size_t bytes_consumed, bytes_done, to_read;
|
||||
|
||||
bytes_consumed = offset - data->logical_offset;
|
||||
to_read = data->data_size - bytes_consumed;
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
memcpy(dest, data->dst + data->skip_size + bytes_consumed, to_read);
|
||||
bytes_done = to_read;
|
||||
|
||||
total_read += bytes_done;
|
||||
dest += bytes_done;
|
||||
offset += bytes_done;
|
||||
length -= bytes_done;
|
||||
|
||||
if (bytes_done != to_read || bytes_done == 0) {
|
||||
break; /* error/EOF */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
|
||||
static size_t xnb_io_size(STREAMFILE *streamfile, xnb_io_data* data) {
|
||||
uint8_t buf[1];
|
||||
|
||||
if (data->logical_size)
|
||||
return data->logical_size;
|
||||
|
||||
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
|
||||
xnb_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
|
||||
data->logical_size = data->logical_offset;
|
||||
|
||||
return data->logical_size;
|
||||
}
|
||||
|
||||
|
||||
/* Decompresses XNB streams */
|
||||
static STREAMFILE* setup_xnb_streamfile(STREAMFILE* sf, int flags, off_t compression_start, size_t compression_size) {
|
||||
STREAMFILE *new_sf = NULL;
|
||||
xnb_io_data io_data = {0};
|
||||
|
||||
if (flags & 0x80)
|
||||
io_data.type = XNB_TYPE_LZX;
|
||||
else if (flags & 0x40)
|
||||
io_data.type = XNB_TYPE_LZ4;
|
||||
else
|
||||
goto fail;
|
||||
io_data.compression_start = compression_start;
|
||||
io_data.compression_size = compression_size;
|
||||
io_data.physical_offset = 0x00;
|
||||
io_data.logical_size = compression_start + compression_size;
|
||||
io_data.logical_offset = -1; /* force reset */
|
||||
|
||||
#ifndef XNB_ENABLE_LZX
|
||||
/* no known audio use it (otherwise works if enabled and included lzx.c/lzx.h) */
|
||||
if (io_data.type == XNB_TYPE_LZX)
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
/* setup subfile */
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_streamfile_ex_f(new_sf, &io_data, sizeof(xnb_io_data), xnb_io_read, xnb_io_size, xnb_io_init, xnb_io_close);
|
||||
//new_sf = open_buffer_streamfile_f(new_sf, 0); /* useful? we already have a decompression buffer */
|
||||
return new_sf;
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _XNB_STREAMFILE_H_ */
|
151
src/meta/xpcm.c
151
src/meta/xpcm.c
@ -1,69 +1,82 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XPCM - from Circus games [Eternal Fantasy (PC), D.C. White Season (PC)] */
|
||||
VGMSTREAM * init_vgmstream_xpcm(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t decompressed_size;
|
||||
int loop_flag, channel_count, codec, subcodec, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "pcm"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x5850434D) /* "XPCM" "*/
|
||||
goto fail;
|
||||
|
||||
decompressed_size = read_32bitLE(0x04,streamFile); /* (data_size for PCM) */
|
||||
codec = read_8bit(0x08, streamFile);
|
||||
subcodec = read_8bit(0x09, streamFile);
|
||||
/* 0x0a: always null */
|
||||
/* 0x0c: always 0x01 (PCM codec) */
|
||||
channel_count = read_16bitLE(0x0e,streamFile);
|
||||
sample_rate = read_32bitLE(0x10,streamFile);
|
||||
/* 0x14: average bitrate */
|
||||
/* 0x18: block size */
|
||||
/* 0x1a: output bits (16) */
|
||||
start_offset = 0x1c; /* compressed size in codec 0x01/03 */
|
||||
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_XPCM;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = decompressed_size / sizeof(int16_t) / channel_count;
|
||||
|
||||
switch(codec) {
|
||||
case 0x00:
|
||||
if (subcodec != 0) goto fail;
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
break;
|
||||
case 0x02:
|
||||
if (subcodec != 0) goto fail;
|
||||
vgmstream->coding_type = coding_CIRCUS_ADPCM;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x01;
|
||||
break;
|
||||
|
||||
case 0x01: /* LZSS + VQ */
|
||||
case 0x03: /* unknown */
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XPCM - from Circus games [Eternal Fantasy (PC), D.C. White Season (PC)] */
|
||||
VGMSTREAM * init_vgmstream_xpcm(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
size_t decompressed_size;
|
||||
int loop_flag, channel_count, codec, flags, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "pcm"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x5850434D) /* "XPCM" "*/
|
||||
goto fail;
|
||||
|
||||
decompressed_size = read_32bitLE(0x04,streamFile); /* (data_size for PCM) */
|
||||
codec = read_8bit(0x08, streamFile);
|
||||
flags = read_8bit(0x09, streamFile);
|
||||
/* 0x0a: always null */
|
||||
/* 0x0c: always 0x01 (PCM codec) */
|
||||
channel_count = read_16bitLE(0x0e,streamFile);
|
||||
sample_rate = read_32bitLE(0x10,streamFile);
|
||||
/* 0x14: average bitrate */
|
||||
/* 0x18: block size */
|
||||
/* 0x1a: output bits (16) */
|
||||
start_offset = 0x1c; /* compressed size in codec 0x01/03 */
|
||||
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_XPCM;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = decompressed_size / sizeof(int16_t) / channel_count;
|
||||
|
||||
switch(codec) {
|
||||
case 0x00:
|
||||
if (flags != 0) goto fail;
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
if (flags != 0) goto fail;
|
||||
vgmstream->coding_type = coding_CIRCUS_ADPCM;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x01;
|
||||
break;
|
||||
|
||||
case 0x01: /* VQ + LZ (usually music) */
|
||||
case 0x03: /* VQ + deflate (usually sfx/voices) */
|
||||
vgmstream->codec_data = init_circus_vq(streamFile, 0x20, codec, flags);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_CIRCUS_VQ;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* not too sure about samples, since decompressed size isn't exact sometimes vs
|
||||
* total decompression, though it's what the code uses to alloc bufs (plus + 0x2000 leeway) */
|
||||
//vgmstream->num_samples = decompressed_size / 0x2000 * 4064 / channel_count;
|
||||
//if (decompressed_size % 0x2000 != 0)
|
||||
// vgmstream->num_samples += (decompressed_size % 0x2000) / channel_count;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,125 +1,26 @@
|
||||
#ifndef _XWMA_KONAMI_STREAMFILE_H_
|
||||
#define _XWMA_KONAMI_STREAMFILE_H_
|
||||
#include "../streamfile.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* config */
|
||||
off_t stream_offset;
|
||||
size_t stream_size;
|
||||
size_t block_align;
|
||||
|
||||
/* state */
|
||||
off_t logical_offset; /* fake offset */
|
||||
off_t physical_offset; /* actual offset */
|
||||
size_t block_size; /* current size */
|
||||
size_t skip_size; /* size from block start to reach data */
|
||||
size_t data_size; /* usable size in a block */
|
||||
|
||||
size_t logical_size;
|
||||
} xwma_konami_io_data;
|
||||
|
||||
|
||||
static size_t xwma_konami_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, xwma_konami_io_data* data) {
|
||||
size_t total_read = 0;
|
||||
|
||||
|
||||
/* re-start when previous offset (can't map logical<>physical offsets) */
|
||||
if (data->logical_offset < 0 || offset < data->logical_offset) {
|
||||
data->physical_offset = data->stream_offset;
|
||||
data->logical_offset = 0x00;
|
||||
data->data_size = 0;
|
||||
}
|
||||
|
||||
/* read blocks */
|
||||
while (length > 0) {
|
||||
|
||||
/* ignore EOF */
|
||||
if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* process new block */
|
||||
if (data->data_size == 0) {
|
||||
data->block_size = align_size_to_block(data->block_align, 0x10);
|
||||
data->data_size = data->block_align;
|
||||
data->skip_size = 0x00;
|
||||
}
|
||||
|
||||
/* move to next block */
|
||||
if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
|
||||
data->physical_offset += data->block_size;
|
||||
data->logical_offset += data->data_size;
|
||||
data->data_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
{
|
||||
size_t bytes_consumed, bytes_done, to_read;
|
||||
|
||||
bytes_consumed = offset - data->logical_offset;
|
||||
to_read = data->data_size - bytes_consumed;
|
||||
if (to_read > length)
|
||||
to_read = length;
|
||||
bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
|
||||
|
||||
total_read += bytes_done;
|
||||
dest += bytes_done;
|
||||
offset += bytes_done;
|
||||
length -= bytes_done;
|
||||
|
||||
if (bytes_done != to_read || bytes_done == 0) {
|
||||
break; /* error/EOF */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
static size_t xwma_konami_io_size(STREAMFILE *streamfile, xwma_konami_io_data* data) {
|
||||
uint8_t buf[1];
|
||||
|
||||
if (data->logical_size)
|
||||
return data->logical_size;
|
||||
|
||||
/* force a fake read at max offset, to get max logical_offset (will be reset next read) */
|
||||
xwma_konami_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
|
||||
data->logical_size = data->logical_offset;
|
||||
|
||||
return data->logical_size;
|
||||
}
|
||||
|
||||
/* Handles de-padding Konami XWMA blocked streams */
|
||||
static STREAMFILE* setup_xwma_konami_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t block_align) {
|
||||
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
|
||||
xwma_konami_io_data io_data = {0};
|
||||
size_t io_data_size = sizeof(xwma_konami_io_data);
|
||||
|
||||
io_data.stream_offset = stream_offset;
|
||||
io_data.stream_size = get_streamfile_size(streamFile) - stream_offset;
|
||||
io_data.block_align = block_align;
|
||||
io_data.logical_offset = -1; /* force phys offset reset */
|
||||
|
||||
/* setup subfile */
|
||||
new_streamFile = open_wrap_streamfile(streamFile);
|
||||
if (!new_streamFile) goto fail;
|
||||
temp_streamFile = new_streamFile;
|
||||
|
||||
new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, xwma_konami_io_read,xwma_konami_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 /* _XWMA_KONAMI_STREAMFILE_H_ */
|
||||
#ifndef _XWMA_KONAMI_STREAMFILE_H_
|
||||
#define _XWMA_KONAMI_STREAMFILE_H_
|
||||
#include "deblock_streamfile.h"
|
||||
|
||||
static void block_callback(STREAMFILE* sf, deblock_io_data* data) {
|
||||
data->block_size = align_size_to_block(data->cfg.chunk_size, 0x10);
|
||||
data->data_size = data->cfg.chunk_size;
|
||||
data->skip_size = 0x00;
|
||||
}
|
||||
|
||||
/* De-pads Konami XWMA streams */
|
||||
static STREAMFILE* setup_xwma_konami_streamfile(STREAMFILE* sf, off_t stream_offset, size_t block_align) {
|
||||
STREAMFILE* new_sf = NULL;
|
||||
deblock_config_t cfg = {0};
|
||||
|
||||
cfg.stream_start = stream_offset;
|
||||
cfg.chunk_size = block_align;
|
||||
cfg.block_callback = block_callback;
|
||||
|
||||
new_sf = open_wrap_streamfile(sf);
|
||||
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
|
||||
//new_sf = open_buffer_streamfile_f(new_sf, 0);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
#endif /* _XWMA_KONAMI_STREAMFILE_H_ */
|
||||
|
@ -518,8 +518,8 @@ typedef struct {
|
||||
size_t data_size;
|
||||
size_t (*read_callback)(STREAMFILE *, uint8_t *, off_t, size_t, void*); /* custom read to modify data before copying into buffer */
|
||||
size_t (*size_callback)(STREAMFILE *, void*); /* size when custom reads make data smaller/bigger than underlying streamfile */
|
||||
//todo would need to make sure re-opened streamfiles work with this, maybe should use init_data_callback per call
|
||||
//size_t (*close_data_callback)(STREAMFILE *, void*); /* called during close, allows to free stuff in data */
|
||||
int (*init_callback)(STREAMFILE*, void*); /* init the data struct members somehow, return >= 0 if ok */
|
||||
void (*close_callback)(STREAMFILE*, void*); /* close the data struct members somehow */
|
||||
} IO_STREAMFILE;
|
||||
|
||||
static size_t io_read(IO_STREAMFILE *streamfile, uint8_t *dst, off_t offset, size_t length) {
|
||||
@ -538,24 +538,25 @@ static void io_get_name(IO_STREAMFILE *streamfile, char *buffer, size_t length)
|
||||
streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */
|
||||
}
|
||||
static STREAMFILE* io_open(IO_STREAMFILE *streamfile, const char * const filename, size_t buffersize) {
|
||||
//todo should have some flag to decide if opening other files with IO
|
||||
STREAMFILE *new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize);
|
||||
return open_io_streamfile(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback, streamfile->size_callback);
|
||||
return open_io_streamfile_ex(new_inner_sf, streamfile->data, streamfile->data_size, streamfile->read_callback, streamfile->size_callback, streamfile->init_callback, streamfile->close_callback);
|
||||
}
|
||||
static void io_close(IO_STREAMFILE *streamfile) {
|
||||
if (streamfile->close_callback)
|
||||
streamfile->close_callback(streamfile->inner_sf, streamfile->data);
|
||||
streamfile->inner_sf->close(streamfile->inner_sf);
|
||||
free(streamfile->data);
|
||||
free(streamfile);
|
||||
}
|
||||
|
||||
STREAMFILE* open_io_streamfile(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback) {
|
||||
STREAMFILE* open_io_streamfile_ex(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback, void* init_callback, void* close_callback) {
|
||||
IO_STREAMFILE *this_sf = NULL;
|
||||
|
||||
if (!streamfile) return NULL;
|
||||
if ((data && !data_size) || (!data && data_size)) return NULL;
|
||||
if (!streamfile) goto fail;
|
||||
if ((data && !data_size) || (!data && data_size)) goto fail;
|
||||
|
||||
this_sf = calloc(1,sizeof(IO_STREAMFILE));
|
||||
if (!this_sf) return NULL;
|
||||
if (!this_sf) goto fail;
|
||||
|
||||
/* set callbacks and internals */
|
||||
this_sf->sf.read = (void*)io_read;
|
||||
@ -569,25 +570,42 @@ STREAMFILE* open_io_streamfile(STREAMFILE *streamfile, void *data, size_t data_s
|
||||
this_sf->inner_sf = streamfile;
|
||||
if (data) {
|
||||
this_sf->data = malloc(data_size);
|
||||
if (!this_sf->data) {
|
||||
free(this_sf);
|
||||
return NULL;
|
||||
}
|
||||
if (!this_sf->data) goto fail;
|
||||
memcpy(this_sf->data, data, data_size);
|
||||
}
|
||||
this_sf->data_size = data_size;
|
||||
this_sf->read_callback = read_callback;
|
||||
this_sf->size_callback = size_callback;
|
||||
this_sf->init_callback = init_callback;
|
||||
this_sf->close_callback = close_callback;
|
||||
|
||||
if (this_sf->init_callback) {
|
||||
int ok = this_sf->init_callback(this_sf->inner_sf, this_sf->data);
|
||||
if (ok < 0) goto fail;
|
||||
}
|
||||
|
||||
return &this_sf->sf;
|
||||
|
||||
fail:
|
||||
if (this_sf) free(this_sf->data);
|
||||
free(this_sf);
|
||||
return NULL;
|
||||
}
|
||||
STREAMFILE* open_io_streamfile_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback) {
|
||||
STREAMFILE *new_sf = open_io_streamfile(streamfile, data, data_size, read_callback, size_callback);
|
||||
|
||||
STREAMFILE* open_io_streamfile_ex_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback, void* init_callback, void* close_callback) {
|
||||
STREAMFILE *new_sf = open_io_streamfile_ex(streamfile, data, data_size, read_callback, size_callback, init_callback, close_callback);
|
||||
if (!new_sf)
|
||||
close_streamfile(streamfile);
|
||||
return new_sf;
|
||||
}
|
||||
|
||||
STREAMFILE* open_io_streamfile(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback) {
|
||||
return open_io_streamfile_ex(streamfile, data, data_size, read_callback, size_callback, NULL, NULL);
|
||||
}
|
||||
STREAMFILE* open_io_streamfile_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback) {
|
||||
return open_io_streamfile_ex_f(streamfile, data, data_size, read_callback, size_callback, NULL, NULL);
|
||||
}
|
||||
|
||||
/* **************************************************** */
|
||||
|
||||
typedef struct {
|
||||
|
@ -109,9 +109,14 @@ STREAMFILE* open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t si
|
||||
STREAMFILE* open_clamp_streamfile_f(STREAMFILE *streamfile, off_t start, size_t size);
|
||||
|
||||
/* Opens a STREAMFILE that uses custom IO for streamfile reads.
|
||||
* Can be used to modify data on the fly (ex. decryption), or even transform it from a format to another. */
|
||||
* Can be used to modify data on the fly (ex. decryption), or even transform it from a format to another.
|
||||
* Data is an optional state struct of some size what will be malloc+copied on open. */
|
||||
STREAMFILE* open_io_streamfile(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback);
|
||||
STREAMFILE* open_io_streamfile_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback);
|
||||
/* Same, but calls init on SF open and close on close, when malloc/free is needed.
|
||||
* Data struct may be used to hold malloc'd pointers and stuff. */
|
||||
STREAMFILE* open_io_streamfile_ex(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback, void* init_callback, void* close_callback);
|
||||
STREAMFILE* open_io_streamfile_ex_f(STREAMFILE *streamfile, void *data, size_t data_size, void *read_callback, void *size_callback, void* init_callback, void* close_callback);
|
||||
|
||||
/* Opens a STREAMFILE that reports a fake name, but still re-opens itself properly.
|
||||
* Can be used to trick a meta's extension check (to call from another, with a modified SF).
|
||||
|
@ -490,6 +490,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_tgc,
|
||||
init_vgmstream_kwb,
|
||||
init_vgmstream_lrmd,
|
||||
init_vgmstream_bkhd,
|
||||
|
||||
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
|
||||
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
|
||||
@ -661,6 +662,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vgmstream->coding_type == coding_CIRCUS_VQ) {
|
||||
reset_circus_vq(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_RELIC) {
|
||||
reset_relic(vgmstream->codec_data);
|
||||
}
|
||||
@ -832,6 +837,11 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vgmstream->coding_type == coding_CIRCUS_VQ) {
|
||||
free_circus_vq(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_RELIC) {
|
||||
free_relic(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
@ -1097,6 +1107,7 @@ void render_vgmstream(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmst
|
||||
case layout_blocked_h4m:
|
||||
case layout_blocked_xa_aiff:
|
||||
case layout_blocked_vs_square:
|
||||
case layout_blocked_vid1:
|
||||
render_vgmstream_blocked(buffer,sample_count,vgmstream);
|
||||
break;
|
||||
case layout_segmented:
|
||||
@ -1288,6 +1299,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
return 0; /* varies per mode */
|
||||
case coding_EA_MT:
|
||||
return 0; /* 432, but variable in looped files */
|
||||
case coding_CIRCUS_VQ:
|
||||
return 0;
|
||||
case coding_RELIC:
|
||||
return 0; /* 512 */
|
||||
case coding_CRI_HCA:
|
||||
@ -1775,6 +1788,9 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
samples_to_do,vgmstream->channels);
|
||||
break;
|
||||
#endif
|
||||
case coding_CIRCUS_VQ:
|
||||
decode_circus_vq(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, samples_to_do, vgmstream->channels);
|
||||
break;
|
||||
case coding_RELIC:
|
||||
decode_relic(&vgmstream->ch[0], vgmstream->codec_data, buffer+samples_written*vgmstream->channels,
|
||||
samples_to_do);
|
||||
@ -2227,6 +2243,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
||||
|
||||
/* prepare certain codecs' internal state for looping */
|
||||
|
||||
if (vgmstream->coding_type == coding_CIRCUS_VQ) {
|
||||
seek_circus_vq(vgmstream->codec_data, vgmstream->loop_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_RELIC) {
|
||||
seek_relic(vgmstream->codec_data, vgmstream->loop_sample);
|
||||
}
|
||||
@ -2426,12 +2446,12 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
snprintf(temp,TEMPSIZE, "interleave: %#x bytes\n", (int32_t)vgmstream->interleave_block_size);
|
||||
concatn(length,desc,temp);
|
||||
|
||||
if (vgmstream->interleave_first_block_size) {
|
||||
if (vgmstream->interleave_first_block_size && vgmstream->interleave_first_block_size != vgmstream->interleave_block_size) {
|
||||
snprintf(temp,TEMPSIZE, "interleave first block: %#x bytes\n", (int32_t)vgmstream->interleave_first_block_size);
|
||||
concatn(length,desc,temp);
|
||||
}
|
||||
|
||||
if (vgmstream->interleave_last_block_size) {
|
||||
if (vgmstream->interleave_last_block_size && vgmstream->interleave_last_block_size != vgmstream->interleave_block_size) {
|
||||
snprintf(temp,TEMPSIZE, "interleave last block: %#x bytes\n", (int32_t)vgmstream->interleave_last_block_size);
|
||||
concatn(length,desc,temp);
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ typedef enum {
|
||||
coding_UBI_ADPCM, /* Ubisoft 4/6-bit ADPCM */
|
||||
|
||||
coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */
|
||||
|
||||
coding_CIRCUS_VQ, /* Circus VQ */
|
||||
coding_RELIC, /* Relic Codec (DCT-based) */
|
||||
coding_CRI_HCA, /* CRI High Compression Audio (MDCT-based) */
|
||||
|
||||
@ -274,6 +274,7 @@ typedef enum {
|
||||
layout_blocked_h4m, /* H4M video */
|
||||
layout_blocked_xa_aiff, /* XA in AIFF files [Crusader: No Remorse (SAT), Road Rash (3DO)] */
|
||||
layout_blocked_vs_square,
|
||||
layout_blocked_vid1,
|
||||
|
||||
/* otherwise odd */
|
||||
layout_segmented, /* song divided in segments (song sections) */
|
||||
@ -620,7 +621,7 @@ typedef enum {
|
||||
meta_VXN, /* Gameloft mobile games */
|
||||
meta_EA_SNR_SNS, /* Electronic Arts SNR+SNS (Burnout Paradise) */
|
||||
meta_EA_SPS, /* Electronic Arts SPS (Burnout Crash) */
|
||||
meta_NGC_VID1, /* Neversoft .ogg (Gun GC) */
|
||||
meta_VID1,
|
||||
meta_PC_FLX, /* Ultima IX PC */
|
||||
meta_MOGG, /* Harmonix Music Systems MOGG Vorbis */
|
||||
meta_OGG_VORBIS, /* Ogg Vorbis */
|
||||
|
@ -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_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;VGM_WINAMP_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;VGM_WINAMP_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
@ -98,7 +98,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;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;VGM_WINAMP_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_ATRAC9;VGM_USE_CELT;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;VGM_WINAMP_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
|
@ -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_VORBIS;VGM_USE_MPEG;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>
|
||||
<PreprocessorDefinitions>WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
@ -86,7 +86,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_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_WIN32_WINNT=0x501;WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
|
Loading…
x
Reference in New Issue
Block a user