diff --git a/BUILD.md b/BUILD.md index 7f720a4a..72879644 100644 --- a/BUILD.md +++ b/BUILD.md @@ -85,7 +85,7 @@ Requires the dev version of Audacious (and dependencies), automake/autoconf, and The plugin needs Audacious 3.5 or higher. New Audacious releases can break plugin compatibility so it may not work with the latest version unless adapted first. -FFmpeg and other external libraries aren't enabled, thus some formats are not supported. libvorbis and libmpg123 can be disabled with -DVGM_DISABLE_VORBIS and -DVGM_DISABLE_MPEG. +libvorbis and libmpg123 will be used if found, while FFmpeg and other external libraries aren't enabled, thus some formats won't work. Windows builds aren't supported at the moment (should be possible but there are complex dependency chains). @@ -200,7 +200,7 @@ To add a new one: - *src/libvgmstream.vcproj/vcxproj/filters*: add to compile new (format-name).c parser in VS - if the format needs an external library don't forget to mark optional parts with: *#ifdef VGM_USE_X ... #endif* -Ultimately the meta must alloc the VGMSTREAM, set config and initial state. vgmstream needs the total number samples to work, so at times must convert from data sizes to samples (doing calculations or using helpers). +Ultimately the meta must alloc the VGMSTREAM, set config and initial state. vgmstream needs the total of number samples to work, so at times must convert from data sizes to samples (doing calculations or using helpers). It also needs to open and assign to the VGMSTREAM one or several STREAMFILEs (usually reopening the base one, but could be any other file) to do I/O during decode, as well as setting the starting offsets of each channel and other values; this gives metas full flexibility at the cost of some repetition. The STREAMFILE passed to the meta will be discarded and its pointer must not be reused. @@ -208,7 +208,7 @@ The .c file is usually named after the format's main extension or header id, opt Different formats may use the same extension but this isn't a problem as long as the header id or some other validation tells them apart, and should be implemented in separate .c files. If the format is headerless and the extension isn't unique enough it probably needs a generic GENH/TXTH header instead of direct support. -If the format supports subsongs it should read the stream index (subsong number) in the passed STREAMFILE, and use it to parse a section of the file. Then it must report the number of subsongs in the VGMSTREAM, to signal this feature is enabled. The index is 1-based (first subsong is 1, 0 is default/first). +If the format supports subsongs it should read the stream index (subsong number) in the passed STREAMFILE, and use it to parse a section of the file. Then it must report the number of subsongs in the VGMSTREAM, to signal this feature is enabled. The index is 1-based (first subsong is 1, 0 is default/first). This makes possible to directly use bank-like formats like .FSB, and while vgmstream could technically support any container (like generic bigfiles or even .zip) it should be restricted to files that actually are audio banks. #### layouts Layouts control most of the main logic: @@ -225,7 +225,7 @@ Available layouts, depending on how codec data is laid out: - blocked: data is divided into blocks, often with a header. Layout detects when a block is done and asks a helper function to fix offsets (skipping the header and pointing to data per channel), depending on the block format. - others: uncommon cases may need its own custom layout (ex.- multistream/subfiles) -The layout is used mainly depends on the decoder. MP3 data (that may have 1 or 2 channels per frame) uses the flat layout, while DSP ADPCM (that only decodes one channel at a time) is interleaved. In case of mono files either could be used as there won't be any actual difference. +The layout used mainly depends on the decoder. MP3 data (that may have 1 or 2 channels per frame) uses flat layout, while DSP ADPCM (that only decodes one channel at a time) is interleaved. In case of mono files either could be used as there won't be any actual difference. Layouts expect the VGMSTREAM to be properly initialized during the meta processing (channel offsets must point to each channel start offset). diff --git a/Makefile b/Makefile index cd7bff0f..efa21762 100644 --- a/Makefile +++ b/Makefile @@ -4,13 +4,13 @@ ### defs # currently aimed to WIN32 builds but vgmstream_cli should work for others (or use autotools instead) -export TARGET_OS = WIN32 - +export TARGET_OS = $(OS) + ### tools RMF = rm -f -ifeq ($(TARGET_OS),WIN32) +ifeq ($(TARGET_OS),Windows_NT) SHELL = sh CC = gcc AR = ar diff --git a/README.md b/README.md index d77d2f04..ffc322c2 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Options: -c: loop forever (continuously) -m: print metadata only, don't decode -x: decode and print adxencd command line to encode as ADX -2 -g: decode and print oggenc command line to encode as OGG + -g: decode and print oggenc command line to encode as OGG -b: decode and print batch variable commands -L: append a smpl chunk and create a looping wav -e: force end-to-end looping @@ -64,7 +64,7 @@ Options: -r outfile2.wav: output a second time after resetting -2 N: only output the Nth (first is 0) set of stereo channels -F: don't fade after N loops and play the rest of the stream - -s N: select subtream N, if the format supports multiple streams + -s N: select subsong N, if the format supports multiple subsongs ``` Typical usage would be: ```test -o happy.wav happy.adx``` to decode ```happy.adx``` to ```happy.wav```. diff --git a/cli/Makefile b/cli/Makefile index 93d1e39b..23a93c1f 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -5,7 +5,7 @@ ### main defs -ifeq ($(TARGET_OS),WIN32) +ifeq ($(TARGET_OS),Windows_NT) OUTPUT_CLI = test.exe OUTPUT_123 = vgmstream123.exe else @@ -14,42 +14,38 @@ else endif # -DUSE_ALLOCA -ifeq ($(TARGET_OS),WIN32) +ifeq ($(TARGET_OS),Windows_NT) CFLAGS += -DWIN32 endif -CFLAGS += -Wall -O3 -DVAR_ARRAYS -I../ext_includes $(EXTRA_CFLAGS) -LDFLAGS += -L../src -L../ext_libs -lm -lvgmstream $(EXTRA_LDFLAGS) +CFLAGS += -Wall -Werror=format-security -Wdeclaration-after-statement -Wvla -O3 -DVAR_ARRAYS -I../ext_includes $(EXTRA_CFLAGS) +LDFLAGS += -L../src -L../ext_libs -lvgmstream $(EXTRA_LDFLAGS) -lm TARGET_EXT_LIBS = LIBAO_INC_PATH = ../../libao/include LIBAO_LIB_PATH = ../../libao/bin #ifdef VGM_DEBUG -# CFLAGS += -DVGM_DEBUG_OUTPUT -O0 -Werror=format-security -Wdeclaration-after-statement -Wvla +# CFLAGS += -DVGM_DEBUG_OUTPUT -O0 # CFLAGS += -Wold-style-definition -Woverflow -Wpointer-arith -Wstrict-prototypes -pedantic -std=gnu90 -fstack-protector -Wformat #endif ### external libs -ifeq ($(TARGET_OS),WIN32) +ifeq ($(TARGET_OS),Windows_NT) VGM_ENABLE_VORBIS = 1 ifeq ($(VGM_ENABLE_VORBIS),1) - #CFLAGS += -DVGM_USE_VORBIS + CFLAGS += -DVGM_USE_VORBIS LDFLAGS += -lvorbis TARGET_EXT_LIBS += libvorbis.a -else - CFLAGS += -DVGM_DISABLE_VORBIS endif VGM_ENABLE_MPEG = 1 ifeq ($(VGM_ENABLE_MPEG),1) - #CFLAGS += -DVGM_USE_MPEG + CFLAGS += -DVGM_USE_MPEG LDFLAGS += -lmpg123-0 TARGET_EXT_LIBS += libmpg123-0.a -else - CFLAGS += -DVGM_DISABLE_MPEG endif VGM_ENABLE_G7221 = 1 diff --git a/cli/test.vcproj b/cli/test.vcproj index 17264224..2f916d66 100644 --- a/cli/test.vcproj +++ b/cli/test.vcproj @@ -121,7 +121,7 @@ Optimization="2" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="..\ext_libs\Getopt;..\ext_includes" - PreprocessorDefinitions="WIN32;VGM_USE_G7221;NDEBUG;_CONSOLE" + PreprocessorDefinitions="WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_G7221;NDEBUG;_CONSOLE" RuntimeLibrary="2" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" diff --git a/cli/test.vcxproj b/cli/test.vcxproj index 82258039..588672ee 100644 --- a/cli/test.vcxproj +++ b/cli/test.vcxproj @@ -71,7 +71,7 @@ Disabled ../ext_libs/Getopt;../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories) - WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_CONSOLE;%(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) true EnableFastChecks MultiThreadedDebug @@ -97,7 +97,7 @@ ../ext_libs/Getopt;../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories) - WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;NDEBUG;_WINDOWS;_CONSOLE;%(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) MultiThreaded diff --git a/cli/vgmstream_cli.c b/cli/vgmstream_cli.c index 1fe89833..a26cd987 100644 --- a/cli/vgmstream_cli.c +++ b/cli/vgmstream_cli.c @@ -50,7 +50,7 @@ static void usage(const char * name) { " -r outfile2.wav: output a second time after resetting\n" " -2 N: only output the Nth (first is 0) set of stereo channels\n" " -F: don't fade after N loops and play the rest of the stream\n" - " -s N: select subtream N, if the format supports multiple streams\n" + " -s N: select subsong N, if the format supports multiple subsongs\n" ,name); } diff --git a/cli/vrts.bat b/cli/vrts.bat index dd4debd5..fee41a4d 100644 --- a/cli/vrts.bat +++ b/cli/vrts.bat @@ -30,6 +30,8 @@ REM # -nc: don't report correct files set OP_NOCORRECT= REM # -p: performance test (decode with new exe and no comparison done) set OP_PERFORMANCE= +REM # -fc : file comparer (Windows's FC is slow) +set OP_CMD_FC=fc /a /b REM # parse options @@ -42,6 +44,7 @@ if "%~1"=="-r" set OP_RECURSIVE=/s if "%~1"=="-nd" set OP_NODELETE=true if "%~1"=="-nc" set OP_NOCORRECT=true if "%~1"=="-p" set OP_PERFORMANCE=true +if "%~1"=="-fc" set OP_CMD_FC=%2 shift goto set_options :end_options @@ -138,9 +141,9 @@ REM # ######################################################################## ) ) - REM # compare files (doesn't use /b for speedup, somehow) - set CMP_WAV=fc /a /lb1 "%WAV_OLD%" "%WAV_NEW%" - set CMP_TXT=fc /a /lb1 "%TXT_OLD%" "%TXT_NEW%" + REM # compare files (without /b may to be faster for small files?) + set CMP_WAV=%OP_CMD_FC% "%WAV_OLD%" "%WAV_NEW%" + set CMP_TXT=%OP_CMD_FC% "%TXT_OLD%" "%TXT_NEW%" %CMP_WAV% 1> nul 2>&1 set CMP_WAV_ERROR=0 diff --git a/configure.ac b/configure.ac index 94f61852..3e4456d8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,8 @@ -dnl audacious-vgmstream m4 script +dnl automake-vgmstream m4 script dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.53) -AC_INIT(audacious-vgmstream,1.3.0) +AC_INIT(automake-vgmstream,1.3.0) AM_INIT_AUTOMAKE([subdir-objects]) AC_CONFIG_HEADERS(audacious/config.h) AM_DISABLE_STATIC @@ -17,22 +17,28 @@ AM_PROG_LIBTOOL AC_PATH_X AC_PATH_XTRA +have_vorbis=no +PKG_CHECK_MODULES(VORBIS, [vorbis], have_vorbis=yes, + [AC_MSG_WARN([Cannot find libvorbis - will not enable Vorbis formats])] +) +AM_CONDITIONAL(HAVE_VORBIS, test "$have_vorbis" = yes) + +have_vorbisfile=no +PKG_CHECK_MODULES(VORBISFILE, [vorbisfile], have_vorbisfile=yes, + [AC_MSG_WARN([Cannot find libvorbisfile - will not enable Vorbis formats])] +) +AM_CONDITIONAL(HAVE_VORBISFILE, test "$have_vorbisfile" = yes) + +have_libmpg123=no +PKG_CHECK_MODULES(MPG123, [libmpg123], have_libmpg123=yes, + [AC_MSG_WARN([Cannot find libmpg123 - will not enable MPEG formats])] +) +AM_CONDITIONAL(HAVE_LIBMPG123, test "$have_libmpg123" = yes) + PKG_CHECK_MODULES(AUDACIOUS, [audacious >= 3.5.0],, [AC_MSG_ERROR([Cannot find audacious >= 3.5.0 correctly installed])] ) -PKG_CHECK_MODULES(VORBIS, [vorbis],, - [AC_MSG_ERROR([Cannot find libvorbis])] -) - -PKG_CHECK_MODULES(VORBISFILE, [vorbisfile],, - [AC_MSG_ERROR([Cannot find libvorbisfile])] -) - -PKG_CHECK_MODULES(MPG123, [libmpg123],, - [AC_MSG_ERROR([Cannot find libmpg123])] -) - PKG_CHECK_MODULES(GTK, [glib-2.0 >= 2.6.0 gtk+-2.0 >= 2.6.0 gthread-2.0 pango], , [AC_MSG_ERROR([Cannot find glib2/gtk2/pango])] ) diff --git a/ext_libs/Makefile b/ext_libs/Makefile index af644561..826cf54f 100644 --- a/ext_libs/Makefile +++ b/ext_libs/Makefile @@ -3,8 +3,8 @@ # # needed? -ifneq ($(TARGET_OS),WIN32) -$(error option must be built with TARGET_OS = WIN32) +ifneq ($(TARGET_OS),Windows_NT) +$(error option must be built with TARGET_OS = Windows_NT) endif diff --git a/fb2k/foo_input_vgmstream.vcproj b/fb2k/foo_input_vgmstream.vcproj index e142a1b0..411856dc 100644 --- a/fb2k/foo_input_vgmstream.vcproj +++ b/fb2k/foo_input_vgmstream.vcproj @@ -42,7 +42,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="../ext_includes" - PreprocessorDefinitions="WIN32;VGM_USE_FFMPEG;VGM_USE_G719;VGM_USE_G7221;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;" + PreprocessorDefinitions="WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G719;VGM_USE_G7221;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -120,7 +120,7 @@ Disabled ../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) - WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions) + 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) true EnableFastChecks MultiThreadedDebug @@ -98,7 +98,7 @@ ../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) - WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions) + 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) MultiThreaded diff --git a/fb2k/foo_streamfile.cpp b/fb2k/foo_streamfile.cpp index 2608bb7e..3235cdb0 100644 --- a/fb2k/foo_streamfile.cpp +++ b/fb2k/foo_streamfile.cpp @@ -15,80 +15,73 @@ extern "C" { } #include "foo_vgmstream.h" -/* On EOF reads we can return length 0, or ignore and return the requested length + 0-set the buffer. - * Some decoders don't check for EOF and may decode garbage if returned 0, as read_Nbit() funcs return -1. - * Only matters for metas that get num_samples wrong (bigger than total data). */ -#define STREAMFILE_IGNORE_EOF 0 - /* a STREAMFILE that operates via foobar's file service using a buffer */ -typedef struct _FOO_STREAMFILE { - struct _STREAMFILE sf; - abort_callback * p_abort; - service_ptr_t m_file; - char * name; - off_t offset; - size_t validsize; - uint8_t * buffer; - size_t buffersize; - size_t filesize; -} FOO_STREAMFILE; +typedef struct { + STREAMFILE sf; /* callbacks */ + + service_ptr_t m_file; /* foobar IO service */ + abort_callback * p_abort; /* foobar error stuff */ + char * name; /* IO filename */ + off_t offset; /* last read offset (info) */ + off_t buffer_offset; /* current buffer data start */ + uint8_t * buffer; /* data buffer */ + size_t buffersize; /* max buffer size */ + size_t validsize; /* current buffer size */ + size_t filesize; /* buffered file size */ +} FOO_STREAMFILE; -static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t m_file,const char * const filename, size_t buffersize, abort_callback * p_abort); static STREAMFILE * open_foo_streamfile_buffer(const char * const filename, size_t buffersize, abort_callback * p_abort, t_filestats * stats); +static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t m_file,const char * const filename, size_t buffersize, abort_callback * p_abort); -static size_t read_the_rest_foo(uint8_t * dest, off_t offset, size_t length, FOO_STREAMFILE * streamfile) { - size_t length_read_total=0; +static size_t read_foo(FOO_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { + size_t length_read_total = 0; + + if (!streamfile || !dest || length <= 0 || offset < 0) + return 0; /* is the part of the requested length in the buffer? */ - if (offset >= streamfile->offset && offset < streamfile->offset + streamfile->validsize) { - size_t length_read; - off_t offset_into_buffer = offset - streamfile->offset; - length_read = streamfile->validsize - offset_into_buffer; + if (offset >= streamfile->buffer_offset && offset < streamfile->buffer_offset + streamfile->validsize) { + size_t length_to_read; + off_t offset_into_buffer = offset - streamfile->buffer_offset; - memcpy(dest,streamfile->buffer + offset_into_buffer,length_read); - length_read_total += length_read; - length -= length_read; - offset += length_read; - dest += length_read; + length_to_read = streamfile->validsize - offset_into_buffer; + if (length_to_read > length) + length_to_read = length; + + memcpy(dest,streamfile->buffer + offset_into_buffer,length_to_read); + length_read_total += length_to_read; + length -= length_to_read; + offset += length_to_read; + dest += length_to_read; } - /* What would make more sense here is to read the whole request - * at once into the dest buffer, as it must be large enough, and then - * copy some part of that into our own buffer. - * The destination buffer is supposed to be much smaller than the - * STREAMFILE buffer, though. Maybe we should only ever return up - * to the buffer size to avoid having to deal with things like this - * which are outside of my intended use. - */ + /* read the rest of the requested length */ while (length > 0) { size_t length_to_read; - size_t length_read; - streamfile->validsize = 0; /* buffer is empty now */ - /* request outside file: ignore to avoid seek/read */ - if (offset > streamfile->filesize) { - streamfile->offset = streamfile->filesize; - -#if STREAMFILE_IGNORE_EOF - memset(dest,0,length); /* dest is already shifted */ - return length_read_total + length; /* partially-read + 0-set buffer */ -#else - return length_read_total; /* partially-read buffer */ -#endif + /* ignore requests at EOF */ + if (offset >= streamfile->filesize) { + //offset = streamfile->filesize; /* seems fseek doesn't clamp offset */ + //VGM_ASSERT_ONCE(offset > streamfile->filesize, "STDIO: reading over filesize 0x%x @ 0x%lx + 0x%x\n", streamfile->filesize, offset, length); + break; } /* position to new offset */ try { streamfile->m_file->seek(offset,*streamfile->p_abort); - //if (streamfile->m_file->is_eof(*streamfile->p_abort)) /* allow edge case of offset=filesize */ - // return length_read; } catch (...) { - streamfile->offset = streamfile->filesize; - return 0; /* fail miserably (fseek shouldn't fail and reach this) */ + break; /* this shouldn't happen in our code */ + } + + /* fill the buffer (offset now is beyond buffer_offset) */ + try { + streamfile->buffer_offset = offset; + streamfile->validsize = streamfile->m_file->read(streamfile->buffer,streamfile->buffersize,*streamfile->p_abort); + } catch(...) { + break; /* improbable? */ } - streamfile->offset = offset; /* decide how much must be read this time */ if (length > streamfile->buffersize) @@ -96,70 +89,45 @@ static size_t read_the_rest_foo(uint8_t * dest, off_t offset, size_t length, FOO else length_to_read = length; - /* fill the buffer */ - try { - length_read = streamfile->m_file->read(streamfile->buffer,streamfile->buffersize,*streamfile->p_abort); - } catch(...) { - return 0; /* fail miserably */ - } - streamfile->validsize = length_read; - - - /* if we can't get enough to satisfy the request (EOF) we give up */ - if (length_read < length_to_read) { - memcpy(dest,streamfile->buffer,length_read); - -#if STREAMFILE_IGNORE_EOF - memset(dest+length_read,0,length-length_read); - return length_read_total + length; /* partially-read + 0-set buffer */ -#else - return length_read_total + length_read; /* partially-read buffer */ -#endif + /* give up on partial reads (EOF) */ + if (streamfile->validsize < length_to_read) { + memcpy(dest,streamfile->buffer,streamfile->validsize); + offset += streamfile->validsize; + length_read_total += streamfile->validsize; + break; } /* use the new buffer */ memcpy(dest,streamfile->buffer,length_to_read); + offset += length_to_read; length_read_total += length_to_read; length -= length_to_read; dest += length_to_read; - offset += length_to_read; } + streamfile->offset = offset; /* last fread offset */ return length_read_total; } - -static size_t read_foo(FOO_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { - - if (!streamfile || !dest || length<=0) - return 0; - - /* request outside file: ignore to avoid seek/read in read_the_rest_foo() */ - if (offset > streamfile->filesize) { - streamfile->offset = streamfile->filesize; - -#if STREAMFILE_IGNORE_EOF - memset(dest,0,length); - return length; /* 0-set buffer */ -#else - return 0; /* nothing to read */ -#endif - } - - /* just copy if entire request is within the buffer */ - if (offset >= streamfile->offset && offset + length <= streamfile->offset + streamfile->validsize) { - off_t offset_into_buffer = offset - streamfile->offset; - memcpy(dest,streamfile->buffer + offset_into_buffer,length); - return length; - } - - /* request outside buffer: new fread */ - { - return read_the_rest_foo(dest,offset,length,streamfile); - } +static size_t get_size_foo(FOO_STREAMFILE * streamfile) { + return streamfile->filesize; } - -STREAMFILE * open_foo_streamfile(const char * const filename, abort_callback * p_abort, t_filestats * stats) { - return open_foo_streamfile_buffer(filename,STREAMFILE_DEFAULT_BUFFER_SIZE, p_abort, stats); +static off_t get_offset_foo(FOO_STREAMFILE *streamfile) { + return streamfile->offset; +} +static void get_name_foo(FOO_STREAMFILE *streamfile,char *buffer,size_t length) { + /* Most crap only cares about the filename itself */ + size_t ourlen = strlen(streamfile->name); + if (ourlen > length) { + if (length) strcpy(buffer, streamfile->name + ourlen - length + 1); + } else { + strcpy(buffer, streamfile->name); + } +} +static void close_foo(FOO_STREAMFILE * streamfile) { + streamfile->m_file.release(); + free(streamfile->name); + free(streamfile->buffer); + free(streamfile); } static STREAMFILE *open_foo(FOO_STREAMFILE *streamFile,const char * const filename,size_t buffersize) { @@ -173,7 +141,7 @@ static STREAMFILE *open_foo(FOO_STREAMFILE *streamFile,const char * const filena // if same name, duplicate the file pointer we already have open if (!strcmp(streamFile->name,filename)) { m_file = streamFile->m_file; - if (1) { + { newstreamFile = open_foo_streamfile_buffer_by_file(m_file,filename,buffersize,streamFile->p_abort); if (newstreamFile) { return newstreamFile; @@ -186,45 +154,15 @@ static STREAMFILE *open_foo(FOO_STREAMFILE *streamFile,const char * const filena return open_foo_streamfile_buffer(filename,buffersize,streamFile->p_abort,NULL); } -static size_t get_size_foo(FOO_STREAMFILE * streamfile) { - return streamfile->filesize; -} - -static off_t get_offset_foo(FOO_STREAMFILE *streamFile) { - return streamFile->offset; -} - -static void close_foo(FOO_STREAMFILE * streamfile) { - streamfile->m_file.release(); - free(streamfile->name); - free(streamfile->buffer); - free(streamfile); -} - -static void get_name_foo(FOO_STREAMFILE *streamfile,char *buffer,size_t length) { - /* Most crap only cares about the filename itself */ - size_t ourlen = strlen(streamfile->name); - if (ourlen > length) { - if (length) strcpy(buffer, streamfile->name + ourlen - length + 1); - } else { - strcpy(buffer, streamfile->name); - } -} - static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t m_file,const char * const filename, size_t buffersize, abort_callback * p_abort) { uint8_t * buffer; FOO_STREAMFILE * streamfile; buffer = (uint8_t *) calloc(buffersize,1); - if (!buffer) { - return NULL; - } + if (!buffer) goto fail; streamfile = (FOO_STREAMFILE *) calloc(1,sizeof(FOO_STREAMFILE)); - if (!streamfile) { - free(buffer); - return NULL; - } + if (!streamfile) goto fail; streamfile->sf.read = (size_t (__cdecl *)(_STREAMFILE *,uint8_t *,off_t,size_t)) read_foo; streamfile->sf.get_size = (size_t (__cdecl *)(_STREAMFILE *)) get_size_foo; @@ -234,22 +172,22 @@ static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t m_fil streamfile->sf.close = (void (__cdecl *)(_STREAMFILE *)) close_foo; streamfile->m_file = m_file; - + streamfile->p_abort = p_abort; streamfile->buffersize = buffersize; streamfile->buffer = buffer; - streamfile->p_abort = p_abort; streamfile->name = strdup(filename); - if (!streamfile->name) { - free(streamfile); - free(buffer); - return NULL; - } + if (!streamfile->name) goto fail; /* cache filesize */ streamfile->filesize = streamfile->m_file->get_size(*streamfile->p_abort); return &streamfile->sf; + +fail: + free(buffer); + free(streamfile); + return NULL; } static STREAMFILE * open_foo_streamfile_buffer(const char * const filename, size_t buffersize, abort_callback * p_abort, t_filestats * stats) { @@ -269,3 +207,7 @@ static STREAMFILE * open_foo_streamfile_buffer(const char * const filename, size return streamFile; } + +STREAMFILE * open_foo_streamfile(const char * const filename, abort_callback * p_abort, t_filestats * stats) { + return open_foo_streamfile_buffer(filename,STREAMFILE_DEFAULT_BUFFER_SIZE, p_abort, stats); +} diff --git a/src/Makefile.autotools.am b/src/Makefile.autotools.am index a15eb51f..68104532 100644 --- a/src/Makefile.autotools.am +++ b/src/Makefile.autotools.am @@ -11,6 +11,17 @@ SUBDIRS = coding layout meta libvgmstream_la_LDFLAGS = coding/libcoding.la layout/liblayout.la meta/libmeta.la libvgmstream_la_SOURCES = (auto-updated) libvgmstream_la_SOURCES += ../ext_libs/clHCA.c -libvgmstream_la_LIBADD = $(AUDACIOUS_LIBS) $(GTK_LIBS) $(VORBISFILE_LIBS) $(VORBIS_LIBS) $(MPG123_LIBS) -lm +libvgmstream_la_LIBADD = $(AUDACIOUS_LIBS) $(GTK_LIBS) -lm EXTRA_DIST = (auto-updated) EXTRA_DIST += ../ext_includes/clHCA.h + +if HAVE_VORBIS +if HAVE_VORBISFILE +AM_CFLAGS += -DVGM_USE_VORBIS +libvgmstream_la_LIBADD += $(VORBISFILE_LIBS) $(VORBIS_LIBS) +endif +endif +if HAVE_LIBMPG123 +AM_CFLAGS += -DVGM_USE_MPEG +libvgmstream_la_LIBADD += $(MPG123_LIBS) +endif diff --git a/src/coding/atrac9_decoder.c b/src/coding/atrac9_decoder.c index bff83eb3..16f11e46 100644 --- a/src/coding/atrac9_decoder.c +++ b/src/coding/atrac9_decoder.c @@ -4,6 +4,22 @@ #include "libatrac9.h" +/* opaque struct */ +struct atrac9_codec_data { + uint8_t *data_buffer; + size_t data_buffer_size; + + sample *sample_buffer; + size_t samples_filled; /* number of samples in the buffer */ + size_t samples_used; /* number of samples extracted from the buffer */ + + int samples_to_discard; + + atrac9_config config; + void *handle; /* decoder handle */ +}; + + atrac9_codec_data *init_atrac9(atrac9_config *cfg) { int status; uint8_t config_data[4]; @@ -93,23 +109,6 @@ void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, status = Atrac9GetCodecInfo(data->handle, &info); if (status < 0) goto decode_fail; - - /* preadjust */ //todo improve - switch(data->config.type) { - case ATRAC9_XVAG: - /* PS4 (ex. The Last of Us) has a RIFF AT9 (can be ignored) instead of the first superframe. - * As subsongs do too, needs to be skipped here instead of adjusting start_offset */ - if (stream->offset == stream->channel_start_offset) { - if (read_32bitBE(stream->offset, stream->streamfile) == 0x00000000 /* padding before RIFF */ - && read_32bitBE(stream->offset + info.superframeSize - 0x08,stream->streamfile) == 0x64617461) { /* RIFF's "data" */ - stream->offset += info.superframeSize; - } - } - break; - default: - break; - } - /* read one raw block (superframe) and advance offsets */ bytes = read_streamfile(data->data_buffer,stream->offset, info.superframeSize,stream->streamfile); if (bytes != data->data_buffer_size) { @@ -119,20 +118,6 @@ void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, stream->offset += bytes; - /* postadjust */ //todo improve - switch(data->config.type) { - case ATRAC9_XVAG: - case ATRAC9_KMA9: - /* skip other subsong blocks */ - if (data->config.interleave_skip && ((stream->offset - stream->channel_start_offset) % data->config.interleave_skip == 0)) { - stream->offset += data->config.interleave_skip * (data->config.subsong_skip - 1); - } - break; - default: - break; - } - - /* decode all frames in the superframe block */ for (iframe = 0; iframe < info.framesInSuperframe; iframe++) { status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used); @@ -222,6 +207,7 @@ fail: return 0; } +#if 0 //not needed (for now) int atrac9_parse_config(uint32_t atrac9_config, int *out_sample_rate, int *out_channels, size_t *out_frame_size) { static const int sample_rate_table[16] = { 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, @@ -252,5 +238,5 @@ int atrac9_parse_config(uint32_t atrac9_config, int *out_sample_rate, int *out_c fail: return 0; } - +#endif #endif diff --git a/src/coding/coding.h b/src/coding/coding.h index 44ed2787..3fa15733 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -62,14 +62,14 @@ void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci void decode_ngc_afc(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* pcm_decoder */ -void decode_pcm16LE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm16BE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm16le(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm16be(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian); void decode_pcm8(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm8_sb_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); -void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_pcm8_sb(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ulaw_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_alaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); @@ -79,6 +79,8 @@ size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample); /* psx_decoder */ void decode_psx(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags); void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int frame_size); +int ps_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end); +int ps_find_loop_offsets_full(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end); size_t ps_bytes_to_samples(size_t bytes, int channels); size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels); @@ -142,7 +144,7 @@ void decode_SASSC(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); /* mtaf_decoder */ -void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int channels); +void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* mta2_decoder */ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); @@ -242,7 +244,7 @@ void reset_atrac9(VGMSTREAM *vgmstream); void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample); void free_atrac9(atrac9_codec_data *data); size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data); -int atrac9_parse_config(uint32_t atrac9_config, int *out_sample_rate, int *out_channels, size_t *out_frame_size); +//int atrac9_parse_config(uint32_t atrac9_config, int *out_sample_rate, int *out_channels, size_t *out_frame_size); #endif #ifdef VGM_USE_CELT @@ -269,7 +271,6 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples); size_t ffmpeg_make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate); -size_t ffmpeg_get_eaxma_virtual_size(int channels, int streamed, off_t real_offset, size_t real_size, STREAMFILE *streamFile); size_t switch_opus_get_samples(off_t offset, size_t data_size, int sample_rate, STREAMFILE *streamFile); diff --git a/src/coding/ffmpeg_decoder.c b/src/coding/ffmpeg_decoder.c index 07733f8c..05098880 100644 --- a/src/coding/ffmpeg_decoder.c +++ b/src/coding/ffmpeg_decoder.c @@ -220,10 +220,7 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) { /* main read */ switch(data->config.type) { - case FFMPEG_EA_XMA: ret = ffmpeg_custom_read_eaxma(data, buf, buf_size); break; case FFMPEG_SWITCH_OPUS: ret = ffmpeg_custom_read_switch_opus(data, buf, buf_size); break; - //case FFMPEG_EA_SCHL: ret = ffmpeg_custom_read_ea_schl(data, buf, buf_size); break; - //case FFMPEG_SFH: ret = ffmpeg_custom_read_sfh(data, buf, buf_size); break; default: ret = ffmpeg_custom_read_standard(data, buf, buf_size); break; } data->virtual_offset += ret; @@ -288,10 +285,7 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { /* main seek */ switch(data->config.type) { - case FFMPEG_EA_XMA: offset = ffmpeg_custom_seek_eaxma(data, offset); break; case FFMPEG_SWITCH_OPUS: offset = ffmpeg_custom_seek_switch_opus(data, offset); break; - //case FFMPEG_EA_SCHL: offset = ffmpeg_custom_seek_ea_schl(data, offset); break; - //case FFMPEG_SFH: offset = ffmpeg_custom_seek_sfh(data, offset); break; default: offset = ffmpeg_custom_seek_standard(data, offset); break; } data->virtual_offset = offset; @@ -305,10 +299,7 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { static int64_t ffmpeg_size(ffmpeg_codec_data * data) { int64_t bytes; switch(data->config.type) { - case FFMPEG_EA_XMA: bytes = ffmpeg_custom_size_eaxma(data); break; case FFMPEG_SWITCH_OPUS: bytes = ffmpeg_custom_size_switch_opus(data); break; - //case FFMPEG_EA_SCHL: bytes = ffmpeg_custom_size_ea_schl(data); break; - //case FFMPEG_SFH: bytes = ffmpeg_custom_size_sfh(data); break; default: bytes = ffmpeg_custom_size_standard(data); break; } diff --git a/src/coding/ffmpeg_decoder_utils.h b/src/coding/ffmpeg_decoder_utils.h index 64461fe9..beaf1493 100644 --- a/src/coding/ffmpeg_decoder_utils.h +++ b/src/coding/ffmpeg_decoder_utils.h @@ -23,22 +23,10 @@ int ffmpeg_custom_read_standard(ffmpeg_codec_data *data, uint8_t *buf, int buf_s int64_t ffmpeg_custom_seek_standard(ffmpeg_codec_data *data, int64_t virtual_offset); int64_t ffmpeg_custom_size_standard(ffmpeg_codec_data *data); -int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); -int64_t ffmpeg_custom_seek_eaxma(ffmpeg_codec_data *data, int64_t virtual_offset); -int64_t ffmpeg_custom_size_eaxma(ffmpeg_codec_data *data); - int ffmpeg_custom_read_switch_opus(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); int64_t ffmpeg_custom_seek_switch_opus(ffmpeg_codec_data *data, int64_t virtual_offset); int64_t ffmpeg_custom_size_switch_opus(ffmpeg_codec_data *data); -//int ffmpeg_custom_read_ea_schl(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); -//int64_t ffmpeg_custom_seek_ea_schl(ffmpeg_codec_data *data, int64_t virtual_offset); -//int64_t ffmpeg_custom_size_ea_schl(ffmpeg_codec_data *data); - -//int ffmpeg_custom_read_sfh(ffmpeg_codec_data *data, uint8_t *buf, int buf_size); -//int64_t ffmpeg_custom_seek_sfh(ffmpeg_codec_data *data, int64_t virtual_offset); -//int64_t ffmpeg_custom_size_sfh(ffmpeg_codec_data *data); - #endif #endif/*_FFMPEG_DECODER_UTILS_*/ diff --git a/src/coding/ffmpeg_decoder_utils_ea_xma.c b/src/coding/ffmpeg_decoder_utils_ea_xma.c deleted file mode 100644 index a9729d36..00000000 --- a/src/coding/ffmpeg_decoder_utils_ea_xma.c +++ /dev/null @@ -1,267 +0,0 @@ -#include "coding.h" -#include "ffmpeg_decoder_utils.h" - -#ifdef VGM_USE_FFMPEG - -#define EAXMA_XMA_MAX_PACKETS_PER_SNS_BLOCK 4 /* normally max 3 (Dante's Inferno), ~14 (1 stream) in Burnout Paradise */ -#define EAXMA_XMA_MAX_STREAMS_PER_SNS_BLOCK 4 /* XMA2 max is 8ch = 4 * 2ch */ -#define EAXMA_XMA_PACKET_SIZE 0x800 -#define EAXMA_XMA_BUFFER_SIZE (EAXMA_XMA_MAX_PACKETS_PER_SNS_BLOCK * EAXMA_XMA_MAX_STREAMS_PER_SNS_BLOCK * EAXMA_XMA_PACKET_SIZE) - -/** - * EA-XMA is XMA2 with padding removed (so a real 0x450 block would be padded to a virtual 0x800 block). - * Each EA-XMA SNS block contains 1~3 packets per stream, and multistream uses fully separate streams - * (no packet_skip set). We'll pad and reinterleave packets so it resembles standard XMA2. - * - * XMA2 data layout (XMA1 is the same but doesn't use blocks, they are only for seeking): - * - frames (containing 1..4 subframes): decode into 128*4 samples - * - packets: size 0x800, containing N frames (last frame can spill into next packet), must be padded - * - blocks: fixed size, containing N packets (last packet's frames won't spill into next block) - * - stream: N interleaved packets (1/2ch) for multichannel (Nch) audio. Interleave is not fixed: - * at file start/new block has one packet per stream, then must follow the "packet_skip" value - * in the XMA packet header to find its next packet (skiping packets from other streams). - * ex.: s1_p1 skip1, s2_p1 skip2, s1_p2 skip0 s1_p3 skip1, s2_p2 skip1, s1_p4... - */ - -static int get_block_max_packets(int num_streams, off_t packets_offset, STREAMFILE * streamfile); - - -int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) { - uint8_t v_buf[EAXMA_XMA_BUFFER_SIZE]; /* intermediate buffer, could be simplified */ - int buf_done = 0; - uint64_t real_offset = data->real_offset; - uint64_t virtual_offset = data->virtual_offset - data->header_size; - uint64_t virtual_base = data->virtual_base; - /* EA-XMA always uses late XMA2 streams (2ch + ... + 1/2ch) */ - int num_streams = (data->config.channels / 2) + (data->config.channels % 2 ? 1 : 0); - - - /* read and transform SNS/EA-XMA blocks into XMA packets */ - while (buf_done < buf_size) { - int s, p, bytes_to_copy, max_packets; - size_t block_size, data_size = 0, gap_size = 0; - uint32_t block_flag; - off_t packets_offset; - - block_flag = (uint8_t)read_8bit(real_offset+0x00,data->streamfile); - block_size = read_32bitBE(real_offset+0x00,data->streamfile) & 0x00FFFFFF; - packets_offset = real_offset + 0x08; /* 0x04(4): decoded samples */ - - if (block_flag == 0x45) /* exit on last block just in case, though should reach real_size */ - break; - - max_packets = get_block_max_packets(num_streams, packets_offset, data->streamfile); - if (max_packets == 0) goto fail; - - if (max_packets * num_streams * EAXMA_XMA_PACKET_SIZE > EAXMA_XMA_BUFFER_SIZE) { - VGM_LOG("EA XMA: block too big (%i * %i * 0x%x = 0x%x vs max 0x%x) at %lx\n", - max_packets,num_streams,EAXMA_XMA_PACKET_SIZE, max_packets*num_streams*EAXMA_XMA_PACKET_SIZE, EAXMA_XMA_BUFFER_SIZE,(off_t)real_offset); - goto fail; - } - - /* data is divided into a sub-block per stream (N packets), can be smaller than block_size (= has padding) - * copy XMA data re-interleaving for multichannel. To simplify some calcs fills the same number of packets - * per stream and adjusts packet headers (see above for XMA2 multichannel layout). */ - //to-do this doesn't make correct blocks sizes (but blocks are not needed to decode) - for (s = 0; s < num_streams; s++) { - size_t packets_size; - size_t packets_size4 = read_32bitBE(packets_offset, data->streamfile); /* size * 4, no idea */ - - packets_size = (packets_size4 / 4) - 0x04; - - /* Re-interleave all packets in order, one per stream. If one stream has more packets than - * others we add empty packets to keep the same number for all, avoiding packet_skip calcs */ - for (p = 0; p < max_packets; p++) { - off_t packet_offset = packets_offset + 0x04 + p * EAXMA_XMA_PACKET_SIZE; /* can be off but will copy 0 */ - off_t v_buf_offset = p * EAXMA_XMA_PACKET_SIZE * num_streams + s * EAXMA_XMA_PACKET_SIZE; - size_t packet_to_do = packets_size - p * EAXMA_XMA_PACKET_SIZE; - size_t extra_size = 0; - uint32_t header; - - if (packets_size < p * EAXMA_XMA_PACKET_SIZE) - packet_to_do = 0; /* empty packet */ - else if (packet_to_do > EAXMA_XMA_PACKET_SIZE) - packet_to_do = EAXMA_XMA_PACKET_SIZE; - - /* padding will be full size if packet_to_do is 0 */ - if (packet_to_do < EAXMA_XMA_PACKET_SIZE) - extra_size = EAXMA_XMA_PACKET_SIZE - (packet_to_do % EAXMA_XMA_PACKET_SIZE); - - /* copy data (or fully pad if empty packet) */ - read_streamfile(v_buf + v_buf_offset, packet_offset, packet_to_do, data->streamfile); - memset(v_buf + v_buf_offset + packet_to_do, 0xFF, extra_size); /* add padding, typically 0xFF */ - - /* rewrite packet header to add packet skips for multichannel (EA XMA streams are fully separate and have none) - * header bits: 6=num_frames, 15=first_frame_bits_offset, 3=metadata, 8=packet_skip */ - if (packet_to_do == 0) - header = 0x3FFF800; /* new empty packet header (0 num_frames, first_frame_bits_offset set to max) */ - else - header = (uint32_t)read_32bitBE(packet_offset, data->streamfile); - - /* get base header + change packet_skip since we know interleave is always 1 packet per stream */ - header = (header & 0xFFFFFF00) | ((header & 0x000000FF) + num_streams - 1); - put_32bitBE(v_buf + v_buf_offset, header); - } - - packets_offset += (packets_size4 / 4); - } - - if (buf_done == 0) /* first read */ - gap_size = virtual_offset - virtual_base; /* might start a few bytes into the XMA */ - - data_size = max_packets * num_streams * EAXMA_XMA_PACKET_SIZE; - - bytes_to_copy = data_size - gap_size; - if (bytes_to_copy > buf_size - buf_done) - bytes_to_copy = buf_size - buf_done; - - /* pad + copy */ - memcpy(buf + buf_done, v_buf + gap_size, bytes_to_copy); - buf_done += bytes_to_copy; - - /* move when block is fully done */ - if (data_size == bytes_to_copy + gap_size) { - real_offset += block_size; - virtual_base += data_size; - } - - if (block_flag == 0x80) /* exit on last block just in case, though should reach real_size */ - break; - } - - - data->real_offset = real_offset; - data->virtual_base = virtual_base; - return buf_size; - -fail: - return 0; -} - -int64_t ffmpeg_custom_seek_eaxma(ffmpeg_codec_data *data, int64_t virtual_offset) { - int64_t real_offset, virtual_base; - int64_t current_virtual_offset = data->virtual_offset - data->header_size; - int64_t seek_virtual_offset = virtual_offset - data->header_size; - - /* Find SNS block start closest to offset. ie. virtual_offset 0x1A10 could mean SNS blocks - * of 0x456+0x820 padded to 0x800+0x1000 (base) + 0x210 (extra for reads), thus real_offset = 0xC76 */ - - if (seek_virtual_offset > current_virtual_offset) { /* seek after current: start from current block */ - real_offset = data->real_offset; - virtual_base = data->virtual_base; - } - else { /* seek before current: start from the beginning */ - real_offset = data->real_start; - virtual_base = 0; - } - - - /* find target block */ - while (virtual_base < seek_virtual_offset) { - size_t block_size, data_size, extra_size = 0; - uint32_t block_flag; - - block_flag = (uint8_t)read_8bit(real_offset+0x00,data->streamfile); - block_size = read_32bitBE(real_offset+0x00,data->streamfile) & 0x00FFFFFF; - - if (block_flag == 0x45) /* exit on last block just in case (v1/SPS, empty) */ - break; - - data_size = block_size - 0x0c; - if (data_size % EAXMA_XMA_PACKET_SIZE) - extra_size = EAXMA_XMA_PACKET_SIZE - (data_size % EAXMA_XMA_PACKET_SIZE); - - /* stop if virtual_offset lands inside current block */ - if (data_size + extra_size > seek_virtual_offset) - break; - - real_offset += block_size; - virtual_base += data_size + extra_size; - - if (block_flag == 0x80) /* exit on last block just in case (v0/SNS, full) */ - break; - } - - /* closest we can use for reads */ - data->real_offset = real_offset; - data->virtual_base = virtual_base; - - return virtual_offset; -} - -int64_t ffmpeg_custom_size_eaxma(ffmpeg_codec_data *data) { - - uint64_t virtual_size = data->config.virtual_size; - if (!virtual_size) - return 0; - - return virtual_size + data->header_size; -} - -/* needed to know in meta for fake RIFF */ -size_t ffmpeg_get_eaxma_virtual_size(int channels, int streamed, off_t real_offset, size_t real_size, STREAMFILE *streamFile) { - size_t virtual_size = 0; - size_t real_end_offset = real_offset + real_size; - /* EA-XMA always uses late XMA2 streams (2ch + ... + 1/2ch) */ - int num_streams = (channels / 2) + (channels % 2 ? 1 : 0); - - - /* count all SNS/EAXMA blocks size + padding size */ - while (real_offset < real_end_offset) { - int max_packets; - uint32_t block_flag, block_size; - off_t packets_offset; - - block_flag = (uint8_t)read_8bit(real_offset+0x00,streamFile); - block_size = read_32bitBE(real_offset+0x00,streamFile) & 0x00FFFFFF; - packets_offset = real_offset + 0x08; /* 0x04(4): decoded samples */ - - if (block_flag == 0x45) /* exit on last block just in case (v1/SPS, empty) */ - break; - - max_packets = get_block_max_packets(num_streams, packets_offset, streamFile); - if (max_packets == 0) goto fail; - - /* fixed data_size per block for multichannel, see reads */ - virtual_size += max_packets * num_streams * EAXMA_XMA_PACKET_SIZE; - - real_offset += block_size; - - if (!streamed || block_flag == 0x80) /* exit on last block just in case (v0/SNS, full) */ - break; - } - - return virtual_size; - -fail: - return 0; -} - -/* a block can have N streams each with a varying number of packets, get max */ -static int get_block_max_packets(int num_streams, off_t packets_offset, STREAMFILE * streamfile) { - int s; - int max_packets = 0; - - for (s = 0; s < num_streams; s++) { - size_t packets_size; - size_t packets_size4 = read_32bitBE(packets_offset, streamfile); /* size * 4, no idea */ - int num_packets; - - if (packets_size4 == 0) { - VGM_LOG("EA XMA: null packets in stream %i at %lx\n", s, (off_t)packets_offset); - goto fail; - } - packets_size = (packets_size4 / 4) - 0x04; - - num_packets = (int)(packets_size / EAXMA_XMA_PACKET_SIZE) + 1; - if (num_packets > max_packets) - max_packets = num_packets; - } - - return max_packets; - -fail: - return 0; -} - -#endif diff --git a/src/coding/mtaf_decoder.c b/src/coding/mtaf_decoder.c index 642aa34d..322cd0d1 100644 --- a/src/coding/mtaf_decoder.c +++ b/src/coding/mtaf_decoder.c @@ -125,7 +125,7 @@ static void mtaf_block_update(VGMSTREAMCHANNEL * stream) { } #endif -void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int channels) { +void decode_mtaf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int32_t sample_count; int i; int c = channel%2; /* global channel to track channel */ diff --git a/src/coding/pcm_decoder.c b/src/coding/pcm_decoder.c index 406f0dff..d958d4fd 100644 --- a/src/coding/pcm_decoder.c +++ b/src/coding/pcm_decoder.c @@ -2,7 +2,7 @@ #include "../util.h" #include -void decode_pcm16LE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_pcm16le(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i; int32_t sample_count; @@ -11,7 +11,7 @@ void decode_pcm16LE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci } } -void decode_pcm16BE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_pcm16be(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i; int32_t sample_count; @@ -20,6 +20,15 @@ void decode_pcm16BE(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci } } +void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian) { + int i, sample_count; + int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; + + for (i=first_sample,sample_count=0; ioffset+i*2*channelspacing,stream->streamfile); + } +} + void decode_pcm8(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i; int32_t sample_count; @@ -38,14 +47,13 @@ void decode_pcm8_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac } } -void decode_pcm8_sb_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i; int32_t sample_count; for (i=first_sample,sample_count=0; ioffset+i*channelspacing,stream->streamfile); - if (v&0x80) v = 0-(v&0x7f); - outbuf[sample_count] = v*0x100; + int16_t v = (uint8_t)read_8bit(stream->offset+i,stream->streamfile); + outbuf[sample_count] = v*0x100 - 0x8000; } } @@ -59,22 +67,14 @@ void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int ch } } -void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { +void decode_pcm8_sb(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { int i; int32_t sample_count; for (i=first_sample,sample_count=0; ioffset+i,stream->streamfile); - outbuf[sample_count] = v*0x100 - 0x8000; - } -} - -void decode_pcm16_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian) { - int i, sample_count; - int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE; - - for (i=first_sample,sample_count=0; ioffset+i*2*channelspacing,stream->streamfile); + if (v&0x80) v = 0-(v&0x7f); + outbuf[sample_count] = v*0x100; } } diff --git a/src/coding/psx_decoder.c b/src/coding/psx_decoder.c index 63bae82e..8cdf4f7c 100644 --- a/src/coding/psx_decoder.c +++ b/src/coding/psx_decoder.c @@ -40,16 +40,6 @@ static const int ps_adpcm_coefs_i[5][2] = { * Some official PC tools decode using float coefs (from the spec), as does this code, but * consoles/games/libs would vary (PS1 could do it in hardware using BRR/XA's logic, FMOD/PS3 * may use int math in software, etc). There are inaudible rounding diffs between implementations. - * - * Optional bit flag combinations in the header control the SPU: - * 0x0 (0000): Nothing - * 0x1 (0001): End marker + decode - * 0x2 (0010): Loop region - * 0x3 (0011): Loop end - * 0x4 (0100): Start marker - * 0x6 (0110): Loop start - * 0x7 (0111): End marker + don't decode - * 0x5/8+ (1NNN): Not valid */ /* standard PS-ADPCM (float math version) */ @@ -162,6 +152,122 @@ void decode_psx_configurable(VGMSTREAMCHANNEL * stream, sample * outbuf, int cha stream->adpcm_history2_32 = hist2; } + +/* Find loop samples in PS-ADPCM data and return if the file loops. + * + * PS-ADPCM/VAG has optional bit flags that control looping in the SPU. + * Possible combinations (as usually defined in Sony's docs): + * - 0x0 (0000): Normal decode + * - 0x1 (0001): End marker (last frame) + * - 0x2 (0010): Loop region (marks files that *may* have loop flags somewhere) + * - 0x3 (0011): Loop end (jump to loop address) + * - 0x4 (0100): Start marker + * - 0x5 (0101): Same as 0x07? Extremely rare [Blood Omen: Legacy of Kain (PS1)] + * - 0x6 (0110): Loop start (save loop address) + * - 0x7 (0111): End marker and don't decode + * - 0x8+(1NNN): Not valid + */ +static int ps_find_loop_offsets_internal(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end, int config) { + int num_samples = 0, loop_start = 0, loop_end = 0; + int loop_start_found = 0, loop_end_found = 0; + off_t offset = start_offset; + off_t max_offset = start_offset + data_size; + size_t interleave_consumed = 0; + int detect_full_loops = config & 1; + + + while (offset < max_offset) { + uint8_t flag = (uint8_t)read_8bit(offset+0x01,streamFile) & 0x0F; /* lower nibble only (for HEVAG) */ + + /* theoretically possible and would use last 0x06 */ + VGM_ASSERT_ONCE(loop_start_found && flag == 0x06, "PS LOOPS: multiple loop start found at %lx\n", offset); + + if (flag == 0x06 && !loop_start_found) { + loop_start = num_samples; /* loop start before this frame */ + loop_start_found = 1; + } + + if (flag == 0x03 && !loop_end) { + loop_end = num_samples + 28; /* loop end after this frame */ + loop_end_found = 1; + + /* ignore strange case in Commandos (PS2), has many loop starts and ends */ + if (channels == 1 + && offset + 0x10 < max_offset + && ((uint8_t)read_8bit(offset+0x11,streamFile) & 0x0F) == 0x06) { + loop_end = 0; + loop_end_found = 0; + } + + if (loop_start_found && loop_end_found) + break; + } + + /* hack for some games that don't have loop points but do full loops, + * if there is a "partial" 0x07 end flag pretend it wants to loop + * (sometimes this will loop non-looping tracks, and won't loop all repeating files) */ + if (flag == 0x01 && detect_full_loops) { + static const uint8_t eof1[0x10] = {0x00,0x07,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77}; /* common */ + static const uint8_t eof2[0x10] = {0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + //static const uint8_t eofx[0x10] = {0x07,0x00,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77}; /* sometimes loops */ + //static const uint8_t eofx[0x10] = {0xNN,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; /* sometimes loops */ + uint8_t buf[0x10]; + + int read = read_streamfile(buf,offset+0x10,0x10,streamFile); + + if (read > 0 + /* also test some extra stuff */ + && buf[0] != 0x00 /* skip padding */ + && buf[0] != 0x0c + && buf[0] != 0x3c /* skip Ecco the Dolphin (PS2), Ratchet & Clank 2 (PS2), lame hack */ + ) { + + /* assume full loop if there isn't an EOF tag after current frame */ + if (memcmp(buf,eof1,0x10) != 0 && memcmp(buf,eof2,0x10) != 0) { + loop_start = 28; /* skip first frame as it's null in PS-ADPCM */ + loop_end = num_samples + 28; /* loop end after this frame */ + loop_start_found = 1; + loop_end_found = 1; + //;VGM_LOG("PS LOOPS: full loop found\n"); + break; + } + } + } + + + num_samples += 28; + offset += 0x10; + + /* skip other channels */ + interleave_consumed += 0x10; + if (interleave_consumed == interleave) { + interleave_consumed = 0; + offset += interleave*(channels - 1); + } + } + + VGM_ASSERT(loop_start_found && !loop_end_found, "PS LOOPS: found loop start but not loop end\n"); + VGM_ASSERT(loop_end_found && !loop_start_found, "PS LOOPS: found loop end but not loop start\n"); + //;VGM_LOG("PS LOOPS: start=%i, end=%i\n", loop_start, loop_end); + + /* From Sony's docs: if only loop_end is set loop back to "phoneme region start", but in practice doesn't */ + if (loop_start_found && loop_end_found) { + *out_loop_start = loop_start; + *out_loop_end = loop_end; + return 1; + } + + return 0; /* no loop */ +} + +int ps_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end) { + return ps_find_loop_offsets_internal(streamFile, start_offset, data_size, channels, interleave, out_loop_start, out_loop_end, 0); +} + +int ps_find_loop_offsets_full(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int32_t * out_loop_start, int32_t * out_loop_end) { + return ps_find_loop_offsets_internal(streamFile, start_offset, data_size, channels, interleave, out_loop_start, out_loop_end, 1); +} + size_t ps_bytes_to_samples(size_t bytes, int channels) { return bytes / channels / 0x10 * 28; } @@ -169,3 +275,4 @@ size_t ps_bytes_to_samples(size_t bytes, int channels) { size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels) { return bytes / channels / frame_size * 28; } + diff --git a/src/formats.c b/src/formats.c index 872cdb3b..ea243db4 100644 --- a/src/formats.c +++ b/src/formats.c @@ -79,9 +79,7 @@ static const char* extension_list[] = { "bik", "bika", "bik2", - "bik2a", "bk2", - "bk2a", "bmdx", "bms", "bnk", @@ -108,6 +106,7 @@ static const char* extension_list[] = { "cxs", "da", + "dax", "dbm", "dcs", "ddsp", @@ -216,6 +215,7 @@ static const char* extension_list[] = { "mihb", "mnstr", "mogg", + //"mp3", //common //"mp4", //common //"mpc", //FFmpeg, not parsed (musepack) //common "mpdsp", @@ -497,7 +497,7 @@ static const coding_info coding_info_list[] = { {coding_PCM8_int, "8-bit PCM with 1 byte interleave (block)"}, {coding_PCM8_U, "8-bit unsigned PCM"}, {coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave (block)"}, - {coding_PCM8_SB_int, "8-bit PCM with sign bit, 1 byte interleave (block)"}, + {coding_PCM8_SB, "8-bit PCM with sign bit"}, {coding_ULAW, "8-bit u-Law"}, {coding_ULAW_int, "8-bit u-Law with 1 byte interleave (block)"}, {coding_ALAW, "8-bit a-Law"}, @@ -616,7 +616,7 @@ static const coding_info coding_info_list[] = { }; static const layout_info layout_info_list[] = { - {layout_none, "flat (no layout)"}, + {layout_none, "flat"}, {layout_interleave, "interleave"}, {layout_segmented, "segmented"}, @@ -672,8 +672,9 @@ static const meta_info meta_info_list[] = { {meta_AIX, "CRI AIX header"}, {meta_AAX, "CRI AAX header"}, {meta_UTF_DSP, "CRI ADPCM_WII header"}, - {meta_DSP_AGSC, "Retro Studios AGSC header"}, - {meta_DSP_CSMP, "Retro Studios CSMP header"}, + {meta_AGSC, "Retro Studios AGSC header"}, + {meta_CSMP, "Retro Studios CSMP header"}, + {meta_RFRM, "Retro Studios RFRM header"}, {meta_NGC_ADPDTK, "Nintendo ADP raw header"}, {meta_RSF, "Retro Studios RSF raw header"}, {meta_AFC, "Nintendo AFC header"}, @@ -697,7 +698,7 @@ static const meta_info meta_info_list[] = { {meta_DSP_STM, "Nintendo STM header"}, {meta_PS2_EXST, "Sony EXST header"}, {meta_PS2_SVAG, "Konami SVAG header"}, - {meta_PS2_MIB, "Headerless/MIB PS-ADPCM raw header"}, + {meta_PS_HEADERLESS, "Headerless PS-ADPCM raw header"}, {meta_PS2_MIB_MIH, "Sony MultiStream MIH+MIB header"}, {meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"}, {meta_PS2_MIC, "assume KOEI MIC file by .mic extension"}, @@ -707,11 +708,9 @@ static const meta_info meta_info_list[] = { {meta_IDSP_TT, "Traveller's Tales IDSP header"}, {meta_RSTM_SPM, "Nintendo RSTM header (brstmspm)"}, {meta_RAW, "assumed RAW PCM file by .raw extension"}, - {meta_PS2_VAGi, "Sony VAG Interleaved header (VAGi)"}, - {meta_PS2_VAGp, "Sony VAG Mono header (VAGp)"}, - {meta_PS2_VAGs, "Sony VAG Stereo header (VAGp)"}, - {meta_PS2_VAGm, "Sony VAG Mono header (VAGm)"}, - {meta_PS2_pGAV, "Sony VAG Stereo Little Endian header (pGAV)"}, + {meta_PS2_VAGi, "Sony VAGi header"}, + {meta_PS2_VAGp, "Sony VAGp header"}, + {meta_PS2_pGAV, "Sony pGAV header"}, {meta_PSX_GMS, "assumed Grandia GMS file by .gms extension"}, {meta_STR_WAV, "Blitz Games STR+WAV header"}, {meta_PS2_ILD, "ILD header"}, @@ -823,7 +822,7 @@ static const meta_info meta_info_list[] = { {meta_NAOMI_SPSD, "Naomi SPSD header"}, {meta_FFXI_BGW, "BGW BGMStream header"}, {meta_FFXI_SPW, "SPW SeWave header"}, - {meta_PS2_ASS, "ASS Header"}, + {meta_PS2_ASS, "SystemSoft .ASS header"}, {meta_NUB_IDSP, "Namco NUB IDSP header"}, {meta_IDSP_NL, "Next Level IDSP header"}, {meta_IDSP_IE, "Inevitable Entertainment IDSP Header"}, @@ -953,10 +952,9 @@ static const meta_info meta_info_list[] = { {meta_OTNS_ADP, "Omikron: The Nomad Soul ADP header"}, {meta_EB_SFX, "Excitebots .sfx header"}, {meta_EB_SF0, "assumed Excitebots .sf0 by extension"}, - {meta_PS3_KLBS, "klBS Header"}, {meta_PS2_MTAF, "Konami MTAF header"}, - {meta_PS2_VAG1, "Konami VAG Mono header (VAG1)"}, - {meta_PS2_VAG2, "Konami VAG Stereo header (VAG2)"}, + {meta_PS2_VAG1, "Konami VAG1 header"}, + {meta_PS2_VAG2, "Konami VAG2 header"}, {meta_TUN, "Lego Racers ALP header"}, {meta_WPD, "WPD 'DPW' header"}, {meta_MN_STR, "Mini Ninjas 'STR' header"}, diff --git a/src/layout/Makefile.autotools.am b/src/layout/Makefile.autotools.am index 9d0ea898..cd877881 100644 --- a/src/layout/Makefile.autotools.am +++ b/src/layout/Makefile.autotools.am @@ -9,3 +9,12 @@ AM_MAKEFLAGS=-f Makefile.autotools liblayout_la_LDFLAGS = liblayout_la_SOURCES = (auto-updated) EXTRA_DIST = (auto-updated) + +if HAVE_VORBIS +if HAVE_VORBISFILE +AM_CFLAGS += -DVGM_USE_VORBIS +endif +endif +if HAVE_LIBMPG123 +AM_CFLAGS += -DVGM_USE_MPEG +endif diff --git a/src/layout/blocked.c b/src/layout/blocked.c index 728969e0..186275a1 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -1,16 +1,18 @@ #include "layout.h" #include "../vgmstream.h" -static void block_update(VGMSTREAM * vgmstream); +/* Decodes samples for blocked streams. + * Data is divided into headered blocks with a bunch of data. The layout calls external helper functions + * when a block is decoded, and those must parse the new block and move offsets accordingly. */ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; + int samples_written = 0; + int frame_size, samples_per_frame, samples_this_block; - int frame_size = get_vgmstream_frame_size(vgmstream); - int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - int samples_this_block; + frame_size = get_vgmstream_frame_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + samples_this_block = 0; - /* get samples in the current block */ if (vgmstream->current_block_samples) { samples_this_block = vgmstream->current_block_samples; } else if (frame_size == 0) { /* assume 4 bit */ //TODO: get_vgmstream_frame_size() really should return bits... */ @@ -19,12 +21,13 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame; } - /* decode all samples */ + while (samples_written < sample_count) { int samples_to_do; + if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { - /* on loop those values are changed */ + /* handle looping, readjust back to loop start values */ if (vgmstream->current_block_samples) { samples_this_block = vgmstream->current_block_samples; } else if (frame_size == 0) { /* assume 4 bit */ //TODO: get_vgmstream_frame_size() really should return bits... */ @@ -35,26 +38,26 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * continue; } - /* probably block bug or EOF, next calcs would give wrong values and buffer segfaults */ if (samples_this_block < 0) { + /* probably block bug or EOF, next calcs would give wrong values/segfaults/infinite loop */ VGM_LOG("layout_blocked: wrong block samples at 0x%lx\n", vgmstream->current_block_offset); memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); - break; /* probable infinite loop otherwise */ + break; } - /* probably block bug or EOF, block functions won't be able to read anything useful */ if (vgmstream->current_block_offset < 0 || vgmstream->current_block_offset == 0xFFFFFFFF) { + /* probably block bug or EOF, block functions won't be able to read anything useful/infinite loop */ VGM_LOG("layout_blocked: wrong block offset found\n"); memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); - break; /* probable infinite loop otherwise */ + break; } samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - if (samples_written + samples_to_do > sample_count) + if (samples_to_do > sample_count - samples_written) samples_to_do = sample_count - samples_written; - /* samples_this_block = 0 is allowed (empty block, do nothing then move to next block) */ if (samples_to_do > 0) { + /* samples_this_block = 0 is allowed (empty block, do nothing then move to next block) */ decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); } @@ -64,15 +67,13 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * /* move to next block when all samples are consumed */ - if (vgmstream->samples_into_block==samples_this_block - /*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */ - block_update(vgmstream); + if (vgmstream->samples_into_block == samples_this_block + /*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */ //todo + block_update(vgmstream->next_block_offset,vgmstream); - /* for VBR these may change */ + /* update since these may change each block */ frame_size = get_vgmstream_frame_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - - /* get samples in the current block */ if (vgmstream->current_block_samples) { samples_this_block = vgmstream->current_block_samples; } else if (frame_size == 0) { /* assume 4 bit */ //TODO: get_vgmstream_frame_size() really should return bits... */ @@ -87,124 +88,124 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * } } - -static void block_update(VGMSTREAM * vgmstream) { +/* helper functions to parse new block */ +void block_update(off_t block_offset, VGMSTREAM * vgmstream) { switch (vgmstream->layout_type) { case layout_blocked_ast: - block_update_ast(vgmstream->next_block_offset,vgmstream); + block_update_ast(block_offset,vgmstream); break; case layout_blocked_mxch: - block_update_mxch(vgmstream->next_block_offset,vgmstream); + block_update_mxch(block_offset,vgmstream); break; case layout_blocked_halpst: - block_update_halpst(vgmstream->next_block_offset,vgmstream); + block_update_halpst(block_offset,vgmstream); break; case layout_blocked_xa: - block_update_xa(vgmstream->next_block_offset,vgmstream); + block_update_xa(block_offset,vgmstream); break; case layout_blocked_ea_schl: - block_update_ea_schl(vgmstream->next_block_offset,vgmstream); + block_update_ea_schl(block_offset,vgmstream); break; case layout_blocked_ea_1snh: - block_update_ea_1snh(vgmstream->next_block_offset,vgmstream); + block_update_ea_1snh(block_offset,vgmstream); break; case layout_blocked_caf: - block_update_caf(vgmstream->next_block_offset,vgmstream); + block_update_caf(block_offset,vgmstream); break; case layout_blocked_wsi: - block_update_wsi(vgmstream->next_block_offset,vgmstream); + block_update_wsi(block_offset,vgmstream); break; case layout_blocked_str_snds: - block_update_str_snds(vgmstream->next_block_offset,vgmstream); + block_update_str_snds(block_offset,vgmstream); break; case layout_blocked_ws_aud: - block_update_ws_aud(vgmstream->next_block_offset,vgmstream); + block_update_ws_aud(block_offset,vgmstream); break; case layout_blocked_matx: - block_update_matx(vgmstream->next_block_offset,vgmstream); + block_update_matx(block_offset,vgmstream); break; case layout_blocked_dec: - block_update_dec(vgmstream->next_block_offset,vgmstream); + block_update_dec(block_offset,vgmstream); break; case layout_blocked_emff_ps2: - block_update_emff_ps2(vgmstream->next_block_offset,vgmstream); + block_update_emff_ps2(block_offset,vgmstream); break; case layout_blocked_emff_ngc: - block_update_emff_ngc(vgmstream->next_block_offset,vgmstream); + block_update_emff_ngc(block_offset,vgmstream); break; case layout_blocked_gsb: - block_update_gsb(vgmstream->next_block_offset,vgmstream); + block_update_gsb(block_offset,vgmstream); break; case layout_blocked_vs: - block_update_vs(vgmstream->next_block_offset,vgmstream); + block_update_vs(block_offset,vgmstream); break; case layout_blocked_xvas: - block_update_xvas(vgmstream->next_block_offset,vgmstream); + block_update_xvas(block_offset,vgmstream); break; case layout_blocked_thp: - block_update_thp(vgmstream->next_block_offset,vgmstream); + block_update_thp(block_offset,vgmstream); break; case layout_blocked_filp: - block_update_filp(vgmstream->next_block_offset,vgmstream); + block_update_filp(block_offset,vgmstream); break; case layout_blocked_ivaud: - block_update_ivaud(vgmstream->next_block_offset,vgmstream); + block_update_ivaud(block_offset,vgmstream); break; case layout_blocked_ea_swvr: - block_update_ea_swvr(vgmstream->next_block_offset,vgmstream); + block_update_ea_swvr(block_offset,vgmstream); break; case layout_blocked_adm: - block_update_adm(vgmstream->next_block_offset,vgmstream); + block_update_adm(block_offset,vgmstream); break; case layout_blocked_bdsp: - block_update_bdsp(vgmstream->next_block_offset,vgmstream); + block_update_bdsp(block_offset,vgmstream); break; case layout_blocked_tra: - block_update_tra(vgmstream->next_block_offset,vgmstream); + block_update_tra(block_offset,vgmstream); break; case layout_blocked_ps2_iab: - block_update_ps2_iab(vgmstream->next_block_offset,vgmstream); + block_update_ps2_iab(block_offset,vgmstream); break; case layout_blocked_ps2_strlr: - block_update_ps2_strlr(vgmstream->next_block_offset,vgmstream); + block_update_ps2_strlr(block_offset,vgmstream); break; case layout_blocked_rws: - block_update_rws(vgmstream->next_block_offset,vgmstream); + block_update_rws(block_offset,vgmstream); break; case layout_blocked_hwas: - block_update_hwas(vgmstream->next_block_offset,vgmstream); + block_update_hwas(block_offset,vgmstream); break; case layout_blocked_ea_sns: - block_update_ea_sns(vgmstream->next_block_offset,vgmstream); + block_update_ea_sns(block_offset,vgmstream); break; case layout_blocked_awc: - block_update_awc(vgmstream->next_block_offset,vgmstream); + block_update_awc(block_offset,vgmstream); break; case layout_blocked_vgs: - block_update_vgs(vgmstream->next_block_offset,vgmstream); + block_update_vgs(block_offset,vgmstream); break; case layout_blocked_vawx: - block_update_vawx(vgmstream->next_block_offset,vgmstream); + block_update_vawx(block_offset,vgmstream); break; case layout_blocked_xvag_subsong: - block_update_xvag_subsong(vgmstream->next_block_offset,vgmstream); + block_update_xvag_subsong(block_offset,vgmstream); break; case layout_blocked_ea_wve_au00: - block_update_ea_wve_au00(vgmstream->next_block_offset,vgmstream); + block_update_ea_wve_au00(block_offset,vgmstream); break; case layout_blocked_ea_wve_ad10: - block_update_ea_wve_ad10(vgmstream->next_block_offset,vgmstream); + block_update_ea_wve_ad10(block_offset,vgmstream); break; case layout_blocked_sthd: - block_update_sthd(vgmstream->next_block_offset,vgmstream); + block_update_sthd(block_offset,vgmstream); break; case layout_blocked_h4m: - block_update_h4m(vgmstream->next_block_offset,vgmstream); + block_update_h4m(block_offset,vgmstream); break; case layout_blocked_xa_aiff: - block_update_xa_aiff(vgmstream->next_block_offset,vgmstream); + block_update_xa_aiff(block_offset,vgmstream); break; - default: + default: /* not a blocked layout */ break; } } diff --git a/src/layout/blocked_ast.c b/src/layout/blocked_ast.c index 8ff32ba0..3aab1668 100644 --- a/src/layout/blocked_ast.c +++ b/src/layout/blocked_ast.c @@ -1,18 +1,21 @@ #include "layout.h" #include "../vgmstream.h" -/* set up for the block at the given offset */ +/* simple headered blocks */ void block_update_ast(off_t block_offset, VGMSTREAM * vgmstream) { + STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; - vgmstream->current_block_offset = block_offset; - vgmstream->current_block_size = read_32bitBE( - vgmstream->current_block_offset+4, - vgmstream->ch[0].streamfile); - vgmstream->next_block_offset = vgmstream->current_block_offset + - vgmstream->current_block_size*vgmstream->channels + 0x20; + size_t block_data, header_size; - for (i=0;ichannels;i++) { - vgmstream->ch[i].offset = vgmstream->current_block_offset + - 0x20 + vgmstream->current_block_size*i; + /* 0x00: "BLCK", rest: null */ + block_data = read_32bitBE(block_offset+0x04,streamFile); + header_size = 0x20; + + vgmstream->current_block_offset = block_offset; + vgmstream->current_block_size = block_data; + vgmstream->next_block_offset = block_offset + block_data*vgmstream->channels + header_size; + + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = block_offset + header_size + block_data*i; } } diff --git a/src/layout/blocked_ea_1snh.c b/src/layout/blocked_ea_1snh.c index f57a5bcc..7df1c3a6 100644 --- a/src/layout/blocked_ea_1snh.c +++ b/src/layout/blocked_ea_1snh.c @@ -88,7 +88,7 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) { break; case coding_DVI_IMA: - if (vgmstream->codec_version == 1) { /* ADPCM hist */ + if (vgmstream->codec_config == 1) { /* ADPCM hist */ vgmstream->current_block_samples = read_32bit(block_offset + block_header, streamFile); vgmstream->current_block_size = 0; // - (0x04 + 0x08*vgmstream->channels); /* should be equivalent */ diff --git a/src/layout/blocked_ea_schl.c b/src/layout/blocked_ea_schl.c index 6293feb9..bfa07412 100644 --- a/src/layout/blocked_ea_schl.c +++ b/src/layout/blocked_ea_schl.c @@ -23,7 +23,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { { uint32_t block_id = read_32bitBE(block_offset+0x00,streamFile); - if (vgmstream->codec_version & 0x02) /* size is always LE, except in early SS/MAC */ + if (vgmstream->codec_config & 0x02) /* size is always LE, except in early SS/MAC */ block_size = read_32bitBE(block_offset + 0x04,streamFile); else block_size = read_32bitLE(block_offset + 0x04,streamFile); @@ -180,7 +180,7 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) { } /* read ADPCM history before each channel if needed (not actually read in sx.exe) */ - if (vgmstream->codec_version & 0x01) { + if (vgmstream->codec_config & 0x01) { for (i = 0; i < vgmstream->channels; i++) { //vgmstream->ch[i].adpcm_history1_32 = read_16bit(vgmstream->ch[i].offset+0x00,streamFile); //vgmstream->ch[i].adpcm_history3_32 = read_16bit(vgmstream->ch[i].offset+0x02,streamFile); diff --git a/src/layout/blocked_h4m.c b/src/layout/blocked_h4m.c index a328292e..f488d624 100644 --- a/src/layout/blocked_h4m.c +++ b/src/layout/blocked_h4m.c @@ -34,7 +34,7 @@ void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream) { uint32_t frame_samples = read_32bitBE(block_offset+0x08, streamFile); size_t block_skip; - if (vgmstream->codec_version & 0x80) { + if (vgmstream->codec_config & 0x80) { frame_samples /= 2; /* ??? */ } @@ -51,7 +51,7 @@ void block_update_h4m(off_t block_offset, VGMSTREAM * vgmstream) { VGM_ASSERT(frame_format == 1, "H4M: unknown frame_format %x at %lx\n", frame_format, block_offset); /* pass current mode to the decoder */ - vgmstream->codec_version = (frame_format << 8) | (vgmstream->codec_version & 0xFF); + vgmstream->codec_config = (frame_format << 8) | (vgmstream->codec_config & 0xFF); for (i = 0; i < vgmstream->channels; i++) { vgmstream->ch[i].offset = block_offset + block_skip; diff --git a/src/layout/blocked_wsi.c b/src/layout/blocked_wsi.c index 572c6cc1..6be576fb 100644 --- a/src/layout/blocked_wsi.c +++ b/src/layout/blocked_wsi.c @@ -6,6 +6,7 @@ void block_update_wsi(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* streamFile = vgmstream->ch[0].streamfile; int i; off_t channel_block_size; + //int is_first_offset = /* assume that all channels have the same size for this block */ @@ -18,4 +19,14 @@ void block_update_wsi(off_t block_offset, VGMSTREAM * vgmstream) { for (i = 0; i < vgmstream->channels; i++) { vgmstream->ch[i].offset = block_offset + channel_block_size*i + 0x10; } + + /* first block has DSP header, remove */ + if (block_offset == vgmstream->ch[0].channel_start_offset) { + int i; + + vgmstream->current_block_size -= 0x60; + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset += 0x60; + } + } } diff --git a/src/layout/flat.c b/src/layout/flat.c new file mode 100644 index 00000000..59f3ce03 --- /dev/null +++ b/src/layout/flat.c @@ -0,0 +1,39 @@ +#include "layout.h" +#include "../vgmstream.h" + + +/* Decodes samples for flat streams. + * Data forms a single stream, and the decoder may internally skip chunks and move offsets as needed. */ +void render_vgmstream_flat(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { + int samples_written = 0; + int samples_per_frame, samples_this_block; + + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + samples_this_block = vgmstream->num_samples; /* do all samples if possible */ + + + while (samples_written < sample_count) { + int samples_to_do; + + if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { + /* handle looping */ + continue; + } + + samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; + + if (samples_to_do == 0) { + VGM_LOG("layout_flat: wrong samples_to_do found\n"); + memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); + break; + } + + decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); + + samples_written += samples_to_do; + vgmstream->current_sample += samples_to_do; + vgmstream->samples_into_block += samples_to_do; + } +} diff --git a/src/layout/interleave.c b/src/layout/interleave.c index 6090d197..9e64fdfd 100644 --- a/src/layout/interleave.c +++ b/src/layout/interleave.c @@ -1,64 +1,92 @@ #include "layout.h" #include "../vgmstream.h" + +/* Decodes samples for interleaved streams. + * Data has interleaved chunks per channel, and once one is decoded the layout moves offsets, + * skipping other chunks (essentially a simplified variety of blocked layout). + * Incompatible with decoders that move offsets. */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; - - int frame_size = get_vgmstream_frame_size(vgmstream); - int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - int samples_this_block; + int samples_written = 0; + int frame_size, samples_per_frame, samples_this_block; + int has_interleave_last = vgmstream->interleave_last_block_size && vgmstream->channels > 1; + frame_size = get_vgmstream_frame_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; - if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 && - vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block> vgmstream->num_samples) { + if (has_interleave_last && + vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block > vgmstream->num_samples) { + /* adjust values again if inside last interleave */ frame_size = get_vgmstream_shortframe_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); - samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame; } - while (samples_writtenchannels == 1) + samples_this_block = vgmstream->num_samples; + + + while (samples_written < sample_count) { int samples_to_do; if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { - /* we assume that the loop is not back into a short block */ - if (vgmstream->interleave_last_block_size && vgmstream->channels > 1) { + /* handle looping, restore standard interleave sizes */ + if (has_interleave_last) { /* assumes that won't loop back into a interleave_last */ frame_size = get_vgmstream_frame_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; + if (samples_this_block == 0 && vgmstream->channels == 1) + samples_this_block = vgmstream->num_samples; } continue; } samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - /*printf("vgmstream_samples_to_do(samples_this_block=%d,samples_per_frame=%d,vgmstream) returns %d\n",samples_this_block,samples_per_frame,samples_to_do);*/ + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; - if (samples_written+samples_to_do > sample_count) - samples_to_do=sample_count-samples_written; + if (samples_to_do == 0) { /* happens when interleave is not set */ + VGM_LOG("layout_interleave: wrong samples_to_do found\n"); + memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); + break; + } decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); samples_written += samples_to_do; vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block+=samples_to_do; + vgmstream->samples_into_block += samples_to_do; - if (vgmstream->samples_into_block==samples_this_block) { - int chan; - if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 && - vgmstream->current_sample + samples_this_block > vgmstream->num_samples) { + + /* move to next interleaved block when all samples are consumed */ + if (vgmstream->samples_into_block == samples_this_block) { + int ch; + + if (has_interleave_last && + vgmstream->current_sample + samples_this_block > vgmstream->num_samples) { + /* adjust values again if inside last interleave */ frame_size = get_vgmstream_shortframe_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); - samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame; - for (chan=0;chanchannels;chan++) - vgmstream->ch[chan].offset+=vgmstream->interleave_block_size*(vgmstream->channels-chan)+vgmstream->interleave_last_block_size*chan; - } else { + if (samples_this_block == 0 && vgmstream->channels == 1) + samples_this_block = vgmstream->num_samples; - for (chan=0;chanchannels;chan++) - vgmstream->ch[chan].offset+=vgmstream->interleave_block_size*vgmstream->channels; + for (ch = 0; ch < vgmstream->channels; ch++) { + off_t skip = vgmstream->interleave_block_size*(vgmstream->channels-ch) + + vgmstream->interleave_last_block_size*ch;; + vgmstream->ch[ch].offset += skip; + } } - vgmstream->samples_into_block=0; + else { + for (ch = 0; ch < vgmstream->channels; ch++) { + off_t skip = vgmstream->interleave_block_size*vgmstream->channels; + vgmstream->ch[ch].offset += skip; + } + } + + vgmstream->samples_into_block = 0; } } diff --git a/src/layout/layered.c b/src/layout/layered.c index 624aa670..2dcae87b 100644 --- a/src/layout/layered.c +++ b/src/layout/layered.c @@ -1,37 +1,41 @@ #include "layout.h" #include "../vgmstream.h" -/* TODO: there must be a reasonable way to respect the loop settings, as - the substreams are in their own little world. - Currently the VGMSTREAMs layers loop internally and the external/base VGMSTREAM - doesn't actually loop, and would ignore any altered values/loop_flag. */ +/* NOTE: if loop settings change the layered vgmstreams must be notified (preferably using vgmstream_force_loop) */ #define LAYER_BUF_SIZE 512 #define LAYER_MAX_CHANNELS 6 /* at least 2, but let's be generous */ - +/* Decodes samples for layered streams. + * Similar to interleave layout, but decodec samples are mixed from complete vgmstreams, each + * with custom codecs and different number of channels, creating a single super-vgmstream. + * Usually combined with custom streamfiles to handle data interleaved in weird ways. */ void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - sample interleave_buf[LAYER_BUF_SIZE*LAYER_MAX_CHANNELS]; - int32_t samples_done = 0; + int samples_written = 0; layered_layout_data *data = vgmstream->layout_data; + sample interleave_buf[LAYER_BUF_SIZE*LAYER_MAX_CHANNELS]; - while (samples_done < sample_count) { - int32_t samples_to_do = LAYER_BUF_SIZE; + + while (samples_written < sample_count) { + int samples_to_do = LAYER_BUF_SIZE; int layer, ch = 0; - if (samples_to_do > sample_count - samples_done) - samples_to_do = sample_count - samples_done; + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; for (layer = 0; layer < data->layer_count; layer++) { - int s,l_ch; + int s, layer_ch; int layer_channels = data->layers[layer]->channels; + /* each layer will handle its own looping internally */ + render_vgmstream(interleave_buf, samples_to_do, data->layers[layer]); - for (l_ch = 0; l_ch < layer_channels; l_ch++) { + /* mix layer samples to main samples */ + for (layer_ch = 0; layer_ch < layer_channels; layer_ch++) { for (s = 0; s < samples_to_do; s++) { - size_t layer_sample = s*layer_channels + l_ch; - size_t buffer_sample = (samples_done+s)*vgmstream->channels + ch; + size_t layer_sample = s*layer_channels + layer_ch; + size_t buffer_sample = (samples_written+s)*vgmstream->channels + ch; buffer[buffer_sample] = interleave_buf[layer_sample]; } @@ -39,8 +43,9 @@ void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM * } } - - samples_done += samples_to_do; + samples_written += samples_to_do; + vgmstream->current_sample = data->layers[0]->current_sample; /* just in case it's used for info */ + //vgmstream->samples_into_block = 0; /* handled in each layer */ } } diff --git a/src/layout/layout.h b/src/layout/layout.h index bd17b738..f547d677 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -6,6 +6,7 @@ /* blocked layouts */ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void block_update(off_t block_offset, VGMSTREAM * vgmstream); void block_update_ast(off_t block_ofset, VGMSTREAM * vgmstream); void block_update_mxch(off_t block_ofset, VGMSTREAM * vgmstream); @@ -49,7 +50,7 @@ void block_update_xa_aiff(off_t block_offset, VGMSTREAM * vgmstream); /* other layouts */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); -void render_vgmstream_nolayout(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); +void render_vgmstream_flat(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); void render_vgmstream_aix(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); diff --git a/src/layout/nolayout.c b/src/layout/nolayout.c deleted file mode 100644 index f481a034..00000000 --- a/src/layout/nolayout.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "layout.h" -#include "../vgmstream.h" - -void render_vgmstream_nolayout(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; - - const int samples_this_block = vgmstream->num_samples; - int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - - while (samples_writtenloop_flag && vgmstream_do_loop(vgmstream)) { - continue; - } - - samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - - if (samples_written+samples_to_do > sample_count) - samples_to_do=sample_count-samples_written; - - if (!samples_to_do) { - memset(buffer + samples_written * vgmstream->channels, 0, sizeof(sample) * vgmstream->channels * (sample_count - samples_written)); - return; - } - - decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); - - samples_written += samples_to_do; - vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block+=samples_to_do; - } -} diff --git a/src/layout/segmented.c b/src/layout/segmented.c index 4f2b7073..ff87c68e 100644 --- a/src/layout/segmented.c +++ b/src/layout/segmented.c @@ -2,35 +2,54 @@ #include "../vgmstream.h" +/* Decodes samples for segmented streams. + * Chains together sequential vgmstreams, for data divided into separate sections or files + * (like one part for intro and other for loop segments, which may even use different codecs). */ void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; + int samples_written = 0; segmented_layout_data *data = vgmstream->layout_data; - //int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - while (samples_writtensegments[data->current_segment]->num_samples; + if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { - //todo can only loop in a segment start - // (for arbitrary values find loop segment from loop_start_sample, and skip N samples until loop start) - data->current_segment = data->loop_segment; + /* handle looping, finding loop segment */ + int loop_segment = 0, samples = 0, loop_samples_skip = 0; + while (samples < vgmstream->num_samples) { + int32_t segment_samples = data->segments[loop_segment]->num_samples; + if (vgmstream->loop_start_sample >= samples && vgmstream->loop_start_sample < samples + segment_samples) { + loop_samples_skip = vgmstream->loop_start_sample - samples; + break; /* loop_start falls within loop_segment's samples */ + } + samples += segment_samples; + loop_segment++; + } + if (loop_segment == data->segment_count) { + VGM_LOG("segmented_layout: can't find loop segment\n"); + loop_segment = 0; + } + if (loop_samples_skip > 0) { + VGM_LOG("segmented_layout: loop starts after %i samples\n", loop_samples_skip); + //todo skip/fix, but probably won't happen + } + data->current_segment = loop_segment; reset_vgmstream(data->segments[data->current_segment]); - vgmstream->samples_into_block = 0; continue; } - samples_to_do = vgmstream_samples_to_do(samples_this_block, 1, vgmstream); - - if (samples_written+samples_to_do > sample_count) - samples_to_do=sample_count-samples_written; + samples_to_do = vgmstream_samples_to_do(samples_this_block, sample_count, vgmstream); + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; + /* detect segment change and restart */ if (samples_to_do == 0) { data->current_segment++; reset_vgmstream(data->segments[data->current_segment]); - vgmstream->samples_into_block = 0; continue; } @@ -40,7 +59,7 @@ void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM samples_written += samples_to_do; vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block+=samples_to_do; + vgmstream->samples_into_block += samples_to_do; } } diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 81ae3a83..e345af4e 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -41,7 +41,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="../ext_includes" - PreprocessorDefinitions="WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_G719;USE_ALLOCA;_DEBUG;_LIB;" + PreprocessorDefinitions="WIN32;VGM_USE_VORBIS;VGM_USE_MPEG;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_G719;USE_ALLOCA;_DEBUG;_LIB;" MinimalRebuild="true" BasicRuntimeChecks="3" RuntimeLibrary="3" @@ -103,7 +103,7 @@ + + @@ -248,6 +252,10 @@ RelativePath=".\meta\ppst_streamfile.h" > + + @@ -255,6 +263,10 @@ + + + + + + @@ -963,7 +983,7 @@ > - - @@ -1182,10 +1198,14 @@ RelativePath=".\meta\raw.c" > - - + + + + @@ -1430,10 +1450,14 @@ RelativePath=".\meta\wpd.c" > - - + + + + @@ -1618,10 +1642,6 @@ RelativePath=".\coding\ffmpeg_decoder_utils_ea_schl.c" > - - @@ -1895,7 +1915,7 @@ > Disabled ../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories) - WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_G719;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;USE_ALLOCA;_DEBUG;_LIB;%(PreprocessorDefinitions) + 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) true EnableFastChecks MultiThreadedDebug @@ -73,7 +73,7 @@ ../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories) - _WIN32_WINNT=0x501;WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_G719;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;USE_ALLOCA;NDEBUG;_LIB;%(PreprocessorDefinitions) + _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) MultiThreaded @@ -102,10 +102,12 @@ + + - + @@ -123,7 +125,6 @@ - @@ -147,6 +148,7 @@ + @@ -167,7 +169,6 @@ - @@ -213,6 +214,7 @@ + @@ -341,7 +343,7 @@ - + @@ -385,6 +387,7 @@ + @@ -436,6 +439,7 @@ + @@ -522,7 +526,7 @@ - + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index 1c1bc65a..ea8d8e16 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -86,18 +86,27 @@ meta\Header Files + + meta\Header Files + meta\Header Files meta\Header Files + + meta\Header Files + meta\Header Files meta\Header Files + + meta\Header Files + meta\Header Files @@ -223,6 +232,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files @@ -589,7 +601,7 @@ meta\Source Files - + meta\Source Files @@ -721,6 +733,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files @@ -874,6 +889,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files @@ -1126,7 +1144,7 @@ layout\Source Files - + layout\Source Files @@ -1252,9 +1270,6 @@ meta\Source Files - - meta\Source Files - coding\Source Files @@ -1273,6 +1288,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files @@ -1336,9 +1354,6 @@ coding\Source Files - - coding\Source Files - coding\Source Files diff --git a/src/meta/Makefile.autotools.am b/src/meta/Makefile.autotools.am index 03a89813..c6081a67 100644 --- a/src/meta/Makefile.autotools.am +++ b/src/meta/Makefile.autotools.am @@ -9,3 +9,12 @@ AM_MAKEFLAGS=-f Makefile.autotools libmeta_la_LDFLAGS = libmeta_la_SOURCES = (auto-updated) EXTRA_DIST = (auto-updated) + +if HAVE_VORBIS +if HAVE_VORBISFILE +AM_CFLAGS += -DVGM_USE_VORBIS +endif +endif +if HAVE_LIBMPG123 +AM_CFLAGS += -DVGM_USE_MPEG +endif diff --git a/src/meta/aax.c b/src/meta/aax.c index 15df8ede..1ad4dbaa 100644 --- a/src/meta/aax.c +++ b/src/meta/aax.c @@ -15,7 +15,7 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { int loop_flag = 0, channel_count = 0; int32_t sample_count, loop_start_sample = 0, loop_end_sample = 0; - int segment_count, loop_segment = 0; + int segment_count; segmented_layout_data *data = NULL; int table_error = 0; @@ -102,7 +102,6 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { if (!loop_flag && segment_loop_flag) { loop_start_sample = sample_count; - loop_segment = i; } sample_count += data->segments[i]->num_samples; @@ -130,7 +129,6 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) { vgmstream->layout_type = layout_segmented; vgmstream->layout_data = data; - data->loop_segment = loop_segment; return vgmstream; diff --git a/src/meta/agsc.c b/src/meta/agsc.c index d2f55b42..e19922bc 100644 --- a/src/meta/agsc.c +++ b/src/meta/agsc.c @@ -42,7 +42,8 @@ VGMSTREAM * init_vgmstream_agsc(STREAMFILE *streamFile) { vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_DSP_AGSC; + vgmstream->meta_type = meta_AGSC; + vgmstream->allow_dual_stereo = 1; for (i=0;i<16;i++) { vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(header_offset+0xf6+i*2,streamFile); diff --git a/src/meta/aifc.c b/src/meta/aifc.c index 4c61c3c0..daf2663c 100644 --- a/src/meta/aifc.c +++ b/src/meta/aifc.c @@ -2,7 +2,7 @@ #include "../layout/layout.h" -/* for reading integers inexplicably packed into 80 bit floats */ +/* for reading integers inexplicably packed into 80-bit ('double extended') floats */ static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) { uint8_t buf[10]; int32_t exponent; @@ -286,10 +286,6 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - - if (vgmstream->layout_type == layout_blocked_xa_aiff) - block_update_xa_aiff(start_offset,vgmstream); - return vgmstream; fail: diff --git a/src/meta/ast.c b/src/meta/ast.c index fccf95db..ede13594 100644 --- a/src/meta/ast.c +++ b/src/meta/ast.c @@ -2,82 +2,60 @@ #include "../layout/layout.h" #include "../util.h" +/* .AST - from Nintendo games [Super Mario Galaxy (Wii), Pac-Man Vs (GC)] */ VGMSTREAM * init_vgmstream_ast(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - coding_t coding_type; - - int codec_number; - int channel_count; - int loop_flag; + off_t start_offset; + int loop_flag, channel_count, codec; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("ast",filename_extension(filename))) goto fail; - - /* check header */ - if ((uint32_t)read_32bitBE(0,streamFile)!=0x5354524D || /* "STRM" */ - read_16bitBE(0xa,streamFile)!=0x10 || - /* check that file = header (0x40) + data */ - read_32bitBE(4,streamFile)+0x40!=get_streamfile_size(streamFile)) + /* checks */ + if (!check_extensions(streamFile, "ast")) goto fail; - - /* check for a first block */ - if (read_32bitBE(0x40,streamFile)!=0x424C434B) /* "BLCK" */ + if (read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */ + goto fail; + if (read_16bitBE(0x0a,streamFile) != 0x10) /* ? */ goto fail; - /* check type details */ - codec_number = read_16bitBE(8,streamFile); - loop_flag = read_16bitBE(0xe,streamFile); - channel_count = read_16bitBE(0xc,streamFile); - /*max_block = read_32bitBE(0x20,streamFile);*/ + if (read_32bitBE(0x04,streamFile)+0x40 != get_streamfile_size(streamFile)) + goto fail; + codec = read_16bitBE(0x08,streamFile); + channel_count = read_16bitBE(0x0c,streamFile); + loop_flag = read_16bitBE(0x0e,streamFile); + //max_block = read_32bitBE(0x20,streamFile); - switch (codec_number) { - case 0: - coding_type = coding_NGC_AFC; + start_offset = 0x40; + if (read_32bitBE(start_offset,streamFile) != 0x424C434B) /* "BLCK" */ + goto fail; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_AST; + vgmstream->sample_rate = read_32bitBE(0x10,streamFile); + vgmstream->num_samples = read_32bitBE(0x14,streamFile); + vgmstream->loop_start_sample = read_32bitBE(0x18,streamFile); + vgmstream->loop_end_sample = read_32bitBE(0x1c,streamFile); + + vgmstream->layout_type = layout_blocked_ast; + switch (codec) { + case 0x00: /* , Pikmin 2 (GC) */ + vgmstream->coding_type = coding_NGC_AFC; break; - case 1: - coding_type = coding_PCM16BE; + case 0x01: /* Mario Kart: Double Dash!! (GC) */ + vgmstream->coding_type = coding_PCM16BE; break; default: goto fail; } - /* build the VGMSTREAM */ - - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = read_32bitBE(0x14,streamFile); - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - /* channels and loop flag are set by allocate_vgmstream */ - vgmstream->loop_start_sample = read_32bitBE(0x18,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0x1c,streamFile); - - vgmstream->coding_type = coding_type; - vgmstream->layout_type = layout_blocked_ast; - vgmstream->meta_type = meta_AST; - - /* open the file for reading by each channel */ - { - int i; - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - - if (!vgmstream->ch[i].streamfile) goto fail; - } - } - - /* start me up */ - block_update_ast(0x40,vgmstream); - + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/src/meta/atsl.c b/src/meta/atsl.c index bb19f62f..7ec19024 100644 --- a/src/meta/atsl.c +++ b/src/meta/atsl.c @@ -120,10 +120,12 @@ VGMSTREAM * init_vgmstream_atsl(STREAMFILE *streamFile) { vgmstream = init_vgmstream_riff(temp_streamFile); if (!vgmstream) goto fail; break; +#ifdef VGM_USE_VORBIS case KOVS: vgmstream = init_vgmstream_ogg_vorbis(temp_streamFile); if (!vgmstream) goto fail; break; +#endif case KTSS: vgmstream = init_vgmstream_ktss(temp_streamFile); if (!vgmstream) goto fail; diff --git a/src/meta/awc.c b/src/meta/awc.c index 2990c772..451ac67a 100644 --- a/src/meta/awc.c +++ b/src/meta/awc.c @@ -156,13 +156,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) { } - /* open files; channel offsets are updated below */ if (!vgmstream_open_stream(vgmstream,streamFile,awc.stream_offset)) goto fail; - - if (vgmstream->layout_type == layout_blocked_awc) - block_update_awc(awc.stream_offset, vgmstream); - return vgmstream; fail: diff --git a/src/meta/awc_xma_streamfile.h b/src/meta/awc_xma_streamfile.h index f4c231c7..54106624 100644 --- a/src/meta/awc_xma_streamfile.h +++ b/src/meta/awc_xma_streamfile.h @@ -5,26 +5,25 @@ typedef struct { /* config */ + int channel; + int channel_count; + size_t block_size; off_t stream_offset; size_t stream_size; - int channel_count; - int channel; - size_t chunk_size; /* state */ - off_t logical_offset; /* offset that corresponds to physical_offset */ - off_t physical_offset; /* actual file offset */ - off_t next_block_offset; /* physical offset of the next block start */ - off_t last_offset; /* physical offset of where the last block ended */ - size_t current_data_size; - size_t current_consumed_size; + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ - size_t total_size; /* size of the resulting XMA data */ + 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 new_offset, off_t last_offset); +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. @@ -35,7 +34,7 @@ static size_t awc_xma_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offse size_t frame_size = 0x800; /* ignore bad reads */ - if (offset < 0 || offset > data->total_size) { + if (offset < 0 || offset > data->logical_size) { return 0; } @@ -44,97 +43,84 @@ static size_t awc_xma_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offse if (offset < data->logical_offset) { data->logical_offset = 0x00; data->physical_offset = data->stream_offset; - data->next_block_offset = 0; - data->last_offset = 0; - data->current_data_size = 0; - data->current_consumed_size = 0; + data->data_size = 0; } - /* read from block, moving to next when all data is consumed */ + /* read blocks, one at a time */ while (length > 0) { - size_t to_read, bytes_read; - /* new block */ - if (data->current_data_size == 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 skip_size = get_block_skip_count(streamfile, data->physical_offset, data->channel) * frame_size; + 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 && data->last_offset) { - off_t data_offset = data->physical_offset + header_size + skip_size; - repeat_size = get_repeated_data_size(streamfile, data_offset, data->last_offset); + 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->next_block_offset = data->physical_offset + data->chunk_size; - data->physical_offset += header_size + skip_size + repeat_size; /* data start */ - data->current_data_size = data_size - repeat_size; /* readable max in this block */ - data->current_consumed_size = 0; + 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; } - /* block end, go next */ - if (data->current_consumed_size == data->current_data_size) { - data->last_offset = data->physical_offset; /* where last block ended */ - data->physical_offset = data->next_block_offset; - data->current_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 */ + } } - /* requested offset is further along, pretend we consumed data and try again */ - if (offset > data->logical_offset) { - size_t to_consume = offset - data->logical_offset; - if (to_consume > data->current_data_size - data->current_consumed_size) - to_consume = data->current_data_size - data->current_consumed_size; - - data->physical_offset += to_consume; - data->logical_offset += to_consume; - data->current_consumed_size += to_consume; - continue; - } - - /* clamp reads up to this block's end */ - to_read = (data->current_data_size - data->current_consumed_size); - if (to_read > length) - to_read = length; - if (to_read == 0) - return total_read; /* should never happen... */ - - /* finally read and move buffer/offsets */ - bytes_read = read_streamfile(dest, data->physical_offset, to_read, streamfile); - total_read += bytes_read; - if (bytes_read != to_read) - return total_read; /* couldn't read fully */ - - dest += bytes_read; - offset += bytes_read; - length -= bytes_read; - - data->physical_offset += bytes_read; - data->logical_offset += bytes_read; - data->current_consumed_size += bytes_read; } return total_read; } static size_t awc_xma_io_size(STREAMFILE *streamfile, awc_xma_io_data* data) { - off_t physical_offset, max_physical_offset, last_offset; + off_t physical_offset, max_physical_offset; size_t frame_size = 0x800; - size_t total_size = 0; + size_t logical_size = 0; - if (data->total_size) - return data->total_size; + if (data->logical_size) + return data->logical_size; physical_offset = data->stream_offset; max_physical_offset = data->stream_offset + data->stream_size; - last_offset = 0; - /* read blocks and sum final 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 */ @@ -145,33 +131,38 @@ static size_t awc_xma_io_size(STREAMFILE *streamfile, awc_xma_io_data* data) { size_t repeat_size = 0; /* if there are repeat samples current block repeats some frames from last block, find out size */ - if (repeat_samples && last_offset) { + if (repeat_samples) { off_t data_offset = physical_offset + header_size + skip_size; - repeat_size = get_repeated_data_size(streamfile, data_offset, last_offset); + repeat_size = get_repeated_data_size(streamfile, data_offset, repeat_samples); } - last_offset = physical_offset + header_size + skip_size + data_size; - total_size += data_size - repeat_size; - physical_offset += data->chunk_size; + logical_size += data_size - repeat_size; + physical_offset += data->block_size; } - data->total_size = total_size; - return data->total_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 chunk_size, int channel_count, int channel) { +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.chunk_size = chunk_size; - io_data.channel_count = channel_count; - io_data.channel = channel; + 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); @@ -182,7 +173,10 @@ static STREAMFILE* setup_awc_xma_streamfile(STREAMFILE *streamFile, off_t stream if (!new_streamFile) goto fail; temp_streamFile = new_streamFile; - //todo maybe should force to read filesize once + new_streamFile = open_buffer_streamfile(new_streamFile,0); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + return temp_streamFile; fail: @@ -209,35 +203,29 @@ static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, awc_xm /* 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 new_offset, off_t last_offset) { - uint8_t new_frame[0x800];/* buffer to avoid fseek back and forth */ - size_t frame_size = 0x800; - off_t offset; - int i; +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; - /* read block first frame */ - if (read_streamfile(new_frame,new_offset, frame_size,streamFile) != frame_size) - goto fail; + //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. */ - /* find the frame in last bytes of prev block */ - offset = last_offset - 0x4000; /* typical max is 1 frame of ~0x800, no way to know exact size */ - while (offset < last_offset) { - /* compare frame vs prev block data */ - for (i = 0; i < frame_size; i++) { - if ((uint8_t)read_8bit(offset+i,streamFile) != new_frame[i]) - break; - } - - /* frame fully compared? */ - if (i == frame_size) - return last_offset - offset; - else - offset += i+1; + 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; } - -fail: - VGM_LOG("AWC: can't find repeat size, new=0x%08lx, last=0x%08lx\n", new_offset, last_offset); - return 0; /* keep on truckin' */ } /* header has a skip value, but somehow it's sometimes bigger than expected (WHY!?!?) so just sum all */ diff --git a/src/meta/bik.c b/src/meta/bik.c index 18a3996d..eecff1f0 100644 --- a/src/meta/bik.c +++ b/src/meta/bik.c @@ -12,8 +12,11 @@ VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { size_t stream_size; - /* check extension, case insensitive (bika = manually demuxed audio) */ - if (!check_extensions(streamFile,"bik,bika,bik2,bik2a,bk2,bk2a")) goto fail; + /* checks */ + /* .bik/bik2/bk2: standard + * .bika = fake extension for demuxed audio */ + if (!check_extensions(streamFile,"bik,bika,bik2,bk2")) + goto fail; /* check header "BIK" (bink 1) or "KB2" (bink 2), followed by version-char (audio is the same for both) */ if ((read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x42494B00 && diff --git a/src/meta/bnk_sony.c b/src/meta/bnk_sony.c index 51da0c0f..f6c7a595 100644 --- a/src/meta/bnk_sony.c +++ b/src/meta/bnk_sony.c @@ -1,40 +1,55 @@ #include "meta.h" #include "../coding/coding.h" +typedef enum { PSX, PCM16, ATRAC9 } bnk_codec; + /* BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { #if 1 VGMSTREAM * vgmstream = NULL; off_t start_offset, stream_offset, name_offset = 0; - size_t stream_size; + size_t stream_size, interleave = 0; off_t sblk_offset, data_offset; - int channel_count, loop_flag, sample_rate, codec; - int version; + size_t data_size; + int channel_count = 0, loop_flag, sample_rate, version; + int loop_start = 0, loop_end = 0; uint32_t atrac9_info = 0; - int loop_start = 0, loop_length = 0; + int total_subsongs, target_subsong = streamFile->stream_index; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + bnk_codec codec; /* checks */ if (!check_extensions(streamFile, "bnk")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x03000000) + + if (read_32bitBE(0x00,streamFile) == 0x00000003 && read_32bitBE(0x04,streamFile) == 0x00000002) { /* PS3 */ + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + } + else if (read_32bitBE(0x00,streamFile) == 0x03000000 && read_32bitBE(0x04,streamFile) == 0x02000000) { /* Vita/PS4 */ + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + } + else { goto fail; - if (read_32bitBE(0x04,streamFile) != 0x02000000) - goto fail; - sblk_offset = read_32bitLE(0x08,streamFile); - /* 0x0c: sblk size */ - data_offset = read_32bitLE(0x10,streamFile); - /* 0x14: data size */ + } + + sblk_offset = read_32bit(0x08,streamFile); + /* 0x0c: sklb size */ + data_offset = read_32bit(0x10,streamFile); + data_size = read_32bit(0x14,streamFile); /* SE banks, also used for music. Most table fields seems reserved/defaults and * don't change much between subsongs or files, so they aren't described in detail */ /* SBlk part: parse header */ - if (read_32bitBE(sblk_offset+0x00,streamFile) != 0x53426C6B) /* "SBlk" */ + if (read_32bit(sblk_offset+0x00,streamFile) != 0x6B6C4253) /* "SBlk" (sample block?) */ goto fail; - version = read_32bitLE(sblk_offset+0x04,streamFile); + version = read_32bit(sblk_offset+0x04,streamFile); /* 0x08: possibly when version=0x0d, 0x03=Vita, 0x06=PS4 */ //;VGM_LOG("BNK: sblk_offset=%lx, data_offset=%lx, version %x\n", sblk_offset, data_offset, version); @@ -50,18 +65,15 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { switch(version) { + case 0x03: /* L@ove Once - Mermaid's Tears (PS3) */ case 0x09: /* Puyo Puyo Tetris (PS4) */ - section_entries = (uint16_t)read_16bitLE(sblk_offset+0x16,streamFile); /* entry size: ~0x0c */ - material_entries = (uint16_t)read_16bitLE(sblk_offset+0x18,streamFile); /* entry size: ~0x08 */ - stream_entries = (uint16_t)read_16bitLE(sblk_offset+0x1a,streamFile); /* entry size: ~0x60 */ - table1_offset = sblk_offset + read_32bitLE(sblk_offset+0x1c,streamFile); - table2_offset = sblk_offset + read_32bitLE(sblk_offset+0x20,streamFile); - /* 0x24: null? */ - /* 0x28: offset to end? */ - /* 0x2c: offset to table3? */ - /* 0x30: null? */ - table3_offset = sblk_offset + read_32bitLE(sblk_offset+0x34,streamFile); - table4_offset = sblk_offset + read_32bitLE(sblk_offset+0x38,streamFile); + section_entries = (uint16_t)read_16bit(sblk_offset+0x16,streamFile); /* entry size: ~0x0c */ + material_entries = (uint16_t)read_16bit(sblk_offset+0x18,streamFile); /* entry size: ~0x08 */ + stream_entries = (uint16_t)read_16bit(sblk_offset+0x1a,streamFile); /* entry size: ~0x60 */ + table1_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile); + table2_offset = sblk_offset + read_32bit(sblk_offset+0x20,streamFile); + table3_offset = sblk_offset + read_32bit(sblk_offset+0x34,streamFile); + table4_offset = sblk_offset + read_32bit(sblk_offset+0x38,streamFile); table1_entry_size = 0x0c; table1_suboffset = 0x08; @@ -70,17 +82,13 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { break; case 0x0d: /* Polara (Vita), Crypt of the Necrodancer (Vita) */ - table1_offset = sblk_offset + read_32bitLE(sblk_offset+0x18,streamFile); - table2_offset = sblk_offset + read_32bitLE(sblk_offset+0x1c,streamFile); - /* 0x20: null? */ - /* 0x24: offset to end? */ - /* 0x28: offset to table4? */ - table3_offset = sblk_offset + read_32bitLE(sblk_offset+0x2c,streamFile); - table4_offset = sblk_offset + read_32bitLE(sblk_offset+0x30,streamFile); - /* 0x34: null? */ - section_entries = (uint16_t)read_16bitLE(sblk_offset+0x38,streamFile); /* entry size: ~0x24 */ - material_entries = (uint16_t)read_16bitLE(sblk_offset+0x3a,streamFile); /* entry size: ~0x08 */ - stream_entries = (uint16_t)read_16bitLE(sblk_offset+0x3c,streamFile); /* entry size: ~0x90 + variable (sometimes) */ + table1_offset = sblk_offset + read_32bit(sblk_offset+0x18,streamFile); + table2_offset = sblk_offset + read_32bit(sblk_offset+0x1c,streamFile); + table3_offset = sblk_offset + read_32bit(sblk_offset+0x2c,streamFile); + table4_offset = sblk_offset + read_32bit(sblk_offset+0x30,streamFile); + section_entries = (uint16_t)read_16bit(sblk_offset+0x38,streamFile); /* entry size: ~0x24 */ + material_entries = (uint16_t)read_16bit(sblk_offset+0x3a,streamFile); /* entry size: ~0x08 */ + stream_entries = (uint16_t)read_16bit(sblk_offset+0x3c,streamFile); /* entry size: ~0x90 + variable (sometimes) */ table1_entry_size = 0x24; table1_suboffset = 0x0c; @@ -93,7 +101,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { goto fail; } - //;VGM_LOG("BNK: table offsets=t1=%lx, %lx, %lx, %lx\n", table1_offset,table2_offset,table3_offset,table4_offset); + //;VGM_LOG("BNK: table offsets=%lx, %lx, %lx, %lx\n", table1_offset,table2_offset,table3_offset,table4_offset); //;VGM_LOG("BNK: table entries=%i, %i, %i\n", section_entries,material_entries,stream_entries); @@ -108,19 +116,24 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { * - get stream offsets * - find if one section points to the selected material, and get section name = stream name */ + /* parse materials */ total_subsongs = 0; if (target_subsong == 0) target_subsong = 1; for (i = 0; i < material_entries; i++) { - uint16_t table2_subtype = (uint16_t)read_16bitLE(table2_offset+(i*0x08)+table2_suboffset+0x02,streamFile); + uint32_t table2_value, table2_subinfo, table2_subtype; + + table2_value = (uint32_t)read_32bit(table2_offset+(i*0x08)+table2_suboffset+0x00,streamFile); + table2_subinfo = (table2_value >> 0) & 0xFFFF; + table2_subtype = (table2_value >> 16) & 0xFFFF; if (table2_subtype != 0x100) continue; /* not sounds */ total_subsongs++; if (total_subsongs == target_subsong) { table2_entry_offset = (i*0x08); - table3_entry_offset = (uint16_t)read_16bitLE(table2_offset+(i*0x08)+table2_suboffset+0x00,streamFile); + table3_entry_offset = table2_subinfo; /* continue to count all subsongs*/ } } @@ -136,106 +149,152 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { /* parse sounds */ - stream_offset = read_32bitLE(table3_offset+table3_entry_offset+table3_suboffset+0x00,streamFile); - stream_size = read_32bitLE(table3_offset+table3_entry_offset+table3_suboffset+0x04,streamFile); + stream_offset = read_32bit(table3_offset+table3_entry_offset+table3_suboffset+0x00,streamFile); + stream_size = read_32bit(table3_offset+table3_entry_offset+table3_suboffset+0x04,streamFile); + /* parse names */ + switch(version) { + //case 0x03: /* different format? */ + case 0x09: + case 0x0d: + /* find if this sound has an assigned name in table1 */ + for (i = 0; i < section_entries; i++) { + off_t entry_offset = (uint16_t)read_16bit(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,streamFile); - /* find if this sound has an assigned name in table1 */ - for (i = 0; i < section_entries; i++) { - off_t entry_offset = (uint16_t)read_16bitLE(table1_offset+(i*table1_entry_size)+table1_suboffset+0x00,streamFile); + /* rarely (ex. Polara sfx) one name applies to multiple materials, + * from current entry_offset to next entry_offset (section offsets should be in order) */ + if (entry_offset <= table2_entry_offset ) { + table4_entry_id = i; + //break; + } + } - /* rarely (ex. Polara sfx) one name applies to multiple materials, - * from current entry_offset to next entry_offset (section offsets should be in order) */ - if (entry_offset <= table2_entry_offset ) { - table4_entry_id = i; - //break; - } - } + /* table4: */ + /* 0x00: bank name (optional) */ + /* 0x08: header size */ + /* 0x0c: table4 size */ + /* variable: entries */ + /* variable: names (null terminated) */ + table4_entries_offset = table4_offset + read_32bit(table4_offset+0x08, streamFile); + table4_names_offset = table4_entries_offset + (0x10*section_entries); + //;VGM_LOG("BNK: t4_entries=%lx, t4_names=%lx\n", table4_entries_offset, table4_names_offset); - /* table4: */ - /* 0x00: bank name (optional) */ - /* 0x08: header size */ - /* 0x0c: table4 size */ - /* variable: entries */ - /* variable: names (null terminated) */ - table4_entries_offset = table4_offset + read_32bitLE(table4_offset+0x08, streamFile); - table4_names_offset = table4_entries_offset + (0x10*section_entries); - //;VGM_LOG("BNK: t4_entries=%lx, t4_names=%lx\n", table4_entries_offset, table4_names_offset); + /* get assigned name from table4 names */ + for (i = 0; i < section_entries; i++) { + int entry_id = read_32bit(table4_entries_offset+(i*0x10)+0x0c, streamFile); + if (entry_id == table4_entry_id) { + name_offset = table4_names_offset + read_32bit(table4_entries_offset+(i*0x10)+0x00, streamFile); + break; + } + } - /* get assigned name from table4 names */ - for (i = 0; i < section_entries; i++) { - int entry_id = read_32bitLE(table4_entries_offset+(i*0x10)+0x0c, streamFile); - if (entry_id == table4_entry_id) { - name_offset = table4_names_offset + read_32bitLE(table4_entries_offset+(i*0x10)+0x00, streamFile); break; - } + default: + break; } //;VGM_LOG("BNK: stream_offset=%lx, stream_size=%x, name_offset=%lx\n", stream_offset, stream_size, name_offset); } - /* data part: parse extradata before the codec, very annoying */ + /* data part: parse extradata before the codec, if needed */ { - size_t extradata_size = 0; + int type, loop_length; + size_t extradata_size = 0, postdata_size = 0; start_offset = data_offset + stream_offset; switch(version) { - case 0x09: - codec = read_16bitLE(start_offset+0x00,streamFile); - extradata_size = 0x08 + read_32bitLE(start_offset+0x04,streamFile); /* 0x14 for AT9 */ + case 0x03: + sample_rate = 48000; /* seems ok */ + channel_count = 1; - switch(codec) { -#ifdef VGM_USE_ATRAC9 - case 0x02: - case 0x05: - if (read_32bitLE(start_offset+0x08,streamFile) + 0x08 != extradata_size) /* repeat? */ + /* hack for PS3 files that use dual subsongs as stereo */ + if (total_subsongs == 2 && stream_size * 2 == data_size) { + channel_count = 2; + stream_size = stream_size*channel_count; + total_subsongs = 1; + } + + interleave = stream_size / channel_count; + //postdata_size = 0x10; /* last frame may be garbage */ + + loop_flag = ps_find_loop_offsets(streamFile, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end); + loop_flag = (loop_start > 28); /* ignore full loops since they just fadeout + repeat */ + + codec = PSX; + break; + + case 0x09: + type = read_16bit(start_offset+0x00,streamFile); + extradata_size = 0x08 + read_32bit(start_offset+0x04,streamFile); /* 0x14 for AT9 */ + + switch(type) { + case 0x02: /* ATRAC9 mono */ + case 0x05: /* ATRAC9 stereo */ + if (read_32bit(start_offset+0x08,streamFile) + 0x08 != extradata_size) /* repeat? */ goto fail; + sample_rate = 48000; /* seems ok */ + channel_count = (type == 0x02) ? 1 : 2; + atrac9_info = (uint32_t)read_32bitBE(start_offset+0x0c,streamFile); /* 0x10: null? */ - loop_length = read_32bitLE(start_offset+0x14,streamFile); - loop_start = read_32bitLE(start_offset+0x18,streamFile); + loop_length = read_32bit(start_offset+0x14,streamFile); + loop_start = read_32bit(start_offset+0x18,streamFile); + loop_end = loop_start + loop_length; /* loop_start is -1 if not set */ - /* get from AT9 config just in case, but probably: sr=48000 / codec 0x02=1ch, 0x05=2ch */ - atrac9_parse_config(atrac9_info, &sample_rate, &channel_count, NULL); + codec = ATRAC9; break; -#endif + + default: + VGM_LOG("BNK: unknown type %x\n", type); + goto fail; } break; case 0x0d: - codec = read_16bitLE(start_offset+0x00,streamFile); - if (read_32bitLE(start_offset+0x04,streamFile) != 0x01) /* type? */ + type = read_16bit(start_offset+0x00,streamFile); + if (read_32bit(start_offset+0x04,streamFile) != 0x01) /* type? */ goto fail; - extradata_size = 0x10 + read_32bitLE(start_offset+0x08,streamFile); /* 0x80 for AT9, 0x10 for PCM */ + extradata_size = 0x10 + read_32bit(start_offset+0x08,streamFile); /* 0x80 for AT9, 0x10 for PCM */ /* 0x0c: null? */ - - switch(codec) { -#ifdef VGM_USE_ATRAC9 - case 0x02: - case 0x05: - if (read_32bitLE(start_offset+0x10,streamFile) + 0x10 != extradata_size) /* repeat? */ + switch(type) { + case 0x02: /* ATRAC9 mono */ + case 0x05: /* ATRAC9 stereo */ + if (read_32bit(start_offset+0x10,streamFile) + 0x10 != extradata_size) /* repeat? */ goto fail; + sample_rate = 48000; /* seems ok */ + channel_count = (type == 0x02) ? 1 : 2; + atrac9_info = (uint32_t)read_32bitBE(start_offset+0x14,streamFile); /* 0x18: null? */ /* 0x1c: channels? */ /* 0x20: null? */ - loop_length = read_32bitLE(start_offset+0x24,streamFile); - loop_start = read_32bitLE(start_offset+0x28,streamFile); - /* get from AT9 config just in case, but probably: sr=48000 / codec 0x02=1ch, 0x05=2ch */ - atrac9_parse_config(atrac9_info, &sample_rate, &channel_count, NULL); + loop_length = read_32bit(start_offset+0x24,streamFile); + loop_start = read_32bit(start_offset+0x28,streamFile); + loop_end = loop_start + loop_length; /* loop_start is -1 if not set */ + + codec = ATRAC9; break; -#endif - case 0x01: - case 0x04: + + case 0x01: /* PCM16LE mono? (NekoBuro/Polara sfx) */ + case 0x04: /* PCM16LE stereo? (NekoBuro/Polara sfx) */ sample_rate = 48000; /* seems ok */ /* 0x10: null? */ - channel_count = read_32bitLE(start_offset+0x14,streamFile); - loop_start = read_32bitLE(start_offset+0x18,streamFile); - loop_length = read_32bitLE(start_offset+0x1c,streamFile); + channel_count = read_32bit(start_offset+0x14,streamFile); + interleave = 0x02; + + loop_start = read_32bit(start_offset+0x18,streamFile); + loop_length = read_32bit(start_offset+0x1c,streamFile); + loop_end = loop_start + loop_length; /* loop_start is -1 if not set */ + + codec = PCM16; break; + + default: + VGM_LOG("BNK: unknown type %x\n", type); + goto fail; } break; @@ -245,9 +304,11 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { start_offset += extradata_size; stream_size -= extradata_size; + stream_size -= postdata_size; + //;VGM_LOG("BNK: offset=%lx, size=%x\n", start_offset, stream_size); } - loop_flag = (loop_start >= 0) && (loop_length > 0); /* loop_start is -1 if not set */ + loop_flag = (loop_start >= 0) && (loop_end > 0); /* build the VGMSTREAM */ @@ -262,8 +323,7 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { switch(codec) { #ifdef VGM_USE_ATRAC9 - case 0x02: /* ATRAC9 mono? */ - case 0x05: { /* ATRAC9 stereo? */ + case ATRAC9: { atrac9_config cfg = {0}; cfg.channels = vgmstream->channels; @@ -277,25 +337,30 @@ VGMSTREAM * init_vgmstream_bnk_sony(STREAMFILE *streamFile) { vgmstream->num_samples = atrac9_bytes_to_samples(stream_size, vgmstream->codec_data); vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_start + loop_length; + vgmstream->loop_end_sample = loop_end; break; } #endif - case 0x01: /* PCM16LE mono? (NekoBuro/Polara sfx) */ - case 0x04: /* PCM16LE stereo? (NekoBuro/Polara sfx) */ + case PCM16: vgmstream->coding_type = coding_PCM16LE; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x02; + vgmstream->interleave_block_size = interleave; vgmstream->num_samples = pcm_bytes_to_samples(stream_size, vgmstream->channels, 16); vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_start + loop_length; - + vgmstream->loop_end_sample = loop_end; break; - default: - VGM_LOG("BNK: unknown codec %x\n", codec); - goto fail; + case PSX: + vgmstream->sample_rate = 48000; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + break; } if (name_offset) diff --git a/src/meta/caf.c b/src/meta/caf.c index 8180fe8e..ca1b1134 100644 --- a/src/meta/caf.c +++ b/src/meta/caf.c @@ -54,9 +54,6 @@ VGMSTREAM * init_vgmstream_caf(STREAMFILE *streamFile) { if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) goto fail; - - block_update_caf(start_offset,vgmstream); - return vgmstream; fail: diff --git a/src/meta/csmp.c b/src/meta/csmp.c new file mode 100644 index 00000000..30915b29 --- /dev/null +++ b/src/meta/csmp.c @@ -0,0 +1,55 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* CSMP - Retro Studios sample [Metroid Prime 3 (Wii), Donkey Kong Country Returns (Wii)] */ +VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, first_offset = 0x08, chunk_offset; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "csmp")) + goto fail; + if (read_32bitBE(0x00, streamFile) != 0x43534D50) /* "CSMP" */ + goto fail; + if (read_32bitBE(0x04, streamFile) != 1) /* version? */ + goto fail; + + if (!find_chunk(streamFile, 0x44415441,first_offset,0, &chunk_offset,NULL, 1, 0)) /*"DATA"*/ + goto fail; + + /* contains standard DSP header, but somehow some validations (start/loop ps) + * don't seem to work, so no point to handle as standard DSP */ + + channel_count = 1; + loop_flag = read_16bitBE(chunk_offset+0x0c,streamFile); + start_offset = chunk_offset + 0x60; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_CSMP; + vgmstream->sample_rate = read_32bitBE(chunk_offset+0x08,streamFile); + vgmstream->num_samples = read_32bitBE(chunk_offset+0x00,streamFile); + vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bitBE(chunk_offset+0x10,streamFile)); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(read_32bitBE(chunk_offset+0x14,streamFile))+1; + if (vgmstream->loop_end_sample > vgmstream->num_samples) /* ? */ + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_none; + dsp_read_coefs_be(vgmstream, streamFile, chunk_offset+0x1c, 0x00); + dsp_read_hist_be(vgmstream, streamFile, chunk_offset+0x40, 0x00); + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/dec.c b/src/meta/dec.c index 43a3fe18..69247e15 100644 --- a/src/meta/dec.c +++ b/src/meta/dec.c @@ -13,14 +13,16 @@ VGMSTREAM * init_vgmstream_dec(STREAMFILE *streamFile) { int loop_flag, channel_count, sample_rate, loop_start = 0, loop_end = 0; - /* check extension (.dec: main, .de2: Gurumin) */ + /* checks + * .dec: main, + * .de2: Gurumin (PC) */ if ( !check_extensions(streamFile,"dec,de2") ) goto fail; /* Gurumin has extra data, maybe related to rhythm (~0x50000) */ if (check_extensions(streamFile,"de2")) { /* still not sure what this is for, but consistently 0xb */ - if (read_32bitLE(0x04,streamFile) != 0xb) goto fail; + if (read_32bitLE(0x04,streamFile) != 0x0b) goto fail; /* legitimate! really! */ riff_off = 0x10 + (read_32bitLE(0x0c,streamFile) ^ read_32bitLE(0x04,streamFile)); @@ -67,6 +69,7 @@ VGMSTREAM * init_vgmstream_dec(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_DEC; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = pcm_size / 2 / channel_count; vgmstream->loop_start_sample = loop_start; @@ -76,14 +79,8 @@ VGMSTREAM * init_vgmstream_dec(STREAMFILE *streamFile) { vgmstream->interleave_block_size = 0x800; vgmstream->layout_type = layout_blocked_dec; - vgmstream->meta_type = meta_DEC; - - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - - block_update_dec(start_offset, vgmstream); - return vgmstream; fail: diff --git a/src/meta/ea_1snh.c b/src/meta/ea_1snh.c index c4259f22..b7b4881d 100644 --- a/src/meta/ea_1snh.c +++ b/src/meta/ea_1snh.c @@ -21,7 +21,7 @@ typedef struct { int big_endian; int loop_flag; int is_sead; - int codec_version; + int codec_config; } ea_header; static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset); @@ -85,7 +85,7 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { case EA_CODEC_IMA: /* Need for Speed II (PC) */ if (ea.bits && ea.bits!=2) goto fail; /* only in EACS */ vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */ - vgmstream->codec_version = ea.codec_version; + vgmstream->codec_config = ea.codec_config; break; case EA_CODEC_PSX: /* Need for Speed (PS) */ @@ -97,12 +97,9 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) { goto fail; } - /* open files; channel offsets are updated below */ + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - - block_update_ea_1snh(start_offset,vgmstream); - return vgmstream; fail: @@ -127,7 +124,7 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { VGM_ASSERT(ea->type != 0, "EA EACS: unknown type\n"); /* block type? */ if (ea->codec == EA_CODEC_IMA) - ea->codec_version = get_ea_1snh_ima_version(streamFile, 0x00, ea); + ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea); } else if (ea->is_sead) { /* alt subheader (found in some PC videos) */ @@ -136,7 +133,7 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) { ea->codec = read_32bit(offset+0x08, streamFile); if (ea->codec == EA_CODEC_IMA) - ea->codec_version = get_ea_1snh_ima_version(streamFile, 0x00, ea); + ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea); set_ea_1snh_num_samples(streamFile, 0x00, ea); if (ea->loop_start_offset) /* offset found, now find actual start sample */ @@ -198,7 +195,7 @@ static void set_ea_1snh_num_samples(STREAMFILE* streamFile, off_t start_offset, block_samples = ps_bytes_to_samples(block_size - block_header, ea->channels); break; case EA_CODEC_IMA: - if (ea->codec_version == 1) + if (ea->codec_config == 1) block_samples = read_32bit(block_offset + block_header, streamFile); else block_samples = ima_bytes_to_samples(block_size - block_header, ea->channels); diff --git a/src/meta/ea_eaac.c b/src/meta/ea_eaac.c index 1d65786d..9f8604c4 100644 --- a/src/meta/ea_eaac.c +++ b/src/meta/ea_eaac.c @@ -483,6 +483,7 @@ typedef struct { } eaac_header; static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *streamData, eaac_header *eaac); +static layered_layout_data* build_layered_eaaudiocore_eaxma(STREAMFILE *streamFile, eaac_header *eaac); /* EA newest header from RwAudioCore (RenderWare?) / EAAudioCore library (still generated by sx.exe). @@ -508,7 +509,6 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST /* rest is optional, depends on used flags and codec (handled below) */ eaac.stream_offset = start_offset; - /* V0: SNR+SNS, V1: SPR+SPS (no apparent differences, other than block flags) */ if (eaac.version != EAAC_VERSION_V0 && eaac.version != EAAC_VERSION_V1) { VGM_LOG("EA EAAC: unknown version\n"); @@ -586,28 +586,10 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST #ifdef VGM_USE_FFMPEG case EAAC_CODEC_EAXMA: { /* "EXm0": EA-XMA [Dante's Inferno (X360)] */ - uint8_t buf[0x100]; - int bytes, block_size, block_count; - size_t stream_size, virtual_size; - ffmpeg_custom_config cfg = {0}; - - stream_size = get_streamfile_size(streamData) - eaac.stream_offset; //todo not correct for banks - virtual_size = ffmpeg_get_eaxma_virtual_size(vgmstream->channels, eaac.streamed, eaac.stream_offset,stream_size, streamData); - block_size = 0x10000; /* todo unused and not correctly done by the parser */ - block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0); - - bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, virtual_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - if (bytes <= 0) goto fail; - - cfg.type = FFMPEG_EA_XMA; - cfg.virtual_size = virtual_size; - cfg.channels = vgmstream->channels; - - vgmstream->codec_data = init_ffmpeg_config(streamData, buf,bytes, eaac.stream_offset,stream_size, &cfg); - if (!vgmstream->codec_data) goto fail; - + vgmstream->layout_data = build_layered_eaaudiocore_eaxma(streamData, &eaac); + if (!vgmstream->layout_data) goto fail; vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; + vgmstream->layout_type = layout_layered; break; } #endif @@ -638,7 +620,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST vgmstream->layout_type = layout_segmented; } else { - temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed, eaac.stream_offset,0); + temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset); if (!temp_streamFile) goto fail; vgmstream->codec_data = init_mpeg_custom(temp_streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg); @@ -659,7 +641,6 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST #ifdef VGM_USE_ATRAC9 case EAAC_CODEC_EATRAX: { /* EATrax (unknown FourCC) [Need for Speed: Most Wanted (Vita)] */ atrac9_config cfg = {0}; - size_t total_size; /* EATrax is "buffered" ATRAC9, uses custom IO since it's kind of complex to add to the decoder */ @@ -668,14 +649,15 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST cfg.channels = eaac.channels; cfg.config_data = read_32bitBE(header_offset + 0x08,streamHead); /* 0x10: frame size? (same as config data?) */ - total_size = read_32bitLE(header_offset + 0x0c,streamHead); /* actual data size without blocks, LE b/c why make sense */ + /* actual data size without blocks, LE b/c why make sense (but don't use it in case of truncated files) */ + //total_size = read_32bitLE(header_offset + 0x0c,streamHead); vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; - temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed, eaac.stream_offset, total_size); + temp_streamFile = setup_eaac_streamfile(streamData, eaac.version, eaac.codec, eaac.streamed,0,0, eaac.stream_offset); if (!temp_streamFile) goto fail; break; @@ -694,10 +676,6 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST if (!vgmstream_open_stream(vgmstream,temp_streamFile ? temp_streamFile : streamData,start_offset)) goto fail; - - if (vgmstream->layout_type == layout_blocked_ea_sns) - block_update_ea_sns(start_offset, vgmstream); - close_streamfile(temp_streamFile); return vgmstream; @@ -729,7 +707,6 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st segmented_layout_data *data = NULL; STREAMFILE* temp_streamFile[2] = {0}; off_t offsets[2] = { eaac->stream_offset, eaac->loop_offset }; - off_t sizes[2] = { eaac->stream_offset + eaac->loop_offset, 0}; /* 0 = let streamfile guess */ int num_samples[2] = { eaac->loop_start, eaac->num_samples - eaac->loop_start}; int segment_count = 2; /* intro/loop */ int i; @@ -740,7 +717,7 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st if (!data) goto fail; for (i = 0; i < segment_count; i++) { - temp_streamFile[i] = setup_eaac_streamfile(streamData, eaac->version,eaac->codec,eaac->streamed, offsets[i], sizes[i]); + temp_streamFile[i] = setup_eaac_streamfile(streamData, eaac->version,eaac->codec,eaac->streamed,0,0, offsets[i]); if (!temp_streamFile[i]) goto fail; data->segments[i] = allocate_vgmstream(eaac->channels, 0); @@ -774,8 +751,6 @@ static segmented_layout_data* build_segmented_eaaudiocore_looping(STREAMFILE *st /* setup segmented VGMSTREAMs */ if (!setup_layout_segmented(data)) goto fail; - data->loop_segment = 1; - return data; fail: @@ -784,3 +759,69 @@ fail: free_layout_segmented(data); return NULL; } + +static layered_layout_data* build_layered_eaaudiocore_eaxma(STREAMFILE *streamData, eaac_header *eaac) { + layered_layout_data* data = NULL; + STREAMFILE* temp_streamFile = NULL; + int i, layers = (eaac->channels+1) / 2; + + + /* init layout */ + data = init_layout_layered(layers); + if (!data) goto fail; + + /* open each layer subfile (1/2ch streams: 2ch+2ch..+1ch or 2ch+2ch..+2ch). + * EA-XMA uses completely separate 1/2ch streams, unlike standard XMA that interleaves 1/2ch streams + * with a skip counter to reinterleave (so EA-XMA streams don't have skips set) */ + for (i = 0; i < layers; i++) { + int layer_channels = (i+1 == layers && eaac->channels % 2 == 1) ? 1 : 2; /* last layer can be 1/2ch */ + + /* build the layer VGMSTREAM */ + data->layers[i] = allocate_vgmstream(layer_channels, eaac->loop_flag); + if (!data->layers[i]) goto fail; + + data->layers[i]->sample_rate = eaac->sample_rate; + data->layers[i]->num_samples = eaac->num_samples; + data->layers[i]->loop_start_sample = eaac->loop_start; + data->layers[i]->loop_end_sample = eaac->loop_end; + +#ifdef VGM_USE_FFMPEG + { + uint8_t buf[0x100]; + int bytes, block_size, block_count; + size_t stream_size; + + temp_streamFile = setup_eaac_streamfile(streamData, eaac->version, eaac->codec, eaac->streamed,i,layers, eaac->stream_offset); + if (!temp_streamFile) goto fail; + + stream_size = get_streamfile_size(temp_streamFile); + block_size = 0x10000; /* unused */ + block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0); + + bytes = ffmpeg_make_riff_xma2(buf, 0x100, data->layers[i]->num_samples, stream_size, data->layers[i]->channels, data->layers[i]->sample_rate, block_count, block_size); + data->layers[i]->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00, stream_size); + if (!data->layers[i]->codec_data) goto fail; + + data->layers[i]->coding_type = coding_FFmpeg; + data->layers[i]->layout_type = layout_none; + } +#else + goto fail; +#endif + + if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) ) { + goto fail; + } + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + close_streamfile(temp_streamFile); + return data; + +fail: + close_streamfile(temp_streamFile); + free_layout_layered(data); + return NULL; +} diff --git a/src/meta/ea_eaac_streamfile.h b/src/meta/ea_eaac_streamfile.h index cb2434ae..c662697e 100644 --- a/src/meta/ea_eaac_streamfile.h +++ b/src/meta/ea_eaac_streamfile.h @@ -2,141 +2,184 @@ #define _EA_EAAC_STREAMFILE_H_ #include "../streamfile.h" +#define XMA_FRAME_SIZE 0x800 typedef struct { - /* state */ - off_t logical_offset; /* offset that corresponds to physical_offset */ - off_t physical_offset; /* actual file offset */ - /* config */ int version; int codec; int streamed; - off_t start_offset; - size_t total_size; /* size of the resulting substream */ + int stream_number; + int stream_count; + off_t stream_offset; + + /* state */ + off_t logical_offset; /* offset that corresponds to physical_offset */ + off_t physical_offset; /* actual file offset */ + + uint32_t block_flag; /* current block flags */ + 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 extra_size; /* extra padding/etc size of the block */ + + size_t logical_size; } eaac_io_data; /* Reads skipping EA's block headers, so the resulting data is smaller or larger than physical data. - * physical/logical_offset should always be at the start of a block and only advance when a block is fully done */ + * physical/logical_offset will be at the start of a block and only advance when a block is done */ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, eaac_io_data* data) { size_t total_read = 0; /* ignore bad reads */ - if (offset < 0 || offset > data->total_size) { + if (offset < 0 || offset > data->logical_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->physical_offset = data->stream_offset; data->logical_offset = 0x00; + data->data_size = 0; + data->extra_size = 0; } - /* read doing one EA block at a time */ + /* read blocks, one at a time */ while (length > 0) { - size_t to_read, bytes_read; - off_t intrablock_offset, intradata_offset; - uint32_t block_flag, block_size, data_size, skip_size; - block_flag = (uint8_t)read_8bit(data->physical_offset+0x00,streamfile); - block_size = read_32bitBE(data->physical_offset+0x00,streamfile) & 0x00FFFFFF; - - if (data->version == 1 && block_flag == 0x48) { - data->physical_offset += block_size; - continue; /* skip header block */ + /* ignore EOF (implicitly handles block end flags) */ + if (data->logical_offset >= data->logical_size) { + break; } - if (data->version == 1 && block_flag == 0x45) - break; /* stop on last block (always empty) */ - switch(data->codec) { + /* process new block */ + if (data->data_size == 0) { + data->block_flag = (uint8_t)read_8bit(data->physical_offset+0x00,streamfile); + data->block_size = read_32bitBE(data->physical_offset+0x00,streamfile) & 0x00FFFFFF; + + /* ignore header block */ + if (data->version == 1 && data->block_flag == 0x48) { + data->physical_offset += data->block_size; + continue; + } + + switch(data->codec) { + case 0x03: { /* EA-XMA */ + /* block format: 0x04=num-samples, (size*4 + N XMA packets) per stream (with 1/2ch XMA headers) */ + int i; + + data->skip_size = 0x04 + 0x04; + for (i = 0; i < data->stream_number; i++) { + data->skip_size += read_32bitBE(data->physical_offset+data->skip_size, streamfile) / 4; + } + data->data_size = read_32bitBE(data->physical_offset+data->skip_size, streamfile) / 4; /* why size*4...? */ + data->skip_size += 0x04; /* skip mini header */ + data->data_size -= 0x04; /* remove mini header */ + if (data->data_size % XMA_FRAME_SIZE) + data->extra_size = XMA_FRAME_SIZE - (data->data_size % XMA_FRAME_SIZE); + break; + } + + case 0x05: /* EALayer3 v1 */ + case 0x06: /* EALayer3 v2 "PCM" */ + case 0x07: /* EALayer3 v2 "Spike" */ + data->skip_size = 0x08; + data->data_size = data->block_size - data->skip_size; + break; + + case 0x0a: /* EATrax */ + data->skip_size = 0x08; + data->data_size = read_32bitBE(data->physical_offset+0x04,streamfile); /* also block_size - 0x08 */ + break; #if 0 - case 0x03: - data_size = block_size - ???; - extra_size = (data_size % 0x800); /* deflated padding */ - - - skip_size = 0x08 + 0x04*data->stream_count; - break; + case 0x0c: /* EA Opus */ + data->skip_size = 0x08; + data->data_size = data->block_size - data->skip_size; + break; #endif - - case 0x05: /* EALayer3 v1 */ - case 0x06: /* EALayer3 v2 "PCM" */ - case 0x07: /* EALayer3 v2 "Spike" */ - data_size = block_size - 0x08; - skip_size = 0x08; - break; - - case 0x0a: /* EATrax */ - data_size = read_32bitBE(data->physical_offset+0x04,streamfile); /* should be block_size - 0x08 */ - skip_size = 0x08; - break; - - default: - return total_read; + default: + return total_read; + } } - /* requested offset is outside current block, try next */ - if (offset >= data->logical_offset + data_size) { - data->physical_offset += block_size; - data->logical_offset += data_size; + /* move to next block */ + if (offset >= data->logical_offset + data->data_size + data->extra_size) { + data->physical_offset += data->block_size; + data->logical_offset += data->data_size + data->extra_size; + data->data_size = 0; + data->extra_size = 0; continue; } - /* reads could fall in the middle of the block */ - intradata_offset = offset - data->logical_offset; - intrablock_offset = skip_size + intradata_offset; + /* read data */ + { + size_t bytes_consumed, bytes_done, to_read; - /* 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... */ + bytes_consumed = offset - data->logical_offset; - /* 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 */ + switch(data->codec) { + case 0x03: { /* EA-XMA */ + if (bytes_consumed < data->data_size) { /* offset falls within actual data */ + 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); + } + else { /* offset falls within logical padded data */ + to_read = data->data_size + data->extra_size - bytes_consumed; + if (to_read > length) + to_read = length; + memset(dest, 0xFF, to_read); /* no real need though, padding is ignored */ + bytes_done = to_read; + } + break; + } - dest += bytes_read; - offset += bytes_read; - length -= bytes_read; + default: + 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); + break; + } - /* block fully read, go next */ - if (intradata_offset + bytes_read == data_size) { - data->physical_offset += block_size; - data->logical_offset += data_size; + total_read += bytes_done; + dest += bytes_done; + offset += bytes_done; + length -= bytes_done; + + if (bytes_done != to_read || bytes_done == 0) { + break; /* error/EOF */ + } } - - if (data->version == 0 && (!data->streamed || block_flag == 0x80)) - break; /* stop on last block */ } return total_read; } + static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { off_t physical_offset, max_physical_offset; - size_t total_size = 0; + size_t logical_size = 0; - if (data->total_size) - return data->total_size; + if (data->logical_size) + return data->logical_size; - physical_offset = data->start_offset; + physical_offset = data->stream_offset; max_physical_offset = get_streamfile_size(streamfile); - /* get size of the underlying, non-blocked data */ + /* get size of the logical stream */ while (physical_offset < max_physical_offset) { - uint32_t block_flag, block_size, data_size; + uint32_t block_flag, block_size, data_size, skip_size; + int i; block_flag = (uint8_t)read_8bit(physical_offset+0x00,streamfile); block_size = read_32bitBE(physical_offset+0x00,streamfile) & 0x00FFFFFF; if (data->version == 0 && block_flag != 0x00 && block_flag != 0x80) - break; /* data/end block expected */ + break; /* unknown block */ if (data->version == 1 && block_flag == 0x48) { physical_offset += block_size; @@ -145,15 +188,21 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { if (data->version == 1 && block_flag == 0x45) break; /* stop on last block (always empty) */ if (data->version == 1 && block_flag != 0x44) - break; /* data block expected */ + break; /* unknown block */ switch(data->codec) { -#if 0 - case 0x03: - data_size = block_size - ???; - data_size += (data_size % 0x800); /* deflated padding */ + case 0x03: /* EA-XMA */ + skip_size = 0x04 + 0x04; + for (i = 0; i < data->stream_number; i++) { + skip_size += read_32bitBE(physical_offset + skip_size, streamfile) / 4; /* why size*4...? */ + } + data_size = read_32bitBE(physical_offset + skip_size, streamfile) / 4; + skip_size += 0x04; /* skip mini header */ + data_size -= 0x04; /* remove mini header */ + if (data_size % XMA_FRAME_SIZE) + data_size += XMA_FRAME_SIZE - (data_size % XMA_FRAME_SIZE); /* extra padding */ break; -#endif + case 0x05: /* EALayer3 v1 */ case 0x06: /* EALayer3 v2 "PCM" */ case 0x07: /* EALayer3 v2 "Spike" */ @@ -161,46 +210,62 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) { break; case 0x0a: /* EATrax */ - data_size = read_32bitBE(physical_offset+0x04,streamfile); /* should be block_size - 0x08 */ + data_size = read_32bitBE(physical_offset+0x04,streamfile); /* also block_size - 0x08 */ break; +#if 0 + case 0x0c: { /* EAOpus */ + size_t done; + data_size = 0; + while (done < block_size - 0x08) { + size_t packet_size = read_16bitBE(physical_offset+0x08+done,streamfile); + done += 0x02 + packet_size; + data_size = 0x1a + packet_size; /* OggS page per Opus packet */ + } + break; + } +#endif default: return 0; } physical_offset += block_size; - total_size += data_size; + logical_size += data_size; if (data->version == 0 && (!data->streamed || block_flag == 0x80)) break; /* stop on last block */ } - if (total_size > get_streamfile_size(streamfile)) { - VGM_LOG("EA SCHL: wrong streamfile total_size\n"); - total_size = 0; + /* logical size can be bigger in EA-XMA though */ + if (physical_offset > get_streamfile_size(streamfile)) { + VGM_LOG("EA EAAC: wrong size %lx\n", physical_offset); + return 0; } - data->total_size = total_size; - return data->total_size; + data->logical_size = logical_size; + return data->logical_size; } /* Prepares custom IO for some blocked EAAudioCore formats, that need clean reads without block headers: - * - EA-XMA: deflated XMA in multistreams (separate 2ch frames) + * - EA-XMA: deflated XMA in multistreams (separate 1/2ch packets) * - EALayer3: MPEG granule 1 can go in the next block (in V2"P" mainly, others could use layout blocked_sns) * - EATrax: ATRAC9 frames can be split between blooks + * - EAOpus: */ -static STREAMFILE* setup_eaac_streamfile(STREAMFILE *streamFile, int version, int codec, int streamed, off_t start_offset, size_t total_size) { +static STREAMFILE* setup_eaac_streamfile(STREAMFILE *streamFile, int version, int codec, int streamed, int stream_number, int stream_count, off_t stream_offset) { STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; eaac_io_data io_data = {0}; size_t io_data_size = sizeof(eaac_io_data); io_data.version = version; io_data.codec = codec; - io_data.start_offset = start_offset; io_data.streamed = streamed; - io_data.total_size = total_size; /* optional */ - io_data.physical_offset = start_offset; + io_data.stream_number = stream_number; + io_data.stream_count = stream_count; + io_data.stream_offset = stream_offset; + io_data.physical_offset = stream_offset; + io_data.logical_size = eaac_io_size(streamFile, &io_data); /* force init */ /* setup subfile */ new_streamFile = open_wrap_streamfile(streamFile); diff --git a/src/meta/ea_schl.c b/src/meta/ea_schl.c index f63e8ac7..2968f4c8 100644 --- a/src/meta/ea_schl.c +++ b/src/meta/ea_schl.c @@ -93,7 +93,7 @@ typedef struct { int big_endian; int loop_flag; - int codec_version; + int codec_config; } ea_header; static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int total_streams); @@ -447,7 +447,7 @@ static VGMSTREAM * parse_schl_block(STREAMFILE *streamFile, off_t offset, int to if (guess_endianness32bit(offset + 0x04, streamFile)) { /* size is always LE, except in early SS/MAC */ header_size = read_32bitBE(offset + 0x04, streamFile); - ea.codec_version |= 0x02; + ea.codec_config |= 0x02; } else { header_size = read_32bitLE(offset + 0x04, streamFile); @@ -573,7 +573,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_ vgmstream->loop_end_sample = ea->loop_end; vgmstream->codec_endian = ea->big_endian; - vgmstream->codec_version = ea->codec_version; + vgmstream->codec_config = ea->codec_config; vgmstream->meta_type = is_bnk ? meta_EA_BNK : meta_EA_SCHL; @@ -1120,7 +1120,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be if (!is_bnk) { if (ea->codec2 == EA_CODEC2_GCADPCM) { if (ea->platform == EA_PLATFORM_3DS) - ea->codec_version |= 0x01; + ea->codec_config |= 0x01; } else if (ea->codec2 == EA_CODEC2_EAXA) { /* EA-XA has ADPCM hist in earlier versions */ @@ -1128,11 +1128,11 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be /* V2: consoles only */ /* V3: never */ if (ea->version <= EA_VERSION_V1) { - ea->codec_version |= 0x01; + ea->codec_config |= 0x01; } else if (ea->version == EA_VERSION_V2) { if (ea->platform == EA_PLATFORM_PS2 || ea->platform == EA_PLATFORM_GC_WII || ea->platform == EA_PLATFORM_XBOX) - ea->codec_version |= 0x01; + ea->codec_config |= 0x01; } } } diff --git a/src/meta/ea_schl_fixed.c b/src/meta/ea_schl_fixed.c index 1703c0ad..c374f874 100644 --- a/src/meta/ea_schl_fixed.c +++ b/src/meta/ea_schl_fixed.c @@ -75,14 +75,8 @@ VGMSTREAM * init_vgmstream_ea_schl_fixed(STREAMFILE *streamFile) { } - /* open files; channel offsets are updated below */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - - /* setup first block to update offsets */ - block_update_ea_schl(start_offset,vgmstream); - - return vgmstream; fail: diff --git a/src/meta/ea_swvr.c b/src/meta/ea_swvr.c index 7321d253..55ed54d3 100644 --- a/src/meta/ea_swvr.c +++ b/src/meta/ea_swvr.c @@ -61,7 +61,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { total_subsongs = 1; block_id = read_32bit(start_offset, streamFile); - /* files are basically headerless so we inspect blocks the first block + /* files are basically headerless so we inspect the first block * Freekstyle uses multiblocks/subsongs (though some subsongs may be clones?) */ switch(block_id) { case 0x5641474D: /* "VAGM" */ @@ -121,12 +121,12 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_EA_SWVR; vgmstream->sample_rate = sample_rate; vgmstream->codec_endian = big_endian; vgmstream->num_streams = total_subsongs; vgmstream->stream_size = get_streamfile_size(streamFile) / total_subsongs; /* approx... */ - vgmstream->meta_type = meta_EA_SWVR; vgmstream->coding_type = coding; vgmstream->layout_type = layout_blocked_ea_swvr; /* DSP coefs are loaded per block */ @@ -141,7 +141,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { vgmstream->stream_index = target_subsong; /* needed to skip other subsong-blocks */ vgmstream->next_block_offset = start_offset; do { - block_update_ea_swvr(vgmstream->next_block_offset,vgmstream); + block_update(vgmstream->next_block_offset,vgmstream); switch(vgmstream->coding_type) { case coding_PSX: num_samples = ps_bytes_to_samples(vgmstream->current_block_size,1); break; case coding_NGC_DSP: num_samples = dsp_bytes_to_samples(vgmstream->current_block_size,1); break; @@ -151,6 +151,7 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { vgmstream->num_samples += num_samples; } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset, vgmstream); } if (loop_flag) { @@ -158,7 +159,6 @@ VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE *streamFile) { vgmstream->loop_end_sample = vgmstream->num_samples; } - block_update_ea_swvr(start_offset, vgmstream); return vgmstream; fail: diff --git a/src/meta/ea_wve_ad10.c b/src/meta/ea_wve_ad10.c index 494a6b46..53a2a6ac 100644 --- a/src/meta/ea_wve_ad10.c +++ b/src/meta/ea_wve_ad10.c @@ -39,14 +39,13 @@ VGMSTREAM * init_vgmstream_ea_wve_ad10(STREAMFILE *streamFile) { { vgmstream->next_block_offset = start_offset; do { - block_update_ea_wve_ad10(vgmstream->next_block_offset,vgmstream); + block_update(vgmstream->next_block_offset,vgmstream); vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset, vgmstream); } - block_update_ea_wve_ad10(start_offset, vgmstream); - return vgmstream; fail: diff --git a/src/meta/ea_wve_au00.c b/src/meta/ea_wve_au00.c index a4aea7a4..72d32849 100644 --- a/src/meta/ea_wve_au00.c +++ b/src/meta/ea_wve_au00.c @@ -28,8 +28,8 @@ VGMSTREAM * init_vgmstream_ea_wve_au00(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = 22050; vgmstream->meta_type = meta_EA_WVE_AU00; + vgmstream->sample_rate = 22050; /* You'd think they'd use coding_EA_XA_int but instead it's PS-ADPCM without flags and 0x0f frame size * (equivalent to configurable PS-ADPCM), surely to shoehorn EA-XA sizes into the PS1 hardware decoder */ @@ -44,13 +44,13 @@ VGMSTREAM * init_vgmstream_ea_wve_au00(STREAMFILE *streamFile) { { vgmstream->next_block_offset = start_offset; do { - block_update_ea_wve_au00(vgmstream->next_block_offset,vgmstream); + block_update(vgmstream->next_block_offset,vgmstream); vgmstream->num_samples += ps_cfg_bytes_to_samples(vgmstream->current_block_size, vgmstream->interleave_block_size, 1); } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset, vgmstream); } - block_update_ea_wve_au00(start_offset, vgmstream); return vgmstream; fail: diff --git a/src/meta/excitebots.c b/src/meta/excitebots.c index 5ba06f00..2f5de05b 100644 --- a/src/meta/excitebots.c +++ b/src/meta/excitebots.c @@ -82,6 +82,7 @@ VGMSTREAM * init_vgmstream_eb_sfx(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_EB_SFX; + vgmstream->allow_dual_stereo = 1; /* open the file for reading */ { diff --git a/src/meta/genh.c b/src/meta/genh.c index 70cac106..22a39025 100644 --- a/src/meta/genh.c +++ b/src/meta/genh.c @@ -325,6 +325,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { vgmstream->coding_type = coding; vgmstream->meta_type = meta_GENH; + vgmstream->allow_dual_stereo = 1; if ( !vgmstream_open_stream(vgmstream,streamFile,genh.start_offset) ) diff --git a/src/meta/gsp_gsb.c b/src/meta/gsp_gsb.c index d51ec240..4163db82 100644 --- a/src/meta/gsp_gsb.c +++ b/src/meta/gsp_gsb.c @@ -2,10 +2,10 @@ #include "../layout/layout.h" #include "../coding/coding.h" -/* GSP+GSB - from Tecmo's Super Swing Golf 1 & 2 (WII), Quantum Theory (PS3/X360) */ +/* GSP+GSB - from Tecmo's Super Swing Golf 1 & 2 (Wii), Quantum Theory (PS3/X360) */ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileGSP = NULL; + STREAMFILE * streamHeader = NULL; int loop_flag, channel_count; off_t start_offset, chunk_offset, first_offset; size_t datasize; @@ -16,68 +16,70 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { if (!check_extensions(streamFile,"gsb")) goto fail; - streamFileGSP = open_streamfile_by_ext(streamFile, "gsp"); - if (!streamFileGSP) goto fail; + streamHeader = open_streamfile_by_ext(streamFile, "gsp"); + if (!streamHeader) goto fail; /* check header */ - if (read_32bitBE(0x00,streamFileGSP) != 0x47534E44) /* "GSND" */ + if (read_32bitBE(0x00,streamHeader) != 0x47534E44) /* "GSND" */ goto fail; /* 0x04: version?, 0x08: 1?, 0x0c: 0?, */ - first_offset = read_32bitBE(0x10,streamFileGSP); /* usually 0x14*/ + first_offset = read_32bitBE(0x10,streamHeader); /* usually 0x14*/ + + if (!find_chunk_be(streamHeader, 0x44415441,first_offset,1, &chunk_offset,NULL)) goto fail; /*"DATA"*/ + channel_count = read_16bitBE(chunk_offset+0x0e,streamHeader); + if (!find_chunk_be(streamHeader, 0x42534943,first_offset,1, &chunk_offset,NULL)) goto fail; /*"BSIC"*/ + loop_flag = read_8bit(chunk_offset+0x0c,streamHeader); + + start_offset = 0x00; - if (!find_chunk_be(streamFileGSP, 0x44415441,first_offset,1, &chunk_offset,NULL)) goto fail; /*"DATA"*/ - channel_count = read_16bitBE(chunk_offset+0x0e,streamFileGSP); - if (!find_chunk_be(streamFileGSP, 0x42534943,first_offset,1, &chunk_offset,NULL)) goto fail; /*"BSIC"*/ - loop_flag = read_8bit(chunk_offset+0x0c,streamFileGSP); /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_GSP_GSB; - if (!find_chunk_be(streamFileGSP, 0x48454144,first_offset,1, &chunk_offset,NULL)) goto fail; /*"HEAD"*/ + + if (!find_chunk_be(streamHeader, 0x48454144,first_offset,1, &chunk_offset,NULL)) goto fail; /*"HEAD"*/ /* 0x00: header_size, 0x04: num_chunks */ - if (!find_chunk_be(streamFileGSP, 0x44415441,first_offset,1, &chunk_offset,NULL)) goto fail; /*"DATA"*/ + if (!find_chunk_be(streamHeader, 0x44415441,first_offset,1, &chunk_offset,NULL)) goto fail; /*"DATA"*/ /* 0x00: filesize, 0x0c: always 10?, 0x10: always 0?, 0x18: always 0? */ - datasize = read_32bitBE(chunk_offset+0x00,streamFileGSP); - codec_id = read_32bitBE(chunk_offset+0x04,streamFileGSP); - vgmstream->sample_rate = read_32bitBE(chunk_offset+0x08,streamFileGSP); - vgmstream->channels = channel_count; /* 0x0e */ - vgmstream->num_samples = read_32bitBE(chunk_offset+0x14,streamFileGSP); + datasize = read_32bitBE(chunk_offset+0x00,streamHeader); + codec_id = read_32bitBE(chunk_offset+0x04,streamHeader); + vgmstream->sample_rate = read_32bitBE(chunk_offset+0x08,streamHeader); + vgmstream->num_samples = read_32bitBE(chunk_offset+0x14,streamHeader); /* 0x1c: unk (varies with codec_id) */ - if (!find_chunk_be(streamFileGSP, 0x42534943,first_offset,1, &chunk_offset,NULL)) goto fail; /*"BSIC"*/ + if (!find_chunk_be(streamHeader, 0x42534943,first_offset,1, &chunk_offset,NULL)) goto fail; /*"BSIC"*/ /* 0x00+: probably volume/pan/etc */ - vgmstream->loop_start_sample = read_32bitBE(chunk_offset+0x10,streamFileGSP); - vgmstream->loop_end_sample = read_32bitBE(chunk_offset+0x14,streamFileGSP); + vgmstream->loop_start_sample = read_32bitBE(chunk_offset+0x10,streamHeader); + vgmstream->loop_end_sample = read_32bitBE(chunk_offset+0x14,streamHeader); - //if (!find_chunk_be(streamFileGSP, 0x4E414D45,first_offset,1, &chunk_offset,NULL)) goto fail; /*"NAME"*/ + //if (!find_chunk_be(streamHeader, 0x4E414D45,first_offset,1, &chunk_offset,NULL)) goto fail; /*"NAME"*/ /* 0x00: name_size, 0x04+: name*/ - start_offset = 0x0; - switch (codec_id) { - case 4: { /* DSP */ + case 0x04: { /* DSP [Super Swing Golf (Wii)] */ size_t block_header_size; size_t num_blocks; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_blocked_gsb; - if (!find_chunk_be(streamFileGSP, 0x47434558,first_offset,1, &chunk_offset,NULL)) goto fail; /*"GCEX"*/ + if (!find_chunk_be(streamHeader, 0x47434558,first_offset,1, &chunk_offset,NULL)) goto fail; /*"GCEX"*/ - //vgmstream->current_block_size = read_32bitBE(chunk_offset+0x00,streamFileGSP); - block_header_size = read_32bitBE(chunk_offset+0x04,streamFileGSP); - num_blocks = read_32bitBE(chunk_offset+0x08,streamFileGSP); + //vgmstream->current_block_size = read_32bitBE(chunk_offset+0x00,streamHeader); + block_header_size = read_32bitBE(chunk_offset+0x04,streamHeader); + num_blocks = read_32bitBE(chunk_offset+0x08,streamHeader); vgmstream->num_samples = (datasize - block_header_size * num_blocks) / 8 / vgmstream->channels * 14; /* 0x0c+: unk */ - dsp_read_coefs_be(vgmstream, streamFileGSP, chunk_offset+0x18, 0x30); + dsp_read_coefs_be(vgmstream, streamHeader, chunk_offset+0x18, 0x30); break; } #ifdef VGM_USE_FFMPEG - case 8: { /* ATRAC3 */ + case 0x08: { /* ATRAC3 [Quantum Theory (PS3)] */ ffmpeg_codec_data *ffmpeg_data = NULL; uint8_t buf[100]; int32_t bytes, block_size, encoder_delay, joint_stereo, max_samples; @@ -105,15 +107,15 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { break; } - case 9: { /* XMA2 */ + case 0x09: { /* XMA2 [Quantum Theory (PS3)] */ ffmpeg_codec_data *ffmpeg_data = NULL; uint8_t buf[200]; int32_t bytes; - if (!find_chunk_be(streamFileGSP, 0x584D4558,first_offset,1, &chunk_offset,NULL)) goto fail; /*"XMEX"*/ + if (!find_chunk_be(streamHeader, 0x584D4558,first_offset,1, &chunk_offset,NULL)) goto fail; /*"XMEX"*/ /* 0x00: fmt0x166 header (BE), 0x34: seek table */ - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, chunk_offset,0x34, datasize, streamFileGSP, 1); + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,200, chunk_offset,0x34, datasize, streamHeader, 1); if (bytes <= 0) goto fail; ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,datasize); @@ -128,18 +130,15 @@ VGMSTREAM * init_vgmstream_gsp_gsb(STREAMFILE *streamFile) { default: goto fail; } - vgmstream->meta_type = meta_GSP_GSB; - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - - close_streamfile(streamFileGSP); + close_streamfile(streamHeader); return vgmstream; fail: - if (streamFileGSP) close_streamfile(streamFileGSP); + close_streamfile(streamHeader); close_vgmstream(vgmstream); return NULL; } diff --git a/src/meta/h4m.c b/src/meta/h4m.c index e077f34a..80540977 100644 --- a/src/meta/h4m.c +++ b/src/meta/h4m.c @@ -67,7 +67,7 @@ VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_streams = total_subsongs; vgmstream->stream_size = get_streamfile_size(streamFile) / total_subsongs; /* approx... */ - vgmstream->codec_version = format; /* for blocks */ + vgmstream->codec_config = format; /* for blocks */ vgmstream->meta_type = meta_H4M; vgmstream->layout_type = layout_blocked_h4m; @@ -89,17 +89,18 @@ VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile) { /* calc num_samples manually */ { + vgmstream->stream_index = target_subsong; /* extra setup for H4M */ + vgmstream->full_block_size = 0; /* extra setup for H4M */ vgmstream->next_block_offset = start_offset; do { - block_update_h4m(vgmstream->next_block_offset,vgmstream); + block_update(vgmstream->next_block_offset,vgmstream); vgmstream->num_samples += vgmstream->current_block_samples; } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); vgmstream->full_block_size = 0; /* extra cleanup for H4M */ + block_update(start_offset, vgmstream); } - block_update_h4m(start_offset, vgmstream); - return vgmstream; fail: diff --git a/src/meta/idsp.c b/src/meta/idsp.c index aa21bdc9..c1fdca92 100644 --- a/src/meta/idsp.c +++ b/src/meta/idsp.c @@ -92,81 +92,6 @@ fail: return NULL; } -/* IDSP - from Next Level games [Mario Strikers Charged (Wii)] */ -VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag = 1; - int channel_count; - off_t start_offset; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("idsp",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x49445350) /* IDSP */ - goto fail; - - channel_count = read_32bitBE(0x24,streamFile); - - if (channel_count > 8) - goto fail; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - start_offset = (channel_count*0x60)+0xC; - vgmstream->sample_rate = read_32bitBE(0x14,streamFile); - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = read_32bitBE(0x0C,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = (read_32bitBE(0x0C,streamFile)); - } - - vgmstream->interleave_block_size = read_32bitBE(0x04,streamFile); - vgmstream->interleave_last_block_size = ((vgmstream->num_samples/7*8)%(vgmstream->interleave_block_size)/vgmstream->channels); - vgmstream->layout_type = layout_interleave; - - vgmstream->meta_type = meta_IDSP_NL; - - if (vgmstream->coding_type == coding_NGC_DSP) { - int i; - for (i=0;i<16;i++) { - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x28+i*2,streamFile); - } - if (vgmstream->channels) { - for (i=0;i<16;i++) { - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x88+i*2,streamFile); - } - } - } - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - /* IDSP - from Inevitable Entertainment games [Defender (GC)] */ VGMSTREAM * init_vgmstream_idsp_ie(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; diff --git a/src/meta/ivaud.c b/src/meta/ivaud.c index 96efa670..8a85a556 100644 --- a/src/meta/ivaud.c +++ b/src/meta/ivaud.c @@ -73,10 +73,6 @@ VGMSTREAM * init_vgmstream_ivaud(STREAMFILE *streamFile) { if (!vgmstream_open_stream(vgmstream,streamFile,ivaud.stream_offset)) goto fail; - - if (vgmstream->layout_type == layout_blocked_ivaud) - block_update_ivaud(ivaud.stream_offset, vgmstream); - return vgmstream; fail: diff --git a/src/meta/kma9.c b/src/meta/kma9.c index 16f2e783..e1049cd7 100644 --- a/src/meta/kma9.c +++ b/src/meta/kma9.c @@ -1,20 +1,21 @@ #include "meta.h" #include "../coding/coding.h" +#include "kma9_streamfile.h" -/* KMA9 - Koei Tecmo's custom ATRAC9 [Nobunaga no Yabou - Souzou (Vita)] */ + +/* KMA9 - Koei Tecmo's interleaved ATRAC9 [Nobunaga no Yabou - Souzou (Vita)] */ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; + STREAMFILE* temp_streamFile = NULL; off_t start_offset; - size_t stream_size; + size_t stream_size, interleave; int loop_flag, channel_count; int total_subsongs = 0, target_subsong = streamFile->stream_index; - /* check extension */ + /* checks */ if ( !check_extensions(streamFile,"km9") ) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x4B4D4139) /* "KMA9" */ goto fail; @@ -26,6 +27,7 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* 0x0c: unknown */ + interleave = read_32bitLE(0x10,streamFile); /* 1 superframe */ stream_size = read_32bitLE(0x14,streamFile); /* per subsong */ @@ -45,14 +47,9 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { { atrac9_config cfg = {0}; - cfg.type = ATRAC9_KMA9; cfg.channels = vgmstream->channels; - cfg.config_data = read_32bitBE(0x5c,streamFile); cfg.encoder_delay = read_32bitLE(0x20,streamFile); - - cfg.interleave_skip = read_32bitLE(0x10,streamFile); /* 1 superframe */ - cfg.subsong_skip = total_subsongs; - start_offset += (target_subsong-1) * cfg.interleave_skip * (cfg.subsong_skip-1); + cfg.config_data = read_32bitBE(0x5c,streamFile); vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; @@ -63,17 +60,24 @@ VGMSTREAM * init_vgmstream_kma9(STREAMFILE *streamFile) { vgmstream->loop_start_sample -= cfg.encoder_delay; //vgmstream->loop_end_sample -= cfg.encoder_delay; } + + /* KMA9 interleaves one ATRAC9 frame per subsong */ + temp_streamFile = setup_kma9_streamfile(streamFile, start_offset, stream_size, interleave, (target_subsong-1), total_subsongs); + if (!temp_streamFile) goto fail; + start_offset = 0; } #else goto fail; #endif /* open the file for reading */ - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + if ( !vgmstream_open_stream(vgmstream, temp_streamFile, start_offset) ) goto fail; + close_streamfile(temp_streamFile); return vgmstream; fail: + close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; } diff --git a/src/meta/kma9_streamfile.h b/src/meta/kma9_streamfile.h new file mode 100644 index 00000000..34f21dde --- /dev/null +++ b/src/meta/kma9_streamfile.h @@ -0,0 +1,148 @@ +#ifndef _KM9_STREAMFILE_H_ +#define _KM9_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* config */ + int stream_number; + int stream_count; + size_t interleave_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; +} kma9_io_data; + + +static size_t kma9_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, kma9_io_data* data) { + size_t total_read = 0; + + /* 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) { + data->skip_size = data->interleave_size * data->stream_number; + data->data_size = data->interleave_size; + } + + /* move to next block */ + if (offset >= data->logical_offset + data->data_size) { + data->physical_offset += data->interleave_size*data->stream_count; + 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 kma9_io_size(STREAMFILE *streamfile, kma9_io_data* data) { + off_t physical_offset = data->stream_offset; + off_t max_physical_offset = get_streamfile_size(streamfile); + size_t logical_size = 0; + + if (data->logical_size) + return data->logical_size; + + /* get size of the logical stream */ + while (physical_offset < max_physical_offset) { + logical_size += data->interleave_size; + physical_offset += data->interleave_size*data->stream_count; + } + + if (logical_size > max_physical_offset) + return 0; + if (logical_size != data->stream_size) + return 0; + data->logical_size = logical_size; + return data->logical_size; +} + + +/* Prepares custom IO for KMA9, which interleaves ATRAC9 frames */ +static STREAMFILE* setup_kma9_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t stream_size, size_t interleave_size, int stream_number, int stream_count) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + kma9_io_data io_data = {0}; + size_t io_data_size = sizeof(kma9_io_data); + + io_data.stream_number = stream_number; + io_data.stream_count = stream_count; + io_data.stream_offset = stream_offset; + io_data.stream_size = stream_size; + io_data.interleave_size = interleave_size; + io_data.physical_offset = stream_offset; + io_data.logical_size = kma9_io_size(streamFile, &io_data); /* force init */ + + if (io_data.logical_size == 0) { + VGM_LOG("KMA9: 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, kma9_io_read,kma9_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + + +#endif /* _KM9_STREAMFILE_H_ */ diff --git a/src/meta/kraw.c b/src/meta/kraw.c index 2fd4123e..a14a2146 100644 --- a/src/meta/kraw.c +++ b/src/meta/kraw.c @@ -37,6 +37,7 @@ VGMSTREAM * init_vgmstream_kraw(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_KRAW; + vgmstream->allow_dual_stereo = 1; /* open the file for reading */ { diff --git a/src/meta/meta.h b/src/meta/meta.h index 38e6fe3c..ab24b8f9 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -35,7 +35,9 @@ VGMSTREAM * init_vgmstream_ngc_mpdsp(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ngc_dsp_csmp(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_rfrm(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile); @@ -59,13 +61,15 @@ VGMSTREAM * init_vgmstream_ps2_exst(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_svag(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ps2_mic(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_raw(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile); +VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_psx_gms(STREAMFILE *streamFile); @@ -107,8 +111,6 @@ typedef struct { } ogg_vorbis_meta_info_t; VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callbacks *callbacks, off_t other_header_bytes, const ogg_vorbis_meta_info_t *ovmi); - -VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE * streamFile); #endif VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile); @@ -127,7 +129,9 @@ VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *streamFile, uint64_t start VGMSTREAM * init_vgmstream_akb_mp4(STREAMFILE *streamFile); #endif -VGMSTREAM * init_vgmstream_sfl(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_sfl_ogg(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_sadb(STREAMFILE *streamFile); @@ -563,8 +567,6 @@ VGMSTREAM * init_vgmstream_pc_adp_otns(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_eb_sfx(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_eb_sf0(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_ps3_klbs(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_ps2_mtaf(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_tun(STREAMFILE* streamFile); diff --git a/src/meta/mib_mih.c b/src/meta/mib_mih.c new file mode 100644 index 00000000..b3f3f638 --- /dev/null +++ b/src/meta/mib_mih.c @@ -0,0 +1,60 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* MIB+MIH - SCEE MultiStream interleaved bank (header+data) [namCollection: Ace Combat 2 (PS2), Rampage: Total Destruction (PS2)] */ +VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamHeader = NULL; + off_t start_offset; + size_t data_size, frame_size, frame_last, frame_count; + int channel_count, loop_flag; + + /* check extension */ + if (!check_extensions(streamFile, "mib")) + goto fail; + + streamHeader = open_streamfile_by_ext(streamFile,"mih"); + if (!streamHeader) goto fail; + + if (read_32bitBE(0x00,streamHeader) != 0x40000000) /* header size */ + goto fail; + + loop_flag = 0; /* MIB+MIH don't PS-ADPCM loop flags */ + channel_count = read_32bitLE(0x08,streamHeader); + start_offset = 0x00; + + /* 0x04: padding (0x20, MIH header must be multiple of 0x40) */ + frame_last = (uint16_t)read_16bitLE(0x05,streamHeader); + frame_size = read_32bitLE(0x10,streamHeader); + frame_count = read_32bitLE(0x14,streamHeader); + if (frame_count == 0) { /* rarely [Gladius (PS2)] */ + frame_count = get_streamfile_size(streamFile) / (frame_size * channel_count); + } + + data_size = frame_count * frame_size; + data_size -= frame_last ? (frame_size-frame_last) : 0; /* last frame has less usable data */ + data_size *= channel_count; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bitLE(0x0C,streamHeader); + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + + vgmstream->meta_type = meta_PS2_MIB_MIH; + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = frame_size; + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + close_streamfile(streamHeader); + return vgmstream; + +fail: +close_streamfile(streamHeader); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/mus_acm.c b/src/meta/mus_acm.c index 84cfc4e2..fb87869d 100644 --- a/src/meta/mus_acm.c +++ b/src/meta/mus_acm.c @@ -50,9 +50,11 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) { case 0x97280301: /* ACM header id [Planescape: Torment (PC)] */ data->segments[i] = init_vgmstream_acm(temp_streamFile); break; +#ifdef VGM_USE_VORBIS case 0x4F676753: /* "OggS" [Planescape: Torment Enhanced Edition (PC)] */ data->segments[i] = init_vgmstream_ogg_vorbis(temp_streamFile); break; +#endif default: data->segments[i] = NULL; break; @@ -93,9 +95,7 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) { vgmstream->meta_type = meta_MUS_ACM; vgmstream->coding_type = data->segments[0]->coding_type; vgmstream->layout_type = layout_segmented; - vgmstream->layout_data = data; - data->loop_segment = loop_start_index; clean_mus(mus_filenames, segment_count); return vgmstream; diff --git a/src/meta/nds_hwas.c b/src/meta/nds_hwas.c index 37d78ff2..963a8c74 100644 --- a/src/meta/nds_hwas.c +++ b/src/meta/nds_hwas.c @@ -12,8 +12,6 @@ VGMSTREAM * init_vgmstream_nds_hwas(STREAMFILE *streamFile) { /* .hwas: usually in archives but also found named (ex. Guitar Hero On Tour) */ if (!check_extensions(streamFile,"hwas")) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x73617768) /* "sawh" */ goto fail; @@ -27,23 +25,18 @@ VGMSTREAM * init_vgmstream_nds_hwas(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_NDS_HWAS; vgmstream->sample_rate = read_32bitLE(0x08,streamFile); vgmstream->num_samples = ima_bytes_to_samples(read_32bitLE(0x14,streamFile), channel_count); vgmstream->loop_start_sample = ima_bytes_to_samples(read_32bitLE(0x10,streamFile), channel_count); //assumed, always 0 vgmstream->loop_end_sample = ima_bytes_to_samples(read_32bitLE(0x18,streamFile), channel_count); - vgmstream->meta_type = meta_NDS_HWAS; - vgmstream->coding_type = coding_IMA_int; vgmstream->layout_type = layout_blocked_hwas; vgmstream->full_block_size = read_32bitLE(0x04,streamFile); /* usually 0x2000, 0x4000 or 0x8000 */ - /* open the file for reading by each channel */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - - block_update_hwas(start_offset, vgmstream); - return vgmstream; fail: diff --git a/src/meta/ngc_dsp_std.c b/src/meta/ngc_dsp_std.c index 7908131a..7f1cdcdd 100644 --- a/src/meta/ngc_dsp_std.c +++ b/src/meta/ngc_dsp_std.c @@ -5,7 +5,6 @@ /* If these variables are packed properly in the struct (one after another) * then this is actually how they are laid out in the file, albeit big-endian */ - struct dsp_header { uint32_t sample_count; uint32_t nibble_count; @@ -66,179 +65,128 @@ static int read_dsp_header_le(struct dsp_header *header, off_t offset, STREAMFIL return read_dsp_header_endian(header, offset, file, 0); } - -static void setup_vgmstream_dsp(VGMSTREAM* vgmstream, struct dsp_header* ch_header) { - int i, j; - - /* set coeffs and initial history (usually 0) */ - for (i = 0; i < vgmstream->channels; i++){ - for (j = 0; j < 16; j++) { - vgmstream->ch[i].adpcm_coef[j] = ch_header[i].coef[j]; - } - vgmstream->ch[i].adpcm_history1_16 = ch_header[i].initial_hist1; - vgmstream->ch[i].adpcm_history2_16 = ch_header[i].initial_hist2; - } -} - -static int dsp_load_header_endian(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing, int big_endian) { - int i; - - /* load standard dsp header per channel */ - for (i = 0; i < channels; i++) { - if (read_dsp_header_endian(&ch_header[i], offset + i*spacing, streamFile, big_endian)) - goto fail; - } - - return 1; -fail: - return 0; -} -static int dsp_load_header(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing) { - return dsp_load_header_endian(ch_header, channels, streamFile, offset, spacing, 1); -} -//static int dsp_load_header_le(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing) { -// return dsp_load_header_endian(ch_header, channels, streamFile, offset, spacing, 0); -//} -static int check_dsp_format(struct dsp_header* ch_header, int channels) { - int i; - - /* check type==0 and gain==0 */ - for (i = 0; i < channels; i++) { - if (ch_header[i].format || ch_header[i].gain) - goto fail; - } - - return 1; -fail: - return 0; -} -static int check_dsp_samples(struct dsp_header* ch_header, int channels) { - int i; - - /* check for agreement between channels */ - for (i = 0; i < channels - 1; i++) { - if (ch_header[i].sample_count != ch_header[i+1].sample_count || - ch_header[i].nibble_count != ch_header[i+1].nibble_count || - ch_header[i].sample_rate != ch_header[i+1].sample_rate || - ch_header[i].loop_flag != ch_header[i+1].loop_flag || - ch_header[i].loop_start_offset != ch_header[i+1].loop_start_offset || - ch_header[i].loop_end_offset != ch_header[i+1].loop_end_offset ) { - goto fail; - } - } - - return 1; -fail: - return 0; -} -static int check_dsp_initial_ps(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t interleave) { - int i; - - /* check initial predictor/scale */ - for (i = 0; i < channels; i++) { - off_t start_offset = offset + i*interleave; - if (ch_header[i].initial_ps != (uint8_t)read_8bit(start_offset, streamFile)){ - goto fail; - } - } - - return 1; -fail: - return 0; -} -static int check_dsp_loop_ps(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t interleave) { - int i; - - if (!ch_header[0].loop_flag) - return 1; - - /* check loop predictor/scale */ - for (i = 0; i < channels; i++) { - off_t loop_offset = ch_header[i].loop_start_offset; - if (interleave) { - loop_offset = loop_offset / 16 * 8; - loop_offset = (loop_offset / interleave * interleave * channels) + (loop_offset % interleave); - } - - if (ch_header[i].loop_ps != (uint8_t)read_8bit(offset + i*interleave + loop_offset,streamFile)) - goto fail; - } - - return 1; -fail: - return 0; -} - /* ********************************* */ -/* common parser config as most DSPs are basically the same with minor changes */ typedef struct { + /* basic config */ int little_endian; int channel_count; int max_channels; - int force_loop; /* force full loop */ - int fix_looping; /* fix loop end going past num_samples */ - int fix_loop_start; /* weird files with bad loop start */ - int single_header; /* all channels share header, thus totals are off */ - int ignore_header_agreement; /* sometimes there are minor differences between headers */ - int ignore_loop_check; /* loop info in header should match data, but sometimes it's weird */ //todo check if needed anymore - - off_t header_offset; - size_t header_spacing; - off_t start_offset; - size_t interleave; + off_t header_offset; /* standard DSP header */ + size_t header_spacing; /* distance between DSP header of other channels */ + off_t start_offset; /* data start */ + size_t interleave; /* distance between data of other channels */ + size_t interleave_last; /* same, in the last block */ meta_t meta_type; + + /* hacks */ + int force_loop; /* force full loop */ + int force_loop_seconds; /* force loop, but must be longer than this (to catch jingles) */ + int fix_looping; /* fix loop end going past num_samples */ + int fix_loop_start; /* weird files with bad loop start */ + int single_header; /* all channels share header, thus totals are off */ + int ignore_header_agreement; /* sometimes there are minor differences between headers */ } dsp_meta; #define COMMON_DSP_MAX_CHANNELS 6 + +/* Common parser for most DSPs that are basically the same with minor changes. + * Custom variants will just concatenate or interleave standard DSP headers and data, + * so we make sure to validate read vs expected values, based on dsp_meta config. */ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *dspm) { VGMSTREAM * vgmstream = NULL; + int i, j; int loop_flag; struct dsp_header ch_header[COMMON_DSP_MAX_CHANNELS]; + if (dspm->channel_count > dspm->max_channels) goto fail; if (dspm->channel_count > COMMON_DSP_MAX_CHANNELS) goto fail; - - /* read dsp */ - if (!dsp_load_header_endian(ch_header, dspm->channel_count, streamFile,dspm->header_offset,dspm->header_spacing, !dspm->little_endian)) - goto fail; - - if (dspm->fix_loop_start) { - int i; + /* load standard DSP header per channel */ + { + for (i = 0; i < dspm->channel_count; i++) { + if (read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, streamFile, !dspm->little_endian)) + goto fail; + } + } + + /* fix bad/fixed value in loop start */ + if (dspm->fix_loop_start) { for (i = 0; i < dspm->channel_count; i++) { - /* bad/fixed value in loop start */ if (ch_header[i].loop_flag) ch_header[i].loop_start_offset = 0x00; } } - if (!check_dsp_format(ch_header, dspm->channel_count)) - goto fail; - - if (!dspm->ignore_header_agreement && !check_dsp_samples(ch_header, dspm->channel_count)) - goto fail; - - if (dspm->single_header && !check_dsp_initial_ps(ch_header, 1, streamFile,dspm->start_offset,dspm->interleave)) - goto fail; - if (!dspm->single_header && !check_dsp_initial_ps(ch_header, dspm->channel_count, streamFile,dspm->start_offset,dspm->interleave)) - goto fail; - - if (!dspm->ignore_loop_check) { - if (dspm->single_header && !check_dsp_loop_ps(ch_header, 1, streamFile,dspm->start_offset,dspm->interleave)) - goto fail; - if (!dspm->single_header && !check_dsp_loop_ps(ch_header, dspm->channel_count, streamFile,dspm->start_offset,dspm->interleave)) - goto fail; + /* check type==0 and gain==0 */ + { + for (i = 0; i < dspm->channel_count; i++) { + if (ch_header[i].format || ch_header[i].gain) + goto fail; + } } + /* check for agreement between channels */ + if (!dspm->ignore_header_agreement) { + for (i = 0; i < dspm->channel_count - 1; i++) { + if (ch_header[i].sample_count != ch_header[i+1].sample_count || + ch_header[i].nibble_count != ch_header[i+1].nibble_count || + ch_header[i].sample_rate != ch_header[i+1].sample_rate || + ch_header[i].loop_flag != ch_header[i+1].loop_flag || + ch_header[i].loop_start_offset != ch_header[i+1].loop_start_offset || + ch_header[i].loop_end_offset != ch_header[i+1].loop_end_offset) { + goto fail; + } + } + } + + /* check expected initial predictor/scale */ + { + int channels = dspm->channel_count; + if (dspm->single_header) + channels = 1; + + for (i = 0; i < channels; i++) { + off_t channel_offset = dspm->start_offset + i*dspm->interleave; + if (ch_header[i].initial_ps != (uint8_t)read_8bit(channel_offset, streamFile)) + goto fail; + } + } + + /* check expected loop predictor/scale */ + if (ch_header[0].loop_flag) { + int channels = dspm->channel_count; + if (dspm->single_header) + channels = 1; + + for (i = 0; i < channels; i++) { + off_t loop_offset = ch_header[i].loop_start_offset; + if (dspm->interleave) { + loop_offset = loop_offset / 16 * 8; + loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave); + } + + if (ch_header[i].loop_ps != (uint8_t)read_8bit(dspm->start_offset + i*dspm->interleave + loop_offset,streamFile)) + goto fail; + } + } + + /* all done, must be DSP */ loop_flag = ch_header[0].loop_flag; - if (dspm->force_loop) + if (!loop_flag && dspm->force_loop) { loop_flag = 1; + if (dspm->force_loop_seconds && + ch_header[0].sample_count < dspm->force_loop_seconds*ch_header[0].sample_rate) { + loop_flag = 0; + } + } + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(dspm->channel_count,loop_flag); @@ -257,8 +205,18 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d if (dspm->interleave == 0 || vgmstream->coding_type == coding_NGC_DSP_subint) vgmstream->layout_type = layout_none; vgmstream->interleave_block_size = dspm->interleave; + vgmstream->interleave_last_block_size = dspm->interleave_last; - setup_vgmstream_dsp(vgmstream, ch_header); + { + /* set coefs and initial history (usually 0) */ + for (i = 0; i < vgmstream->channels; i++) { + for (j = 0; j < 16; j++) { + vgmstream->ch[i].adpcm_coef[j] = ch_header[i].coef[j]; + } + vgmstream->ch[i].adpcm_history1_16 = ch_header[i].initial_hist1; + vgmstream->ch[i].adpcm_history2_16 = ch_header[i].initial_hist2; + } + } /* don't know why, but it does happen*/ if (dspm->fix_looping && vgmstream->loop_end_sample > vgmstream->num_samples) @@ -291,7 +249,8 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { int i, channel_count; /* checks */ - /* .dsp: standard, .adp: Dr. Muto/Battalion Wars (GC) mono files */ + /* .dsp: standard + * .adp: Dr. Muto/Battalion Wars (GC) mono files */ if (!check_extensions(streamFile, "dsp,adp")) goto fail; @@ -348,6 +307,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->meta_type = meta_DSP_STD; + vgmstream->allow_dual_stereo = 1; /* very common in .dsp */ vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_none; @@ -518,9 +478,7 @@ fail: return NULL; } - -/* Some very simple stereo variants of standard dsp just use the standard header - * twice and add interleave, or just concatenate the channels. We'll support them all here. */ +/* ********************************* */ /* .stm - Intelligent Systems + others (same programmers) full interleaved dsp [Paper Mario TTYD (GC), Fire Emblem: POR (GC), Cubivore (GC)] */ VGMSTREAM * init_vgmstream_ngc_dsp_stm(STREAMFILE *streamFile) { @@ -612,7 +570,6 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile) { goto fail; } - return init_vgmstream_dsp_common(streamFile, &dspm); fail: return NULL; @@ -714,107 +671,6 @@ fail: return NULL; } -#define WSI_MAX_CHANNELS 2 -/* .wsi - blocked dsp [Alone in the Dark (Wii)] */ -VGMSTREAM * init_vgmstream_wsi(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset, header_offset; - size_t header_spacing; - struct dsp_header ch_header[WSI_MAX_CHANNELS]; - int channel_count; - - /* checks */ - if (!check_extensions(streamFile, "wsi")) - goto fail; - - /* I don't know if this is actually the channel count, or a block type - * for the first block. Won't know until I see a mono .wsi */ - channel_count = read_32bitBE(0x04,streamFile); - if (channel_count != 2) goto fail; - - /* check for consistent block headers */ - { - off_t block_offset; - off_t block_size_has_been; - int i; - - block_offset = read_32bitBE(0x00,streamFile); - if (block_offset < 0x08) goto fail; - - block_size_has_been = block_offset; - - /* check 4 blocks, to get an idea */ - for (i = 0; i < 4*channel_count; i++) { - off_t block_size = read_32bitBE(block_offset,streamFile); - - if (block_size < 0x10) - goto fail; /* expect at least the block header */ - if (i%channel_count+1 != read_32bitBE(block_offset+0x08,streamFile)) - goto fail; /* expect the channel numbers to alternate */ - - if (i%channel_count==0) - block_size_has_been = block_size; - else if (block_size != block_size_has_been) - goto fail; /* expect every block in a set of channels to have the same size */ - - block_offset += block_size; - } - } - - start_offset = read_32bitBE(0x00, streamFile); - header_offset = start_offset + 0x10; - header_spacing = read_32bitBE(start_offset,streamFile); - - /* read dsp */ - if (!dsp_load_header(ch_header, channel_count, streamFile,header_offset,header_spacing)) goto fail; - if (!check_dsp_format(ch_header, channel_count)) goto fail; - if (!check_dsp_samples(ch_header, channel_count)) goto fail; - //if (!check_dsp_initial_ps(ch_header, channel_count, streamFile,start_offset,interleave)) goto fail; - //if (!check_dsp_loop_ps(ch_header, channel_count, streamFile,start_offset,interleave)) goto fail; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,ch_header[0].loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = ch_header[0].sample_rate; - - vgmstream->num_samples = ch_header[0].sample_count / 14 * 14; /* remove incomplete last frame */ - vgmstream->loop_start_sample = dsp_nibbles_to_samples(ch_header[0].loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch_header[0].loop_end_offset)+1; - if (vgmstream->loop_end_sample > vgmstream->num_samples) /* don't know why, but it does happen*/ - vgmstream->loop_end_sample = vgmstream->num_samples; - - vgmstream->meta_type = meta_DSP_WSI; - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_blocked_wsi; - - setup_vgmstream_dsp(vgmstream, ch_header); - - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - - block_update_wsi(start_offset,vgmstream); - - /* first block has DSP header */ - { - int i; - - vgmstream->current_block_size -= 0x60; - for (i = 0; i < vgmstream->channels; i++) { - vgmstream->ch[i].offset += 0x60; - } - } - - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - - /* SWD - PSF chunks + interleaved dsps [Conflict: Desert Storm 1 & 2] */ VGMSTREAM * init_vgmstream_ngc_swd(STREAMFILE *streamFile) { dsp_meta dspm = {0}; @@ -899,6 +755,43 @@ fail: return NULL; } +/* IDSP - from Next Level games [Super Mario Strikers (GC), Mario Strikers: Charged (Wii)] */ +VGMSTREAM * init_vgmstream_idsp_nl(STREAMFILE *streamFile) { + dsp_meta dspm = {0}; + + /* checks */ + if (!check_extensions(streamFile, "idsp")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x49445350) /* "IDSP" */ + goto fail; + + dspm.channel_count = 2; + dspm.max_channels = 2; + + dspm.header_offset = 0x0c; + dspm.header_spacing = 0x60; + dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count; + dspm.interleave = read_32bitBE(0x04,streamFile); + /* 0x08: usable channel size */ + { + size_t stream_size = get_streamfile_size(streamFile); + if (read_32bitBE(stream_size - 0x04,streamFile) == 0x30303030) + stream_size -= 0x14; /* remove padding */ + stream_size -= dspm.start_offset; + + dspm.interleave_last = (stream_size / dspm.channel_count) % dspm.interleave; + } + + dspm.fix_looping = 1; + dspm.force_loop = 1; + dspm.force_loop_seconds = 15; + + dspm.meta_type = meta_IDSP_NL; + return init_vgmstream_dsp_common(streamFile, &dspm); +fail: + return NULL; +} + /* .wsd - Custom header + full interleaved dsp [Phantom Brave (Wii)] */ VGMSTREAM * init_vgmstream_wii_wsd(STREAMFILE *streamFile) { dsp_meta dspm = {0}; @@ -1041,7 +934,8 @@ fail: return NULL; } -/* Cabela's series (Magic Wand dev?) - header + interleaved dsp [Cabela's Big Game Hunt 2005 Adventures (GC), Cabela's Outdoor Adventures (GC)] */ +/* Cabela's series (Magic Wand dev?) - header + interleaved dsp + * [Cabela's Big Game Hunt 2005 Adventures (GC), Cabela's Outdoor Adventures (GC)] */ VGMSTREAM * init_vgmstream_dsp_cabelas(STREAMFILE *streamFile) { dsp_meta dspm = {0}; @@ -1163,106 +1057,6 @@ fail: return NULL; } -//todo might be only part of a full header? -/* CSMP - Retro Studios header + interleaved DSPs [Metroid Prime 3 (Wii), Donkey Kong Country Returns (Wii)] */ -VGMSTREAM * init_vgmstream_ngc_dsp_csmp(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - char filename[PATH_LIMIT]; - long current_offset; - int tries; - struct dsp_header header; - int chanel_count, i; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("csmp",filename_extension(filename))) goto fail; - - if (read_32bitBE(0x00, streamFile) != 0x43534D50) /* "CSMP" */ - goto fail; - if (read_32bitBE(0x04, streamFile) != 1) /* version? */ - goto fail; - - chanel_count = 1; - start_offset = 0x60; - - current_offset = 0x08; - tries = 0; - while (1) { - uint32_t chunk_id, chunk_size; - - if (tries > 4) - goto fail; - - chunk_id = read_32bitBE(current_offset + 0x00, streamFile); - chunk_size = read_32bitBE(current_offset + 0x04, streamFile); - current_offset += 0x08; - if (chunk_id != 0x44415441) { /* "DATA" */ - current_offset += chunk_size; - tries++; - continue; - } - - break; - } - - if (read_dsp_header(&header, current_offset, streamFile)) goto fail; - - - - /* check initial predictor/scale */ - /* Retro doesn't seem to abide by this */ - //if (header.initial_ps != (uint8_t)read_8bit(current_offset + start_offset,streamFile)) - // goto fail; - - /* check type==0 and gain==0 */ - if (header.format || header.gain) - goto fail; - - /* Retro doesn't seem to abide by this */ - /* check loop predictor/scale */ - if (header.loop_flag) { -// off_t loop_off = header.loop_start_offset/16*8; -// if (header.loop_ps != (uint8_t)read_8bit(current_offset + start_offset+loop_off,streamFile)) -// goto fail; - } - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(chanel_count,header.loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = header.sample_rate; - vgmstream->num_samples = header.sample_count; - vgmstream->loop_start_sample = dsp_nibbles_to_samples(header.loop_start_offset); - vgmstream->loop_end_sample = dsp_nibbles_to_samples(header.loop_end_offset)+1; - - /* don't know why, but it does happen*/ - if (vgmstream->loop_end_sample > vgmstream->num_samples) - vgmstream->loop_end_sample = vgmstream->num_samples; - - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_DSP_CSMP; - - /* coeffs */ - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i] = header.coef[i]; - vgmstream->ch[0].adpcm_history1_16 = header.initial_hist1; - vgmstream->ch[0].adpcm_history2_16 = header.initial_hist2; - - /* open the file for reading */ - 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=current_offset + start_offset; - - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - /* .mcadpcm - Custom header + full interleaved dsp [Skyrim (Switch)] */ VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile) { dsp_meta dspm = {0}; diff --git a/src/meta/ngc_dsp_ygo.c b/src/meta/ngc_dsp_ygo.c index 77608018..e53905ef 100644 --- a/src/meta/ngc_dsp_ygo.c +++ b/src/meta/ngc_dsp_ygo.c @@ -39,6 +39,7 @@ VGMSTREAM * init_vgmstream_dsp_ygo(STREAMFILE *streamFile) { vgmstream->num_samples = read_32bitBE(0x20,streamFile); vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_DSP_YGO; + vgmstream->allow_dual_stereo = 1; if (loop_flag) { vgmstream->loop_start_sample = (read_32bitBE(0x30,streamFile)*14/16); vgmstream->loop_end_sample = (read_32bitBE(0x34,streamFile)*14/16); diff --git a/src/meta/ngc_lps.c b/src/meta/ngc_lps.c index 6957a782..bafa5f90 100644 --- a/src/meta/ngc_lps.c +++ b/src/meta/ngc_lps.c @@ -37,6 +37,7 @@ VGMSTREAM * init_vgmstream_ngc_lps(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_NGC_LPS; + vgmstream->allow_dual_stereo = 1; if (vgmstream->coding_type == coding_NGC_DSP) { int i; diff --git a/src/meta/ngca.c b/src/meta/ngca.c index 271be64e..fb135204 100644 --- a/src/meta/ngca.c +++ b/src/meta/ngca.c @@ -34,6 +34,7 @@ VGMSTREAM * init_vgmstream_ngca(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_NGCA; + vgmstream->allow_dual_stereo = 1; if (vgmstream->coding_type == coding_NGC_DSP) { int i; diff --git a/src/meta/nub_vag.c b/src/meta/nub_vag.c index c74393e9..6d68746c 100644 --- a/src/meta/nub_vag.c +++ b/src/meta/nub_vag.c @@ -32,6 +32,7 @@ VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_NUB_VAG; + vgmstream->allow_dual_stereo = 1; start_offset = 0xC0; diff --git a/src/meta/opus_ppp.c b/src/meta/opus_ppp.c index 6c69b05b..87906b9f 100644 --- a/src/meta/opus_ppp.c +++ b/src/meta/opus_ppp.c @@ -84,9 +84,7 @@ VGMSTREAM * init_vgmstream_opus_ppp(STREAMFILE *streamFile) { vgmstream->meta_type = meta_OPUS_PPP; vgmstream->coding_type = data->segments[0]->coding_type; vgmstream->layout_type = layout_segmented; - vgmstream->layout_data = data; - data->loop_segment = loop_start_segment; return vgmstream; diff --git a/src/meta/ps2_adm.c b/src/meta/ps2_adm.c index b1c6d3db..b0c5b61c 100644 --- a/src/meta/ps2_adm.c +++ b/src/meta/ps2_adm.c @@ -32,8 +32,8 @@ VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = 44100; vgmstream->meta_type = meta_PS2_ADM; + vgmstream->sample_rate = 44100; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_blocked_adm; @@ -44,19 +44,18 @@ VGMSTREAM * init_vgmstream_ps2_adm(STREAMFILE *streamFile) { { vgmstream->next_block_offset = start_offset; do { - block_update_adm(vgmstream->next_block_offset,vgmstream); - + block_update(vgmstream->next_block_offset,vgmstream); if (loop_flag && vgmstream->current_block_offset == loop_start_offset) vgmstream->loop_start_sample = vgmstream->num_samples; vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset,vgmstream); if (loop_flag) vgmstream->loop_end_sample = vgmstream->num_samples; } - block_update_adm(start_offset,vgmstream); return vgmstream; fail: diff --git a/src/meta/ps2_ass.c b/src/meta/ps2_ass.c index 1253bc43..6fe656bb 100644 --- a/src/meta/ps2_ass.c +++ b/src/meta/ps2_ass.c @@ -1,92 +1,51 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* SVS (from Dai Senryaku VII - Exceed) */ +/* .ASS - from Dai Senryaku VII: Exceed (PS2) */ VGMSTREAM * init_vgmstream_ps2_ass(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - uint8_t testBuffer[0x10]; - off_t loopStart = 0; - off_t loopEnd = 0; - off_t readOffset = 0; - size_t fileLength; - int loop_flag; - int channel_count; + off_t start_offset; + size_t channel_size, interleave; + int loop_flag, channel_count, sample_rate; + int32_t num_samples, loop_start = 0, loop_end = 0; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("ass",filename_extension(filename))) goto fail; - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x02000000) /* "0x02000000" */ + /* checks */ + if (!check_extensions(streamFile, "ass")) goto fail; - loop_flag = 1; - channel_count = 2; - - /* build the VGMSTREAM */ + start_offset = 0x800; + channel_count = read_32bitLE(0x00,streamFile); /* assumed */ + if (channel_count != 2) goto fail; + sample_rate = read_32bitLE(0x04,streamFile); + channel_size = read_32bitLE(0x08,streamFile); + interleave = read_32bitLE(0x0c,streamFile); + num_samples = ps_bytes_to_samples(channel_size,1); + + loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start, &loop_end); + loop_flag = loop_flag && (num_samples > 10*sample_rate); /* disable looping for smaller files (in seconds) */ + + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x04,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (read_32bitLE(0x08,streamFile)*2)*28/16/channel_count; - - fileLength = get_streamfile_size(streamFile); - - do { - - readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - /* Loop Start */ - if(testBuffer[0x01]==0x06) { - if(loopStart == 0) loopStart = readOffset-0x10; - /* break; */ - } - /* Loop End */ - if(testBuffer[0x01]==0x03) { - if(loopEnd == 0) loopEnd = readOffset-0x10; - /* break; */ - } - } while (streamFile->get_offset(streamFile)<(int32_t)fileLength); - - if(loopStart == 0) { - loop_flag = 0; - vgmstream->num_samples = read_32bitLE(0x4,streamFile)*28/16/channel_count; - } else { - loop_flag = 1; - vgmstream->loop_start_sample = (loopStart-start_offset)*28/16/channel_count; - vgmstream->loop_end_sample = (loopEnd-start_offset)*28/16/channel_count; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x0C,streamFile); vgmstream->meta_type = meta_PS2_ASS; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/src/meta/ps2_iab.c b/src/meta/ps2_iab.c index 593cb4f8..edae3d81 100644 --- a/src/meta/ps2_iab.c +++ b/src/meta/ps2_iab.c @@ -40,13 +40,13 @@ VGMSTREAM * init_vgmstream_ps2_iab(STREAMFILE *streamFile) { { vgmstream->next_block_offset = start_offset; do { - block_update_ps2_iab(vgmstream->next_block_offset, vgmstream); + block_update(vgmstream->next_block_offset, vgmstream); vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset, vgmstream); } - block_update_ps2_iab(start_offset, vgmstream); return vgmstream; fail: diff --git a/src/meta/ps2_joe.c b/src/meta/ps2_joe.c index 02c51c95..4d7872d3 100644 --- a/src/meta/ps2_joe.c +++ b/src/meta/ps2_joe.c @@ -5,7 +5,8 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - int channel_count, loop_flag, sample_rate, num_samples; + int channel_count, loop_flag, sample_rate; + int32_t num_samples, loop_start = 0, loop_end = 0; size_t file_size, data_size, unknown1, unknown2, interleave; @@ -20,37 +21,41 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { /* detect version */ if (data_size/2 == file_size - 0x10 - && unknown1 == 0x0045039A && unknown2 == 0x00108920) { /* Super Farm */ + && unknown1 == 0x0045039A && unknown2 == 0x00108920) { /* Super Farm (PS2) */ data_size = data_size / 2; interleave = 0x4000; + start_offset = 0x10; } else if (data_size/2 == file_size - 0x10 - && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* Sitting Ducks */ + && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* Sitting Ducks (PS2) */ data_size = data_size / 2; interleave = 0x8000; + start_offset = 0x10; } else if (data_size == file_size - 0x10 - && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series */ + && unknown1 == 0xCCCCCCCC && unknown2 == 0xCCCCCCCC) { /* The Mummy: The Animated Series (PS2) */ interleave = 0x8000; + start_offset = 0x10; } - else if (data_size == file_size - 0x4020) { /* CT Special Forces (and all games beyond) */ + else if (data_size == file_size - 0x4020) { /* CT Special Forces (PS2), and all games beyond */ interleave = unknown1; /* always 0? */ if (!interleave) interleave = 0x10; - /* header padding contains garbage */ + start_offset = 0x4020; /* header padding contains garbage */ } else { goto fail; } - start_offset = file_size - data_size; - + //start_offset = file_size - data_size; /* also ok */ channel_count = 2; sample_rate = read_32bitLE(0x00,streamFile); num_samples = ps_bytes_to_samples(data_size, channel_count); + + loop_flag = ps_find_loop_offsets(streamFile, start_offset, data_size, channel_count, interleave,&loop_start, &loop_end); /* most songs simply repeat except a few jingles (PS-ADPCM flags are always set) */ - loop_flag = (num_samples > 20*sample_rate); /* in seconds */ + loop_flag = loop_flag && (num_samples > 20*sample_rate); /* in seconds */ /* build the VGMSTREAM */ @@ -59,52 +64,8 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) { vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; - - //todo improve, not working 100% with early .joe - { - uint8_t testBuffer[0x10]; - off_t blockOffset = 0; - off_t sampleOffset = 0; - off_t readOffset = 0; - off_t loopStart = 0, loopEnd = 0; - - readOffset = start_offset; - do { - off_t blockRead = (off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - - readOffset += blockRead; - blockOffset += blockRead; - - if (blockOffset >= interleave) { - readOffset += interleave; - blockOffset -= interleave; - } - - /* Loop Start */ - if(testBuffer[0x01]==0x06) { - if(loopStart == 0) - loopStart = sampleOffset; - /* break; */ - } - - sampleOffset += 28; - - /* Loop End */ - if(testBuffer[0x01]==0x03) { - if(loopEnd == 0) - loopEnd = sampleOffset; - /* break; */ - } - - } while (streamFile->get_offset(streamFile)<(int32_t)file_size); - - if (loopStart == 0 && loopEnd == 0) { - vgmstream->loop_flag = 0; - } else { - vgmstream->loop_start_sample = loopStart; - vgmstream->loop_end_sample = loopEnd; - } - } + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; diff --git a/src/meta/ps2_psh.c b/src/meta/ps2_psh.c index cb9d3a1b..e0b11df2 100644 --- a/src/meta/ps2_psh.c +++ b/src/meta/ps2_psh.c @@ -1,75 +1,72 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" +#include "ps2_psh_streamfile.h" -/* PSH (from Dawn of Mana - Seiken Densetsu 4, Kingdom Hearts Re:Chain of Memories) */ -/* probably Square Vag Stream */ + +/* PSH/VSV - from Square Enix games [Dawn of Mana: Seiken Densetsu 4 (PS2), Kingdom Hearts Re:Chain of Memories (PS2)] */ VGMSTREAM * init_vgmstream_ps2_psh(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; + STREAMFILE *temp_streamFile = NULL; off_t start_offset; - uint8_t testBuffer[0x10]; - off_t loopEnd = 0; - off_t readOffset = 0; - size_t fileLength; + int loop_flag, channel_count; + size_t loop_start, adjust, data_size, interleave; - int loop_flag; - int channel_count; - /* check extension, case insensitive */ - if (!check_extensions(streamFile, "psh,vsv")) // vsv seems to be official extension + /* checks */ + /* .psh: assumed? [Romancing SaGa: Minstrel's Song (PS2)] + * .vsv: official? [Kingdom Hearts HD I.5 + II.5 ReMIX (PS4)] */ + if (!check_extensions(streamFile, "psh,vsv")) + goto fail; + /* 0x00(2): 0x0000 (RS:MS) / 0x6440 (KH:RCoM) / varies (DoM) */ + if ((uint16_t)read_16bitBE(0x02,streamFile) != 0x6400) goto fail; - /* check header */ - if (read_16bitBE(0x02,streamFile) != 0x6400) - goto fail; - - loop_flag = (read_16bitLE(0x06,streamFile)!=0); channel_count = 2; - + start_offset = 0x00; /* correct, but needs some tricks to fix sound (see below) */ + interleave = 0x800; + + adjust = (uint16_t)read_16bitLE(0x04,streamFile) & 0x7FF; /* upper bits = ??? */ + data_size = (uint16_t)read_16bitLE(0x0c,streamFile) * interleave; + /* 0x0e: ? (may be 0x0001, or a low-ish value, not related to looping?) */ + loop_start = ((uint16_t)read_16bitLE(0x06,streamFile) & 0x7FFF) * interleave; /* uper bit == loop flag? */ + loop_flag = (loop_start != 0); /* (no known files loop from beginning to end) */ + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0; - vgmstream->channels = channel_count; - vgmstream->sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (uint16_t)read_16bitLE(0x0C,streamFile)*0x800*28/16/channel_count; - - // loop end is set by the loop marker which we need to find ... - // there's some extra data on unloop files, so we calculate - // the sample count with loop marker on this files - fileLength = get_streamfile_size(streamFile); - do { - readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - - // Loop End ... - if(testBuffer[0x01]==0x03) { - if(loopEnd==0) loopEnd = readOffset-0x10; - break; - } - } while (streamFile->get_offset(streamFile)<(int32_t)fileLength); - - if(loopEnd!=0) - vgmstream->num_samples = loopEnd*28/16/channel_count; - - if(loop_flag) { - vgmstream->loop_start_sample = - ((uint16_t)read_16bitLE(0x06,streamFile)-0x8000)*0x400*28/16; - vgmstream->loop_end_sample=vgmstream->num_samples; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x800; vgmstream->meta_type = meta_PS2_PSH; + vgmstream->sample_rate = (uint16_t)read_16bitLE(0x08,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count); - /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start,channel_count); + vgmstream->loop_end_sample = vgmstream->num_samples; + + /* loops are odd, but comparing the audio wave with the OSTs these values seem correct */ + if (adjust == 0) { /* Romancing SaGa (PS2) */ + vgmstream->loop_start_sample -= ps_bytes_to_samples(channel_count*interleave,channel_count); /* maybe *before* loop block? */ + vgmstream->loop_start_sample -= ps_bytes_to_samples(0x200*channel_count,channel_count); /* maybe default adjust? */ + } + else { /* all others */ + vgmstream->loop_end_sample -= ps_bytes_to_samples((0x800 - adjust)*channel_count,channel_count); /* at last block + adjust is a 0x03 flag */ + } + + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + + temp_streamFile = setup_ps2_psh_streamfile(streamFile, start_offset, data_size); + if (!temp_streamFile) goto fail; + + if (!vgmstream_open_stream(vgmstream, temp_streamFile, start_offset)) goto fail; + + close_streamfile(temp_streamFile); return vgmstream; - /* clean up anything we may have opened */ fail: + close_streamfile(temp_streamFile); close_vgmstream(vgmstream); return NULL; } diff --git a/src/meta/ps2_psh_streamfile.h b/src/meta/ps2_psh_streamfile.h new file mode 100644 index 00000000..105ce566 --- /dev/null +++ b/src/meta/ps2_psh_streamfile.h @@ -0,0 +1,48 @@ +#ifndef _PS2_PSH_STREAMFILE_H_ +#define _PS2_PSH_STREAMFILE_H_ +#include "../streamfile.h" + +typedef struct { + off_t null_offset; +} ps2_psh_io_data; + +static size_t ps2_psh_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, ps2_psh_io_data* data) { + size_t bytes_read; + int i; + + bytes_read = streamfile->read(streamfile, dest, offset, length); + + /* PSHs do start at 0x00, but first line is also the header; must null it to avoid clicks */ + if (offset < data->null_offset) { + for (i = 0; i < data->null_offset - offset; i++) { + dest[i] = 0; + } + } + + return bytes_read; +} + +static STREAMFILE* setup_ps2_psh_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t data_size) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + ps2_psh_io_data io_data = {0}; + size_t io_data_size = sizeof(ps2_psh_io_data); + + io_data.null_offset = 0x10; + + /* setup custom streamfile */ + 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, ps2_psh_io_read,NULL); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + +#endif /* _PS2_PSH_STREAMFILE_H_ */ diff --git a/src/meta/ps2_smpl.c b/src/meta/ps2_smpl.c index e44bd7ed..b4a03712 100644 --- a/src/meta/ps2_smpl.c +++ b/src/meta/ps2_smpl.c @@ -8,11 +8,11 @@ VGMSTREAM * init_vgmstream_ps2_smpl(STREAMFILE *streamFile) { int loop_flag, channel_count; size_t channel_size; - /* check extension (.v0: left channel, .v1: right channel, .smpl: header id) */ + /* checks*/ + /* .v0: left channel, .v1: right channel + * .smpl: header id */ if ( !check_extensions(streamFile,"v0,v1,smpl") ) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x534D504C) /* "SMPL" */ goto fail; @@ -31,13 +31,12 @@ VGMSTREAM * init_vgmstream_ps2_smpl(STREAMFILE *streamFile) { vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->meta_type = meta_PS2_SMPL; + vgmstream->allow_dual_stereo = 1; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_none; - /* always, but can be null or used as special string */ read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; return vgmstream; diff --git a/src/meta/ps2_strlr.c b/src/meta/ps2_strlr.c index 9e8a42c6..83770fc4 100644 --- a/src/meta/ps2_strlr.c +++ b/src/meta/ps2_strlr.c @@ -2,7 +2,7 @@ #include "../layout/layout.h" #include "../coding/coding.h" -/* STR - The Bouncer (PS2) */ +/* .vs/STRx - from The Bouncer (PS2) */ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; int channel_count, loop_flag; @@ -10,11 +10,11 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { /* checks */ - /* .vs: real extension (from .nam container) , .str: partial header id */ + /* .vs: real extension (from .nam container) + * .str: fake, partial header id */ if (!check_extensions(streamFile, "vs,str")) goto fail; - /* check header */ if (!(read_32bitBE(0x000,streamFile) == 0x5354524C && /* "STRL" */ read_32bitBE(0x800,streamFile) == 0x53545252) && /* "STRR" */ read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */ @@ -29,11 +29,10 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_PS2_STRLR; vgmstream->sample_rate = 44100; vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_blocked_ps2_strlr; - vgmstream->meta_type = meta_PS2_STRLR; if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; @@ -42,13 +41,13 @@ VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) { { vgmstream->next_block_offset = start_offset; do { - block_update_ps2_strlr(vgmstream->next_block_offset,vgmstream); + block_update(vgmstream->next_block_offset,vgmstream); vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1); } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset, vgmstream); } - block_update_ps2_strlr(start_offset, vgmstream); return vgmstream; fail: diff --git a/src/meta/ps2_vbk.c b/src/meta/ps2_vbk.c index 3bc44fe1..ea303ac8 100644 --- a/src/meta/ps2_vbk.c +++ b/src/meta/ps2_vbk.c @@ -1,111 +1,66 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -//#include -//#include -/* VBK (from Disney's Stitch - Experiment 626) */ - -VGMSTREAM * init_vgmstream_ps2_vbk(STREAMFILE *streamFile) -{ +/* VBK - from Disney's Stitch - Experiment 626 (PS2) */ +VGMSTREAM * init_vgmstream_ps2_vbk(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - uint8_t testBuffer[0x10]; - off_t loopStart = 0; - off_t loopEnd = 0; - off_t readOffset = 0; - size_t fileLength; - int loop_flag; - int channel_count; + off_t start_offset, header_offset, stream_offset; + size_t stream_size, interleave; + int loop_flag, channel_count, sample_rate; + int32_t num_samples, loop_start = 0, loop_end = 0; + int total_subsongs, target_subsong = streamFile->stream_index; - //_TCHAR szBuffer[100]; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("vbk",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x2E56424B) /* .VBK */ + /* checks */ + if (!check_extensions(streamFile, "vbk")) goto fail; + if (read_32bitBE(0x00,streamFile) != 0x2E56424B) /* ".VBK" */ + goto fail; + /* 0x04: version? always 0x02? */ + start_offset = read_32bitLE(0x0C, streamFile); + /* 0x10: file size */ - loop_flag = 1; - channel_count = read_32bitLE(0x28,streamFile) + 1; + total_subsongs = read_32bitLE(0x08,streamFile); + if (target_subsong == 0) target_subsong = 1; + if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - //_stprintf(szBuffer, _T("%x"), channel_count); - //MessageBox(NULL, szBuffer, _T("Foo"), MB_OK); + header_offset = 0x14 + (target_subsong-1)*0x18; - /* build the VGMSTREAM */ + stream_size = read_32bitLE(header_offset+0x00,streamFile); + /* 0x04: id? */ + stream_offset = read_32bitLE(header_offset+0x08,streamFile); + sample_rate = read_32bitLE(header_offset+0x0c,streamFile); + interleave = read_32bitLE(header_offset+0x10,streamFile); + channel_count = read_32bitLE(header_offset+0x14,streamFile) + 1; /* 4ch is common, 1ch sfx too */ + start_offset += stream_offset; + + num_samples = ps_bytes_to_samples(stream_size,channel_count); + loop_flag = ps_find_loop_offsets(streamFile, start_offset, stream_size, channel_count, interleave, &loop_start, &loop_end); + loop_flag = loop_flag && (num_samples > 10*sample_rate); /* disable looping for smaller files (in seconds) */ + + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - fileLength = get_streamfile_size(streamFile); - start_offset = read_32bitLE(0x0C, streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitLE(0x20,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = (fileLength - start_offset)*28/16/channel_count; - - // get loop start - do { - - readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - - if(testBuffer[0x01]==0x06) - { - loopStart = readOffset-0x10; - break; - } - - } while (streamFile->get_offset(streamFile)<(int32_t)fileLength); - - - // get loop end - readOffset = fileLength - 0x10; - - do { - readOffset-=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - - /* Loop End */ - if(testBuffer[0x01]==0x03) - { - loopEnd = readOffset-0x10; - break; - } - } while (readOffset > 0); - - loop_flag = 1; - vgmstream->loop_start_sample = (loopStart-start_offset)*28/16/channel_count; - vgmstream->loop_end_sample = (loopEnd-start_offset)*28/16/channel_count; - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitLE(0x24,streamFile); vgmstream->meta_type = meta_PS2_VBK; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = stream_size; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } - diff --git a/src/meta/ps2_vgs.c b/src/meta/ps2_vgs.c index ea19558e..9af59737 100644 --- a/src/meta/ps2_vgs.c +++ b/src/meta/ps2_vgs.c @@ -1,122 +1,51 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* VGS (Phantom of Inferno) - - This format is used in _many_ Princess Soft games. -*/ +/* VGS - from Princess Soft games [Gin no Eclipse (PS2), Metal Wolf REV (PS2)] */ VGMSTREAM * init_vgmstream_ps2_vgs(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - size_t fileLength; - off_t readOffset = 0; off_t start_offset; - off_t loop_start_offset = 0; - off_t loop_end_offset = 0; + size_t data_size, channel_size, interleave; + int loop_flag, channel_count; + int32_t loop_start = 0, loop_end = 0; - uint8_t testBuffer[0x10]; - int loop_flag = 0; - int channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("vgs",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x56475300) + /* check */ + if ( !check_extensions(streamFile,"vgs") ) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x56475300) /* "VGS\0" ('VAG stereo', presumably) */ goto fail; - // get file length - fileLength = get_streamfile_size(streamFile); - - // Find loop start - do { - readOffset += (off_t)read_streamfile(testBuffer, readOffset, 0x10, streamFile); - - // Loop Start ... - if(testBuffer[0x01] == 0x06) - { - loop_start_offset = readOffset - 0x10; - break; - } - - } while (streamFile->get_offset(streamFile)<((int32_t)fileLength)); - - // start at last line of file and move up - readOffset = (int32_t)fileLength - 0x10; - - // Find loop end - do { - readOffset -= (off_t)read_streamfile(testBuffer, readOffset, 0x10, streamFile); - - // Loop End ... - if((testBuffer[0x01]==0x03) && (testBuffer[0x03]!=0x77)) - { - loop_end_offset = readOffset + 0x20; - break; - } - } while (readOffset > 0); - - // setup loops - if (loop_start_offset > 0) - { - loop_flag = 1; - - // if we have a start loop, use EOF if end loop is not found - if (loop_end_offset == 0) - { - loop_end_offset = (int32_t)fileLength - 0x10; - } - } - else - { - loop_flag = 0; - } - + start_offset = 0x30; + data_size = get_streamfile_size(streamFile) - start_offset; + interleave = 0x20000; channel_count = 2; - + channel_size = read_32bitBE(0x0c,streamFile); + loop_flag = 0; /* all files have loop flags but simply fade out normally and repeat */ + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x30; - vgmstream->channels = channel_count; - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = ((get_streamfile_size(streamFile)-0x30)/16/channel_count*28); - - if (loop_flag) - { - vgmstream->loop_start_sample = loop_start_offset/16/channel_count*28; - vgmstream->loop_end_sample = loop_end_offset/16/channel_count*28; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0x04,streamFile)*0x1000; vgmstream->meta_type = meta_PS2_VGS; + vgmstream->sample_rate = read_32bitBE(0x10,streamFile); + vgmstream->num_samples = ps_bytes_to_samples(channel_size,1); + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + if (vgmstream->interleave_block_size) + vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels; + read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); /* always, can be null */ + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/src/meta/ps3_klbs.c b/src/meta/ps3_klbs.c deleted file mode 100644 index 272c5c53..00000000 --- a/src/meta/ps3_klbs.c +++ /dev/null @@ -1,122 +0,0 @@ -#include "meta.h" -#include "../util.h" - -/* .KLBS (L@VE ONCE PS3 */ -VGMSTREAM * init_vgmstream_ps3_klbs(STREAMFILE *streamFile) -{ - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - - size_t fileLength; - off_t readOffset = 0; - off_t start_offset; - off_t loop_start_offset = 0; - off_t loop_end_offset = 0; - - uint8_t testBuffer[0x10]; - int loop_flag = 0; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("bnk",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x20,streamFile) != 0x6B6C4253) - goto fail; - - // get file length - fileLength = get_streamfile_size(streamFile); - - // Find loop start - start_offset = read_32bitBE(0x10,streamFile); - readOffset = start_offset; - - do { - readOffset += (off_t)read_streamfile(testBuffer, readOffset, 0x10, streamFile); - - // Loop Start ... - if(testBuffer[0x01] == 0x06) - { - loop_start_offset = readOffset - 0x10; - break; - } - - } while (streamFile->get_offset(streamFile)<((int32_t)fileLength)); - - // start at last line of file and move up - readOffset = (int32_t)fileLength - 0x10; - - // Find loop end - do { - readOffset -= (off_t)read_streamfile(testBuffer, readOffset, 0x10, streamFile); - - // Loop End ... - if((testBuffer[0x01]==0x03) && (testBuffer[0x03]!=0x77)) - { - loop_end_offset = readOffset + 0x20; - break; - } - } while (readOffset > 0); - - // setup loops - if (loop_start_offset > 0) - { - loop_flag = 1; - - // if we have a start loop, use EOF if end loop is not found - if (loop_end_offset == 0) - { - loop_end_offset = (int32_t)fileLength - 0x10; - } - } - else - { - loop_flag = 0; - } - - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0x90, streamFile); - vgmstream->meta_type = meta_PS3_KLBS; - - vgmstream->channels = channel_count; - vgmstream->sample_rate = 48000; - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = ((vgmstream->interleave_block_size * channel_count)/16/channel_count*28); - - if (loop_flag) - { - vgmstream->loop_start_sample = loop_start_offset/16/channel_count*28; - vgmstream->loop_end_sample = loop_end_offset/16/channel_count*28; - } - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - - } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} diff --git a/src/meta/ps2_mib.c b/src/meta/ps_headerless.c similarity index 80% rename from src/meta/ps2_mib.c rename to src/meta/ps_headerless.c index b3de5989..8a4a7d97 100644 --- a/src/meta/ps2_mib.c +++ b/src/meta/ps_headerless.c @@ -4,13 +4,11 @@ static int check_psadpcm(STREAMFILE *streamFile); -/* MIB+MIH - SCEE MultiStream interleaved bank (header+data) [namCollection: Ace Combat 2 (PS2), Rampage: Total Destruction (PS2)] */ /* headerless PS-ADPCM - from Katamary Damacy (PS2), Air (PS2), Aladdin: Nasira's Revenge (PS1) * (guesses interleave and channels by testing data and using the file extension, and finds * loops in PS-ADPCM flags; this is a crutch for convenience, consider using GENH/TXTH instead). */ -VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { +VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileMIH = NULL; off_t start_offset = 0x00; char filename[PATH_LIMIT]; @@ -32,8 +30,6 @@ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { int forceNoLoop=0; int gotEmptyLine=0; - uint8_t gotMIH=0; - int i, channel_count=0; @@ -59,14 +55,6 @@ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { goto fail; - /* .MIB may come with a .MIH header file */ - if (strcasecmp("mib",filename_extension(filename))==0) { - streamFileMIH = open_streamfile_by_ext(streamFile,"mih"); - if (streamFileMIH) - gotMIH = 1; - } - - fileLength = get_streamfile_size(streamFile); /* Search for interleave value (checking channel starts) and loop points (using PS-ADPCM flags). @@ -137,7 +125,7 @@ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { } } - } while (streamFile->get_offset(streamFile)<((int32_t)fileLength)); + } while (readOffset<((int32_t)fileLength)); } if(testBuffer[0]==0x0c && testBuffer[1]==0) @@ -146,9 +134,6 @@ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { if(channel_count==0) channel_count=1; - if(gotMIH) - channel_count=read_32bitLE(0x08,streamFileMIH); - // force no loop if(!strcasecmp("vb",filename_extension(filename))) loopStart=0; @@ -229,34 +214,25 @@ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { vgmstream->coding_type = coding_PSX; vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; - if(gotMIH) { - // Read stuff from the MIH file - vgmstream->sample_rate = read_32bitLE(0x0C,streamFileMIH); - vgmstream->interleave_block_size = read_32bitLE(0x10,streamFileMIH); - vgmstream->num_samples=((read_32bitLE(0x10,streamFileMIH)* - (read_32bitLE(0x14,streamFileMIH)-1)*2)+ - ((read_32bitLE(0x04,streamFileMIH)>>8)*2))/16*28/2; - } else { - vgmstream->interleave_block_size = interleave; + vgmstream->interleave_block_size = interleave; - if(!strcasecmp("mib",filename_extension(filename))) - vgmstream->sample_rate = 44100; + if(!strcasecmp("mib",filename_extension(filename))) + vgmstream->sample_rate = 44100; - if(!strcasecmp("mi4",filename_extension(filename))) - vgmstream->sample_rate = 48000; + if(!strcasecmp("mi4",filename_extension(filename))) + vgmstream->sample_rate = 48000; - if(!strcasecmp("snds", filename_extension(filename))) - vgmstream->sample_rate = 48000; + if(!strcasecmp("snds", filename_extension(filename))) + vgmstream->sample_rate = 48000; - if(!strcasecmp("xag",filename_extension(filename))) - vgmstream->sample_rate = 44100; + if(!strcasecmp("xag",filename_extension(filename))) + vgmstream->sample_rate = 44100; - if (!strcasecmp("cvs", filename_extension(filename)) || - !strcasecmp("vb",filename_extension(filename))) - vgmstream->sample_rate = 22050; + if (!strcasecmp("cvs", filename_extension(filename)) || + !strcasecmp("vb",filename_extension(filename))) + vgmstream->sample_rate = 22050; - vgmstream->num_samples = (int32_t)(fileLength/16/channel_count*28); - } + vgmstream->num_samples = (int32_t)(fileLength/16/channel_count*28); if(loopEnd!=0) { if(vgmstream->channels==1) { @@ -299,17 +275,14 @@ VGMSTREAM * init_vgmstream_ps2_mib(STREAMFILE *streamFile) { vgmstream->loop_end_sample-=(emptySamples*channel_count); } - vgmstream->meta_type = gotMIH ? meta_PS2_MIB_MIH : meta_PS2_MIB; + vgmstream->meta_type = meta_PS_HEADERLESS; + vgmstream->allow_dual_stereo = 1; if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - - - close_streamfile(streamFileMIH); return vgmstream; fail: - close_streamfile(streamFileMIH); close_vgmstream(vgmstream); return NULL; } diff --git a/src/meta/psx_cdxa.c b/src/meta/psx_cdxa.c index fab4911a..612b5adf 100644 --- a/src/meta/psx_cdxa.c +++ b/src/meta/psx_cdxa.c @@ -158,18 +158,16 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) { /* calc num_samples as blocks may be empty or smaller than usual depending on flags */ vgmstream->next_block_offset = start_offset; do { - block_update_xa(vgmstream->next_block_offset,vgmstream); + block_update(vgmstream->next_block_offset,vgmstream); vgmstream->num_samples += vgmstream->current_block_samples; } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); - + block_update(start_offset,vgmstream); } else { vgmstream->num_samples = xa_bytes_to_samples(file_size - start_offset, channel_count, is_blocked); } - if (vgmstream->layout_type == layout_blocked_xa) - block_update_xa(start_offset,vgmstream); return vgmstream; fail: diff --git a/src/meta/rfrm.c b/src/meta/rfrm.c new file mode 100644 index 00000000..44327a09 --- /dev/null +++ b/src/meta/rfrm.c @@ -0,0 +1,60 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* RFTM - Retro Studios format [Donkey Kong Country Tropical Freeze (WiiU)] */ +VGMSTREAM * init_vgmstream_rfrm(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, header_offset; + size_t interleave; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "csmp")) + goto fail; + + /* format uses weird-sized chunks, quick hack until I get enough files */ + + if (read_32bitBE(0x00, streamFile) != 0x5246524D) /* "RFRM" */ + goto fail; + if (read_32bitBE(0x14, streamFile) != 0x43534D50) /* "CSMP" */ + goto fail; + if (read_32bitBE(0x3d, streamFile) != 0x44415441) /* "DATA" */ + goto fail; + + channel_count = read_32bitBE(0x35, streamFile); + + header_offset = 0x58; + loop_flag = read_16bitBE(header_offset+0x0c,streamFile); + + start_offset = header_offset + 0x60; + interleave = (get_streamfile_size(streamFile) - header_offset) / channel_count; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_RFRM; + vgmstream->sample_rate = read_32bitBE(header_offset+0x08,streamFile); + vgmstream->num_samples = read_32bitBE(header_offset+0x00,streamFile); + vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bitBE(header_offset+0x10,streamFile)); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(read_32bitBE(header_offset+0x14,streamFile))+1; + if (vgmstream->loop_end_sample > vgmstream->num_samples) /* ? */ + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + dsp_read_coefs_be(vgmstream, streamFile, header_offset+0x1c, interleave); + dsp_read_hist_be(vgmstream, streamFile, header_offset+0x40, interleave); + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/riff.c b/src/meta/riff.c index 56d00de6..e25b8aac 100644 --- a/src/meta/riff.c +++ b/src/meta/riff.c @@ -13,6 +13,7 @@ static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, si /* return milliseconds */ static long parse_adtl_marker(unsigned char * marker) { long hh,mm,ss,ms; + if (memcmp("Marker ",marker,7)) return -1; if (4 != sscanf((char*)marker+7,"%ld:%ld:%ld.%ld",&hh,&mm,&ss,&ms)) @@ -25,31 +26,31 @@ static long parse_adtl_marker(unsigned char * marker) { static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, long *loop_start, long *loop_end, int *loop_flag) { int loop_start_found = 0; int loop_end_found = 0; - off_t current_chunk = adtl_offset+4; + off_t current_chunk = adtl_offset+0x04; - while (current_chunk < adtl_offset+adtl_length) { - uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = read_32bitLE(current_chunk+4,streamFile); + while (current_chunk < adtl_offset + adtl_length) { + uint32_t chunk_type = read_32bitBE(current_chunk+0x00,streamFile); + off_t chunk_size = read_32bitLE(current_chunk+0x04,streamFile); - if (current_chunk+8+chunk_size > adtl_offset+adtl_length) return; + if (current_chunk+0x08+chunk_size > adtl_offset+adtl_length) + return; switch(chunk_type) { - case 0x6c61626c: { /* labl */ - unsigned char *labelcontent; - labelcontent = malloc(chunk_size-4); + case 0x6c61626c: { /* "labl" */ + unsigned char *labelcontent = malloc(chunk_size-0x04); if (!labelcontent) return; - if (read_streamfile(labelcontent,current_chunk+0xc, chunk_size-4,streamFile)!=chunk_size-4) { + if (read_streamfile(labelcontent,current_chunk+0x0c, chunk_size-0x04,streamFile) != chunk_size-0x04) { free(labelcontent); return; } switch (read_32bitLE(current_chunk+8,streamFile)) { case 1: - if (!loop_start_found && (*loop_start = parse_adtl_marker(labelcontent))>=0) + if (!loop_start_found && (*loop_start = parse_adtl_marker(labelcontent)) >= 0) loop_start_found = 1; break; case 2: - if (!loop_end_found && (*loop_end = parse_adtl_marker(labelcontent))>=0) + if (!loop_end_found && (*loop_end = parse_adtl_marker(labelcontent)) >= 0) loop_end_found = 1; break; default: @@ -254,19 +255,26 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) { /* check extension */ - /* .lwav: to avoid hijacking .wav, .xwav: fake for Xbox games (unneded anymore) */ - /* .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC), .snd: Layton Brothers (iOS/Android), + /* .lwav: to avoid hijacking .wav + * .xwav: fake for Xbox games (not needed anymore) + * .da: The Great Battle VI (PS1) + * .dax: Love Game's - Wai Wai Tennis (PS1) + * .cd: Exector (PS) + * .med: Psi Ops (PC) + * .snd: Layton Brothers (iOS/Android) * .adx: Remember11 (PC) sfx * .adp: Headhunter (DC) * .xss: Spider-Man The Movie (Xbox) * .xsew: Mega Man X Legacy Collections (PC) */ - if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd,adx,adp,xss,xsew") ) { + if ( check_extensions(streamFile, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew") ) { ; } else if ( check_extensions(streamFile, "mwv") ) { mwv = 1; } - /* .rws: Climax games (Silent Hill Origins PSP, Oblivion PSP), .aud: EA Replay */ + /* .at3: standard + * .rws: Climax games (Silent Hill Origins PSP, Oblivion PSP) + * .aud: EA Replay */ else if ( check_extensions(streamFile, "at3,rws,aud") ) { at3 = 1; } diff --git a/src/meta/rs03.c b/src/meta/rs03.c index 6244e2ea..127e783f 100644 --- a/src/meta/rs03.c +++ b/src/meta/rs03.c @@ -27,11 +27,11 @@ VGMSTREAM * init_vgmstream_rs03(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitBE(0xc,streamFile); - vgmstream->num_samples = read_32bitBE(8,streamFile); + vgmstream->sample_rate = read_32bitBE(0x0c,streamFile); + vgmstream->num_samples = read_32bitBE(0x08,streamFile); if (loop_flag) { - vgmstream->loop_start_sample = read_32bitBE(0x18,streamFile)/8*14; - vgmstream->loop_end_sample = read_32bitBE(0x1c,streamFile)/8*14; + vgmstream->loop_start_sample = dsp_bytes_to_samples(read_32bitBE(0x18,streamFile), 1); + vgmstream->loop_end_sample = dsp_bytes_to_samples(read_32bitBE(0x1c,streamFile), 1); } vgmstream->meta_type = meta_DSP_RS03; diff --git a/src/meta/rws.c b/src/meta/rws.c index eff42048..563a7058 100644 --- a/src/meta/rws.c +++ b/src/meta/rws.c @@ -242,10 +242,10 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(rws.channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_RWS; vgmstream->sample_rate = rws.sample_rate; vgmstream->num_streams = total_subsongs; vgmstream->stream_size = stream_size; - vgmstream->meta_type = meta_RWS; if (name_offset) read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile); @@ -300,9 +300,6 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) { if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - - block_update_rws(start_offset, vgmstream); - return vgmstream; fail: diff --git a/src/meta/rwsd.c b/src/meta/rwsd.c index 769f2bbf..7dd87f08 100644 --- a/src/meta/rwsd.c +++ b/src/meta/rwsd.c @@ -280,10 +280,13 @@ VGMSTREAM * init_vgmstream_rwsd(STREAMFILE *streamFile) { vgmstream->meta_type = meta_RWAR; else if (rwav) { - if (big_endian) + if (big_endian) { vgmstream->meta_type = meta_RWAV; - else + } + else { vgmstream->meta_type = meta_CWAV; + vgmstream->allow_dual_stereo = 1; /* LEGO 3DS games */ + } } else vgmstream->meta_type = meta_RWSD; diff --git a/src/meta/scd_pcm.c b/src/meta/scd_pcm.c index 1e374bed..d28c669a 100644 --- a/src/meta/scd_pcm.c +++ b/src/meta/scd_pcm.c @@ -1,63 +1,65 @@ #include "meta.h" -#include "../util.h" +#include "../coding/coding.h" -/* PCM (from Lunar: Eternal Blue (Sega CD) */ + +/* .PCM - from Lunar: Eternal Blue (Sega CD) */ VGMSTREAM * init_vgmstream_scd_pcm(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; + int loop_flag, channel_count; - int loop_flag; - int channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("pcm",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x0,streamFile) != 0x00020000) + /* checks */ + if (!check_extensions(streamFile, "pcm")) goto fail; - loop_flag = (read_32bitLE(0x02,streamFile)!=0); - channel_count = 1; + if (read_16bitBE(0x00,streamFile) == 0x0002) { + channel_count = 1; + } + else if (read_16bitBE(0x00,streamFile) == 0x0001) { + channel_count = 2; /* RP025.PCM, RP039.PCM */ + } + else { + goto fail; + } + + start_offset = 0x800; + + + /* extra validations since .pcm it's kinda generic */ + { + off_t i; + /* should be empty up to start (~0x0a/~0x10 sometimes has unknown values) */ + for (i = 0x20; i < start_offset; i++) { + if (read_8bit(i, streamFile) != 0) + goto fail; + } + } + + /* loops start 0 is possible, plus all? files loops + * (even sfx/voices loop, but those set loop start in silence near the end) */ + loop_flag = (read_32bitBE(0x06,streamFile) != 0); + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = 0x200; - vgmstream->channels = channel_count; - vgmstream->sample_rate = 32000; - vgmstream->coding_type = coding_PCM8_SB_int; - vgmstream->num_samples = read_32bitBE(0x06,streamFile)*2; - if(loop_flag) { - vgmstream->loop_start_sample = read_32bitBE(0x02,streamFile)*0x400*2; - vgmstream->loop_end_sample = read_32bitBE(0x06,streamFile)*2; - } - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x1; vgmstream->meta_type = meta_SCD_PCM; + vgmstream->sample_rate = 32500; /* looks correct compared to emu/recordings */ + vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count, 8); + vgmstream->loop_start_sample = read_32bitBE(0x02,streamFile)*0x400*2; + vgmstream->loop_end_sample = read_32bitBE(0x06,streamFile)*2; - /* open the file for reading */ - { - int i; - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - for (i=0;ich[i].streamfile = file; - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=start_offset+ - vgmstream->interleave_block_size*i; - } - } + vgmstream->coding_type = coding_PCM8_SB; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x800; + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; + close_vgmstream(vgmstream); + return NULL; } diff --git a/src/meta/sfl.c b/src/meta/sfl.c index c656142b..51c64092 100644 --- a/src/meta/sfl.c +++ b/src/meta/sfl.c @@ -1,12 +1,118 @@ -#include "../vgmstream.h" +#include "meta.h" + +static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, long *loop_start, long *loop_end, int *loop_flag); + + +/* .sfl - odd RIFF-formatted files that go along with .ogg [Hanachirasu (PC), Touhou 10.5 (PC)] */ +VGMSTREAM * init_vgmstream_sfl_ogg(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamData = NULL; + int loop_flag = 0; + long loop_start_ms = -1; + long loop_end_ms = -1; + + + /* checks */ + if (!check_extensions(streamFile, "sfl")) + goto fail; + if (read_32bitBE(0x00,streamFile) != 0x52494646) /* "RIFF" */ + goto fail; + if (read_32bitBE(0x08,streamFile) != 0x5346504C) /* "SFPL" */ + goto fail; + + { + /* try with file.ogg.sfl=header and file.ogg=data [Hanachirasu (PC)] */ + char basename[PATH_LIMIT]; + get_streamfile_basename(streamFile,basename,PATH_LIMIT); + streamData = open_streamfile_by_filename(streamFile, basename); + if (!streamData) { + /* try again with file.sfl=header + file.ogg=daba */ + streamData = open_streamfile_by_ext(streamFile,"ogg"); + if (!streamData) goto fail; + } + else { + if (!check_extensions(streamData, "ogg")) + goto fail; + } + } #ifdef VGM_USE_VORBIS + /* let the real initer do the parsing */ + vgmstream = init_vgmstream_ogg_vorbis(streamData); + if (!vgmstream) goto fail; + vgmstream->meta_type = meta_OGG_SFL; +#else + goto fail; +#endif -#include "meta.h" -#include "../layout/layout.h" -#include "../util.h" + /* read through chunks to verify format and find metadata */ + { + size_t riff_size, file_size; + off_t current_chunk = 0x0c; /* start with first chunk */ -/* .sfl, odd RIFF-formatted files that go along with oggs */ + riff_size = read_32bitLE(0x04,streamFile); + file_size = get_streamfile_size(streamFile); + if (file_size < riff_size+0x08) + goto fail; + + while (current_chunk < file_size) { + uint32_t chunk_type = read_32bitBE(current_chunk+0x00,streamFile); + off_t chunk_size = read_32bitLE(current_chunk+0x04,streamFile); + + /* There seem to be a few bytes left over, included in the + * RIFF but not enough to make a new chunk. */ + if (current_chunk+0x08 > file_size) break; + + if (current_chunk+0x08+chunk_size > file_size) + goto fail; + + switch(chunk_type) { + case 0x4C495354: /* "LIST" */ + switch (read_32bitBE(current_chunk+0x08, streamFile)) { + case 0x6164746C: /* "adtl" */ + /* yay, atdl is its own little world */ + parse_adtl(current_chunk + 0x08, chunk_size, streamFile, + &loop_start_ms,&loop_end_ms,&loop_flag); + break; + default: + break; + } + break; + default: + break; + } + current_chunk += 0x08+chunk_size; + } + } + + /* install loops */ + if (loop_flag) { + int loop_start = (long long)loop_start_ms * vgmstream->sample_rate / 1000; + int loop_end = (long long)loop_end_ms * vgmstream->sample_rate / 1000; + vgmstream_force_loop(vgmstream,loop_flag,loop_start, loop_end); + } + /* sfl .ogg often has song endings (use the base .ogg for those) */ + + close_streamfile(streamData); + return vgmstream; + +fail: + close_streamfile(streamData); + close_vgmstream(vgmstream); + return NULL; +} + +/* return milliseconds */ +static long parse_adtl_marker(unsigned char * marker) { + long hh,mm,ss,ms; + + if (memcmp("Marker ",marker,7)) return -1; + + if (4 != sscanf((char*)marker+7,"%ld:%ld:%ld.%ld",&hh,&mm,&ss,&ms)) + return -1; + + return ((hh*60+mm)*60+ss)*1000+ms; +} /* return milliseconds */ static int parse_region(unsigned char * region, long *start, long *end) { @@ -22,161 +128,67 @@ static int parse_region(unsigned char * region, long *start, long *end) { *start = ((start_hh*60+start_mm)*60+start_ss)*1000+start_ms; *end = ((end_hh*60+end_mm)*60+end_ss)*1000+end_ms; - return 0; } /* loop points have been found hiding here */ -static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, - long *loop_start, long *loop_end, int *loop_flag) { - int loop_found = 0; +static void parse_adtl(off_t adtl_offset, off_t adtl_length, STREAMFILE *streamFile, long *loop_start, long *loop_end, int *loop_flag) { + int loop_start_found = 0; + int loop_end_found = 0; + off_t current_chunk = adtl_offset+0x04; - off_t current_chunk = adtl_offset+4; + while (current_chunk < adtl_offset + adtl_length) { + uint32_t chunk_type = read_32bitBE(current_chunk+0x00,streamFile); + off_t chunk_size = read_32bitLE(current_chunk+0x04,streamFile); - while (current_chunk < adtl_offset+adtl_length) { - uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = read_32bitLE(current_chunk+4,streamFile); - - if (current_chunk+8+chunk_size > adtl_offset+adtl_length) return; + if (current_chunk+0x08+chunk_size > adtl_offset+adtl_length) + return; switch(chunk_type) { - case 0x6c61626c: /* labl */ - { - unsigned char *labelcontent; - labelcontent = malloc(chunk_size-4); - if (!labelcontent) return; - if (read_streamfile(labelcontent,current_chunk+0xc, - chunk_size-4,streamFile)!=chunk_size-4) { - free(labelcontent); - return; - } - - if (!loop_found && - parse_region(labelcontent,loop_start,loop_end)>=0) - { - loop_found = 1; - } - + case 0x6c61626c: { /* "labl" */ + unsigned char *labelcontent = malloc(chunk_size-0x04); + if (!labelcontent) return; + if (read_streamfile(labelcontent,current_chunk+0x0c, chunk_size-0x04,streamFile) != chunk_size-0x04) { free(labelcontent); + return; } + + switch (read_32bitLE(current_chunk+8,streamFile)) { + case 1: + if (!loop_start_found && (*loop_start = parse_adtl_marker(labelcontent)) >= 0) + loop_start_found = 1; + + if (!loop_start_found && !loop_end_found && parse_region(labelcontent,loop_start,loop_end) >= 0) { + loop_start_found = 1; + loop_end_found = 1; + } + + break; + case 2: + if (!loop_end_found && (*loop_end = parse_adtl_marker(labelcontent)) >= 0) + loop_end_found = 1; + break; + default: + break; + } + + free(labelcontent); break; + } default: break; } - current_chunk += 8 + chunk_size; + current_chunk += 0x08 + chunk_size; } - if (loop_found) *loop_flag = 1; + if (loop_start_found && loop_end_found) + *loop_flag = 1; + + /* labels don't seem to be consistently ordered */ + if (*loop_start > *loop_end) { + long temp = *loop_start; + *loop_start = *loop_end; + *loop_end = temp; + } } - -VGMSTREAM * init_vgmstream_sfl(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileOGG = NULL; - char filenameOGG[PATH_LIMIT]; - char filename[PATH_LIMIT]; - - off_t file_size = -1; - - int loop_flag = 0; - long loop_start_ms = -1; - long loop_end_ms = -1; - uint32_t riff_size; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sfl",filename_extension(filename))) goto fail; - - /* check header */ - if ((uint32_t)read_32bitBE(0,streamFile)!=0x52494646) /* "RIFF" */ - goto fail; - /* check for SFPL form */ - if ((uint32_t)read_32bitBE(8,streamFile)!=0x5346504C) /* "SFPL" */ - goto fail; - - /* check for .OGG file */ - strcpy(filenameOGG,filename); - strcpy(filenameOGG+strlen(filenameOGG)-3,"ogg"); - - streamFileOGG = streamFile->open(streamFile,filenameOGG,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileOGG) { - goto fail; - } - - /* let the real initer do the parsing */ - vgmstream = init_vgmstream_ogg_vorbis(streamFileOGG); - if (!vgmstream) goto fail; - - close_streamfile(streamFileOGG); - streamFileOGG = NULL; - - /* now that we have an ogg, proceed with parsing the .sfl */ - riff_size = read_32bitLE(4,streamFile); - file_size = get_streamfile_size(streamFile); - - /* check for tructated RIFF */ - if (file_size < riff_size+8) goto fail; - - /* read through chunks to verify format and find metadata */ - { - off_t current_chunk = 0xc; /* start with first chunk */ - - while (current_chunk < file_size) { - uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = read_32bitLE(current_chunk+4,streamFile); - - /* There seem to be a few bytes left over, included in the - * RIFF but not enough to make a new chunk. - */ - if (current_chunk+8 > file_size) break; - - if (current_chunk+8+chunk_size > file_size) goto fail; - - switch(chunk_type) { - case 0x4C495354: /* LIST */ - /* what lurks within?? */ - switch (read_32bitBE(current_chunk + 8, streamFile)) { - case 0x6164746C: /* adtl */ - /* yay, atdl is its own little world */ - parse_adtl(current_chunk + 8, chunk_size, - streamFile, - &loop_start_ms,&loop_end_ms,&loop_flag); - break; - default: - break; - } - break; - default: - /* ignorance is bliss */ - break; - } - - current_chunk += 8+chunk_size; - } - } - - if (loop_flag) { - /* install loops */ - if (!vgmstream->loop_flag) { - vgmstream->loop_flag = 1; - vgmstream->loop_ch = calloc(vgmstream->channels, - sizeof(VGMSTREAMCHANNEL)); - if (!vgmstream->loop_ch) goto fail; - } - - vgmstream->loop_start_sample = (long long)loop_start_ms*vgmstream->sample_rate/1000; - vgmstream->loop_end_sample = (long long)loop_end_ms*vgmstream->sample_rate/1000; - } - - vgmstream->meta_type = meta_OGG_SFL; - - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (streamFileOGG) close_streamfile(streamFileOGG); - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - -#endif diff --git a/src/meta/sli.c b/src/meta/sli.c index a2852667..f4ed5024 100644 --- a/src/meta/sli.c +++ b/src/meta/sli.c @@ -1,121 +1,99 @@ -#include "../vgmstream.h" +#include "meta.h" +#include + + +/* .sli - loop points associated with a similarly named .ogg [Fate/Stay Night (PC), World End Economica (PC)]*/ +VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * streamData = NULL; + int32_t loop_start = -1, loop_length = -1; + int32_t loop_from = -1, loop_to = -1; + + /* checks */ + if (!check_extensions(streamFile, "sli")) + goto fail; + + { + /* try with file.ogg.sli=header and file.ogg=data */ + char basename[PATH_LIMIT]; + get_streamfile_basename(streamFile,basename,PATH_LIMIT); + streamData = open_streamfile_by_filename(streamFile, basename); + if (!streamData) goto fail; + + if (!check_extensions(streamData, "ogg")) + goto fail; + } #ifdef VGM_USE_VORBIS - -#include -#include "meta.h" -#include "../util.h" - -#ifdef WIN32 -#define DIRSEP '\\' + /* let the real initer do the parsing */ + vgmstream = init_vgmstream_ogg_vorbis(streamData); + if (!vgmstream) goto fail; #else -#define DIRSEP '/' + goto fail; #endif -/* .sli is a file with loop points, associated with a similarly named .ogg */ + /* find loop text */ + { + char linebuffer[PATH_LIMIT]; + size_t bytes_read; + off_t sli_offset; + int done; -VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE *streamFile) { - - VGMSTREAM * vgmstream = NULL; - STREAMFILE * streamFileOGG = NULL; - char filename[PATH_LIMIT]; - char filenameOGG[PATH_LIMIT]; - char linebuffer[PATH_LIMIT]; - off_t bytes_read; - off_t sli_offset; - int done; - int32_t loop_start = -1; - int32_t loop_length = -1; - int32_t loop_from = -1; - int32_t loop_to = -1; + sli_offset = 0; + while ((loop_start == -1 || loop_length == -1) && sli_offset < get_streamfile_size(streamFile)) { + char *endptr, *foundptr; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("sli",filename_extension(filename))) goto fail; + bytes_read = get_streamfile_text_line(sizeof(linebuffer),linebuffer,sli_offset,streamFile,&done); + if (!done) goto fail; - /* check for .OGG file */ - strcpy(filenameOGG,filename); - /* strip off .sli */ - filenameOGG[strlen(filenameOGG)-4]='\0'; + if (memcmp("LoopStart=",linebuffer,10)==0 && linebuffer[10] != '\0') { + loop_start = strtol(linebuffer+10,&endptr,10); + if (*endptr != '\0') { + loop_start = -1; /* if it didn't parse cleanly */ + } + } + else if (memcmp("LoopLength=",linebuffer,11)==0 && linebuffer[11] != '\0') { + loop_length = strtol(linebuffer+11,&endptr,10); + if (*endptr != '\0') { + loop_length = -1; /* if it didn't parse cleanly */ + } + } - streamFileOGG = streamFile->open(streamFile,filenameOGG,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!streamFileOGG) { - goto fail; + /* a completely different format (2.0?), also with .sli extension and can be handled similarly */ + if ((foundptr = strstr(linebuffer,"To=")) != NULL && isdigit(foundptr[3])) { + loop_to = strtol(foundptr+3,&endptr,10); + if (*endptr != ';') { + loop_to = -1; + } + } + if ((foundptr = strstr(linebuffer,"From=")) != NULL && isdigit(foundptr[5])) { + loop_from = strtol(foundptr+5,&endptr,10); + if (*endptr != ';') { + loop_from = -1; + } + } + + sli_offset += bytes_read; + } } - /* let the real initer do the parsing */ - vgmstream = init_vgmstream_ogg_vorbis(streamFileOGG); - if (!vgmstream) goto fail; - - close_streamfile(streamFileOGG); - streamFileOGG = NULL; - - sli_offset = 0; - while ((loop_start == -1 || loop_length == -1) && sli_offset < get_streamfile_size(streamFile)) { - char *endptr; - char *foundptr; - bytes_read=get_streamfile_text_line(sizeof(linebuffer),linebuffer,sli_offset,streamFile,&done); - if (!done) goto fail; - - if (!memcmp("LoopStart=",linebuffer,10) && linebuffer[10]!='\0') { - loop_start = strtol(linebuffer+10,&endptr,10); - if (*endptr != '\0') { - /* if it didn't parse cleanly */ - loop_start = -1; - } - } - else if (!memcmp("LoopLength=",linebuffer,11) && linebuffer[11]!='\0') { - loop_length = strtol(linebuffer+11,&endptr,10); - if (*endptr != '\0') { - /* if it didn't parse cleanly */ - loop_length = -1; - } - } - - /* a completely different format, also with .sli extension and can be handled similarly */ - if ((foundptr=strstr(linebuffer,"To="))!=NULL && isdigit(foundptr[3])) { - loop_to = strtol(foundptr+3,&endptr,10); - if (*endptr != ';') { - loop_to = -1; - } - } - if ((foundptr=strstr(linebuffer,"From="))!=NULL && isdigit(foundptr[5])) { - loop_from = strtol(foundptr+5,&endptr,10); - if (*endptr != ';') { - loop_from = -1; - } - } - - sli_offset += bytes_read; + if (loop_start != -1 && loop_length != -1) { + vgmstream_force_loop(vgmstream,1,loop_start, loop_start+loop_length); + vgmstream->meta_type = meta_OGG_SLI; + } + else if (loop_from != -1 && loop_to != -1) { + vgmstream_force_loop(vgmstream,1,loop_to, loop_from); + vgmstream->meta_type = meta_OGG_SLI2; + } + else { + goto fail; /* if there's no loop points the .sli wasn't valid */ } - if ((loop_start != -1 && loop_length != -1) || - (loop_to != -1 && loop_from != -1)) { - /* install loops */ - if (!vgmstream->loop_flag) { - vgmstream->loop_flag = 1; - vgmstream->loop_ch = calloc(vgmstream->channels, - sizeof(VGMSTREAMCHANNEL)); - if (!vgmstream->loop_ch) goto fail; - } - - if (loop_to != -1 && loop_from != -1) { - vgmstream->loop_start_sample = loop_to; - vgmstream->loop_end_sample = loop_from; - vgmstream->meta_type = meta_OGG_SLI2; - } else { - vgmstream->loop_start_sample = loop_start; - vgmstream->loop_end_sample = loop_start+loop_length; - vgmstream->meta_type = meta_OGG_SLI; - } - } else goto fail; /* if there's no loop points the .sli wasn't valid */ - + close_streamfile(streamData); return vgmstream; - /* clean up anything we may have opened */ fail: - if (streamFileOGG) close_streamfile(streamFileOGG); - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(streamData); + close_vgmstream(vgmstream); return NULL; } -#endif diff --git a/src/meta/sps_n1.c b/src/meta/sps_n1.c index 6eca1300..ba0280fc 100644 --- a/src/meta/sps_n1.c +++ b/src/meta/sps_n1.c @@ -29,7 +29,7 @@ VGMSTREAM * init_vgmstream_sps_n1(STREAMFILE *streamFile) { temp_streamFile = setup_sps_streamfile(streamFile, subfile_offset, subfile_size, "vag"); if (!temp_streamFile) goto fail; - vgmstream = init_vgmstream_ps2_vag(temp_streamFile); + vgmstream = init_vgmstream_vag(temp_streamFile); if (!vgmstream) goto fail; break; diff --git a/src/meta/spt_spd.c b/src/meta/spt_spd.c index 749ef0f8..5535a74b 100644 --- a/src/meta/spt_spd.c +++ b/src/meta/spt_spd.c @@ -71,7 +71,8 @@ VGMSTREAM * init_vgmstream_spt_spd(STREAMFILE *streamFile) { } vgmstream->meta_type = meta_SPT_SPD; - + vgmstream->allow_dual_stereo = 1; + /* open the file for reading */ { for (i=0;iloop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); - //todo find if this actually helps - vgmstream->num_samples -= vgmstream->num_samples%576; - vgmstream->loop_start_sample -= vgmstream->loop_start_sample%576; - vgmstream->loop_end_sample -= vgmstream->loop_end_sample%576; - + /* somehow loops offsets aren't always frame-aligned, and the code below supposedly helped, + * but there isn't much difference since MPEG loops are rough (1152-aligned). Seems it + * would help more loop_start - ~1000, loop_end + ~1000 (ex. FFXIII-2 music_SunMizu.ps3.scd) */ + //vgmstream->num_samples -= vgmstream->num_samples % 576; + //vgmstream->loop_start_sample -= vgmstream->loop_start_sample % 576; + //vgmstream->loop_end_sample -= vgmstream->loop_end_sample % 576; break; } #endif diff --git a/src/meta/sthd.c b/src/meta/sthd.c index 9c83a013..5c025df6 100644 --- a/src/meta/sthd.c +++ b/src/meta/sthd.c @@ -28,8 +28,9 @@ VGMSTREAM * init_vgmstream_sthd(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count, loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitLE(0x20, streamFile); /* repeated ~8 times? */ vgmstream->meta_type = meta_STHD; + vgmstream->sample_rate = read_32bitLE(0x20, streamFile); /* repeated ~8 times? */ + vgmstream->coding_type = coding_XBOX_IMA_int; vgmstream->layout_type = layout_blocked_sthd; @@ -45,8 +46,7 @@ VGMSTREAM * init_vgmstream_sthd(STREAMFILE *streamFile) { vgmstream->next_block_offset = start_offset; do { - block_update_sthd(vgmstream->next_block_offset,vgmstream); - + block_update(vgmstream->next_block_offset,vgmstream); if (block_count == loop_start_block) vgmstream->loop_start_sample = vgmstream->num_samples; if (block_count == loop_end_block) @@ -56,10 +56,9 @@ VGMSTREAM * init_vgmstream_sthd(STREAMFILE *streamFile) { block_count++; } while (vgmstream->next_block_offset < get_streamfile_size(streamFile)); + block_update(start_offset, vgmstream); } - block_update_sthd(start_offset, vgmstream); - return vgmstream; fail: diff --git a/src/meta/txth.c b/src/meta/txth.c index f3e2252b..b2ca9fac 100644 --- a/src/meta/txth.c +++ b/src/meta/txth.c @@ -57,6 +57,7 @@ typedef struct { uint32_t loop_flag; int loop_flag_set; + int loop_flag_auto; uint32_t coef_offset; uint32_t coef_spacing; @@ -132,6 +133,13 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { goto fail; } + /* try to autodetect PS-ADPCM loop data */ + if (txth.loop_flag_auto && coding == coding_PSX) { + size_t data_size = get_streamfile_size(streamFile) - txth.start_offset; + txth.loop_flag = ps_find_loop_offsets(streamFile, txth.start_offset, data_size, txth.channels, txth.interleave, + (int32_t*)&txth.loop_start_sample, (int32_t*)&txth.loop_end_sample); + } + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(txth.channels,txth.loop_flag); @@ -376,6 +384,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) { vgmstream->coding_type = coding; vgmstream->meta_type = meta_TXTH; + vgmstream->allow_dual_stereo = 1; if ( !vgmstream_open_stream(vgmstream,streamFile,txth.start_offset) ) @@ -430,7 +439,7 @@ static int parse_txth(STREAMFILE * streamFile, STREAMFILE * streamText, txth_hea txth->data_size = get_streamfile_size(streamFile); /* for later use */ /* skip BOM if needed */ - if (read_16bitLE(0x00, streamText) == 0xFFFE || read_16bitLE(0x00, streamText) == 0xFEFF) + if ((uint16_t)read_16bitLE(0x00, streamText) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamText) == 0xFEFF) txt_offset = 0x02; /* read lines */ @@ -583,8 +592,13 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h txth->loop_adjust = get_bytes_to_samples(txth, txth->loop_adjust * (txth->interleave*txth->channels)); } else if (0==strcmp(key,"loop_flag")) { - if (!parse_num(streamFile,val, &txth->loop_flag)) goto fail; - txth->loop_flag_set = 1; + if (0==strcmp(val,"auto")) { + txth->loop_flag_auto = 1; + } + else { + if (!parse_num(streamFile,val, &txth->loop_flag)) goto fail; + txth->loop_flag_set = 1; + } } else if (0==strcmp(key,"coef_offset")) { if (!parse_num(streamFile,val, &txth->coef_offset)) goto fail; @@ -602,6 +616,9 @@ static int parse_keyval(STREAMFILE * streamFile, STREAMFILE * streamText, txth_h else if (0==strcmp(key,"coef_mode")) { if (!parse_num(streamFile,val, &txth->coef_mode)) goto fail; } + else if (0==strcmp(key,"psx_loops")) { + if (!parse_num(streamFile,val, &txth->coef_mode)) goto fail; + } else { VGM_LOG("TXTH: unknown key=%s, val=%s\n", key,val); goto fail; diff --git a/src/meta/txtp.c b/src/meta/txtp.c index 995bedf6..2c5474bd 100644 --- a/src/meta/txtp.c +++ b/src/meta/txtp.c @@ -180,10 +180,7 @@ VGMSTREAM * init_vgmstream_txtp(STREAMFILE *streamFile) { vgmstream->meta_type = meta_TXTP; vgmstream->coding_type = data_s->segments[0]->coding_type; vgmstream->layout_type = layout_segmented; - vgmstream->layout_data = data_s; - if (loop_flag) - data_s->loop_segment = txtp->loop_start_segment-1; } @@ -422,7 +419,7 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) { /* skip BOM if needed */ - if (read_16bitLE(0x00, streamFile) == 0xFFFE || read_16bitLE(0x00, streamFile) == 0xFEFF) + if ((uint16_t)read_16bitLE(0x00, streamFile) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamFile) == 0xFEFF) txt_offset = 0x02; /* read lines */ diff --git a/src/meta/ubi_bao.c b/src/meta/ubi_bao.c index e81635c2..95d8e43d 100644 --- a/src/meta/ubi_bao.c +++ b/src/meta/ubi_bao.c @@ -420,7 +420,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) /* 0x18: null */ /* 0x1c: null */ descriptor_type = read_32bit(offset+0x20, streamFile); - descriptor_subtype = read_32bit(offset+header_size+0x04, streamFile); /* games may crash if changed */ + descriptor_subtype = read_32bit(offset+header_size+0x04, streamFile); /* for debugging purposes */ switch(descriptor_type) { @@ -457,7 +457,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) //;VGM_ASSERT(descriptor_subtype != 0x01, "UBI BAO: subtype %x at %lx (%lx)\n", descriptor_subtype, offset, offset+header_size+0x04); /* ignore unknown subtypes */ - if (descriptor_subtype != 0x01) + if (descriptor_subtype != 0x00000001) return 1; bao->total_subsongs++; @@ -543,7 +543,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) break; - case 0x00230008: /* Splinter Cell: Conviction (X360/PC) */ + case 0x00230008: /* Splinter Cell: Conviction (X360/PC)-pk */ bao->stream_size = read_32bit(offset+header_size+0x08, streamFile); bao->stream_id = read_32bit(offset+header_size+0x24, streamFile); bao->is_external = read_32bit(offset+header_size+0x38, streamFile); @@ -612,7 +612,7 @@ static int parse_bao(ubi_bao_header * bao, STREAMFILE *streamFile, off_t offset) case 0x001F0010: /* Prince of Persia 2008 (PS3/X360)-file, Far Cry 2 (PS3)-file */ case 0x00280306: /* Far Cry 3: Blood Dragon (X360)-file */ case 0x00290106: /* Splinter Cell Blacklist? */ - default: + default: /* others possibly using BAO: Avatar X360/PS3/PC, Just Dance, Watch_Dogs, Far Cry Primal, Far Cry 4 */ VGM_LOG("UBI BAO: unknown BAO version at %lx\n", offset); goto fail; } diff --git a/src/meta/vag.c b/src/meta/vag.c index 9d5303c8..e88a78a5 100644 --- a/src/meta/vag.c +++ b/src/meta/vag.c @@ -1,24 +1,25 @@ #include "meta.h" +#include "../coding/coding.h" -static int vag_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, off_t * loop_start, off_t * loop_end); - -/* VAGp - SDK format, created by various Sony's tools (like AIFF2VAG) */ -VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { +/* VAGp - Sony SDK format, created by various tools */ +VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, loopStart = 0, loopEnd = 0; + off_t start_offset; + size_t file_size, channel_size, interleave; + meta_t meta_type; + int channel_count = 0, loop_flag, sample_rate; + uint32_t vag_id, version; + int32_t loop_start_sample = 0, loop_end_sample = 0; + int allow_dual_stereo = 0; - uint8_t vagID; - uint32_t version = 0; - - size_t filesize = 0, datasize = 0, interleave; - - int loop_flag = 0, loop_samples_found = 0; - int channel_count = 0; - int is_swag = 0; /* checks */ - /* .swag: Frantix (PSP), .str: Ben10 Galactic Racing, .vig: MX vs. ATV Untamed (PS2) .l/r: Crash Nitro Kart (PS2) */ + /* .vag: standard + * .swag: Frantix (PSP) + * .str: Ben10 Galactic Racing + * .vig: MX vs. ATV Untamed (PS2) + * .l/r: Crash Nitro Kart (PS2), Gradius V (PS2) */ if ( !check_extensions(streamFile,"vag,swag,str,vig,l,r") ) goto fail; @@ -27,302 +28,206 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { ((read_32bitLE(0x00,streamFile) & 0xFFFFFF00) != 0x56414700)) goto fail; - /* Frantix VAGp .swag: some (not all) fields in LE + 2 VAGp in the same file (full interleave) */ - is_swag = check_extensions(streamFile,"swag"); + file_size = get_streamfile_size(streamFile); - filesize = get_streamfile_size(streamFile); - - /* version used to create the file - * ex: 00000000 = v1.8 PC, 00000002 = v1.3 Mac, 00000003 = v1.6+ Mac, 00000020 = v2.0+ PC */ - version = read_32bitBE(0x04,streamFile); + /* version used to create the file: + * - 00000000 = v1.8 PC, + * - 00000002 = v1.3 Mac (used?) + * - 00000003 = v1.6+ Mac + * - 00000020 = v2.0 PC (most common) + * - 00000004 = ? (later games) + * - 00000006 = ? (vagconv) + * - 00020001 = v2.1 (vagconv2) + * - 00030000 = v3.0 (vagconv2) */ + version = (uint32_t)read_32bitBE(0x04,streamFile); /* 0x08-0c: reserved */ - if (is_swag) - datasize = read_32bitLE(0x0c,streamFile); - else - datasize = read_32bitBE(0x0c,streamFile); + channel_size = read_32bitBE(0x0c,streamFile); + sample_rate = read_32bitBE(0x10,streamFile); /* 0x14-20 reserved */ /* 0x20-30: name (optional) */ /* 0x30: data start (first 0x10 usually 0s to init SPU) */ - /* Check for correct channel count and loop flag */ - vagID=read_8bit(0x03,streamFile); - switch(vagID) { - case '1': /* "VAG1" (1 channel) [Metal Gear Solid 3] */ - channel_count=1; - break; - case '2': /* "VAG2" (2 channels) [Metal Gear Solid 3] */ - channel_count=2; - break; - case 'i': /* "VAGi" (interleaved) */ - channel_count=2; - break; - case 'V': /* pGAV (little endian / stereo) [Jak 3, Jak X] */ - if (read_32bitBE(0x20,streamFile)==0x53746572) /* "Ster" */ - channel_count=2; - else - channel_count=1; - break; - case 'p': /* "VAGp" (extended) [most common, ex Ratchet & Clank] */ - if (check_extensions(streamFile,"vig")) { /* MX vs. ATV Untamed PS2 */ - channel_count = 2; /* normal interleave */ + /* check variation */ + vag_id = read_32bitBE(0x00,streamFile); + switch(vag_id) { + + case 0x56414731: /* "VAG1" (1 channel) [Metal Gear Solid 3 (PS2)] */ + meta_type = meta_PS2_VAG1; + start_offset = 0x40; /* 0x30 is extra data in VAG1 */ + channel_count = 1; + interleave = 0; + loop_flag = 0; + break; + + case 0x56414732: /* "VAG2" (2 channels) [Metal Gear Solid 3 (PS2)] */ + meta_type = meta_PS2_VAG2; + start_offset = 0x40; /* 0x30 is extra data in VAG2 */ + channel_count = 2; + interleave = 0x800; + loop_flag = 0; + break; + + case 0x56414769: /* "VAGi" (interleaved) */ + meta_type = meta_PS2_VAGi; + start_offset = 0x800; + channel_count = 2; + interleave = read_32bitLE(0x08,streamFile); + loop_flag = 0; + break; + + case 0x70474156: /* pGAV (little endian / stereo) [Jak 3 (PS2), Jak X (PS2)] */ + meta_type = meta_PS2_pGAV; + start_offset = 0x00; //todo 0x30, requires interleave_first + + if (read_32bitBE(0x20,streamFile) == 0x53746572) { /* "Ster" */ + channel_count = 2; + + if (read_32bitLE(0x2000,streamFile) == 0x56414770) /* "pGAV" */ + interleave = 0x2000; /* Jak 3 interleave, includes header */ + else if (read_32bitLE(0x1000,streamFile) == 0x56414770) /* "pGAV" */ + interleave = 0x1000; /* Jak X interleave, includes header */ + else + goto fail; + //todo interleave_first = interleave - start_offset; /* interleave includes header */ + } + else { + channel_count = 1; + interleave = 0; + } + + channel_size = read_32bitLE(0x0C,streamFile) / channel_count; + sample_rate = read_32bitLE(0x10,streamFile); + //todo adjust channel_size, includes part of header? + loop_flag = 0; + break; + + case 0x56414770: /* "VAGp" (standard and variations) */ + meta_type = meta_PS2_VAGp; + + if (check_extensions(streamFile,"vig")) { + /* MX vs. ATV Untamed (PS2) */ + start_offset = 0x800 - 0x20; + channel_count = 2; + interleave = 0x10; loop_flag = 0; } + else if (check_extensions(streamFile,"swag")) { /* algo "VAGp" at (file_size / channels) */ + /* Frantix (PSP) */ + start_offset = 0x40; /* channel_size ignores empty frame */ + channel_count = 2; + interleave = file_size / channel_count; + + channel_size = read_32bitLE(0x0c,streamFile); + sample_rate = read_32bitLE(0x10,streamFile); + + loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); + } else if (read_32bitBE(0x6000,streamFile) == 0x56414770) { /* "VAGp" */ - channel_count = 2; /* The Simpsons Wrestling PSX interleave */ + /* The Simpsons Wrestling (PS1) */ + start_offset = 0x00; //todo 0x30, requires interleave_first + channel_count = 2; + interleave = 0x6000; + //todo interleave_first = interleave - start_offset; /* includes header */ + channel_size += 0x30; + loop_flag = 0; } - else if ((version <= 0x00000004) && (datasize < filesize / 2)) { /* two VAGp in the same file */ - if (is_swag) - loop_flag = vag_find_loop_offsets(streamFile, 0x30, &loopStart, &loopEnd); - else - loop_flag = read_32bitBE(0x14,streamFile) != 0; - channel_count=2; - } - else if (version == 0x00020001) { /* HEVAG */ - loop_flag = vag_find_loop_offsets(streamFile, 0x30, &loopStart, &loopEnd); + else if (read_32bitBE(0x1000,streamFile) == 0x56414770) { /* "VAGp" */ + /* Shikigami no Shiro (PS2) */ + start_offset = 0x00; //todo 0x30, requires interleave_first + channel_count = 2; + interleave = 0x1000; + //todo interleave_first = interleave - start_offset; /* includes header */ + channel_size += 0x30; - /* channels are usually at 0x1e, but not in Ukiyo no Roushi which has some kind - * of loop-like values instead (who designs this crap?) */ + loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); + } + else if (read_32bitBE(0x30,streamFile) == 0x56414770) { /* "VAGp" */ + /* The Red Star (PS2) */ + start_offset = 0x60; /* two VAGp headers */ + channel_count = 2; + + if ((file_size - start_offset) % 0x4000 == 0) + interleave = 0x4000; + else if ((file_size - start_offset) % 0x4180 == 0) + interleave = 0x4180; + else + goto fail; + + loop_flag = 0; /* loop segments */ + } + else if (version == 0x40000000) { + /* Killzone (PS2) */ + start_offset = 0x30; + channel_count = 1; + interleave = 0; + + channel_size = read_32bitLE(0x0C,streamFile) / channel_count; + sample_rate = read_32bitLE(0x10,streamFile); + loop_flag = 0; + } + else if (version == 0x00020001 || version == 0x00030000) { + /* standard Vita/PS4 .vag [Chronovolt (Vita), Grand Kingdom (PS4)] */ + start_offset = 0x30; + interleave = 0x10; + + /* channels are at 0x1e, except Ukiyo no Roushi (Vita), which has + * loop start/end frame (but also uses PS-ADPCM flags) */ if (read_32bitBE(0x18,streamFile) == 0 && (read_32bitBE(0x1c,streamFile) & 0xFFFF00FF) == 0 && read_8bit(0x1e,streamFile) < 16) { channel_count = read_8bit(0x1e,streamFile); if (channel_count == 0) - channel_count = 1; /* ex. early Vita vag (Lumines) */ + channel_count = 1; /* ex. early games [Lumines (Vita)] */ } else { channel_count = 1; } + + channel_size = channel_size / channel_count; + loop_flag = ps_find_loop_offsets(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); } else { - loop_flag = vag_find_loop_offsets(streamFile, 0x30, &loopStart, &loopEnd); + /* standard PS1/PS2/PS3 .vag [Ecco the Dolphin (PS2), Legasista (PS3)] */ + start_offset = 0x30; + interleave = 0; + channel_count = 1; + loop_flag = ps_find_loop_offsets_full(streamFile, start_offset, channel_size*channel_count, channel_count, interleave, &loop_start_sample, &loop_end_sample); + allow_dual_stereo = 1; /* often found with external L/R files */ } break; + default: goto fail; } + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ + vgmstream->meta_type = meta_type; + vgmstream->allow_dual_stereo = allow_dual_stereo; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(channel_size,1); + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; vgmstream->coding_type = coding_PSX; - if (is_swag) - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - else - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); + if (version == 0x00020001 || version == 0x00030000) + vgmstream->coding_type = coding_HEVAG; + vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave; + vgmstream->interleave_block_size = interleave; - switch(vagID) { - case '1': // VAG1 - vgmstream->layout_type=layout_none; - vgmstream->num_samples = datasize / 16 * 28; - interleave = read_32bitLE(0x08,streamFile); - if (interleave != 0) goto fail; - vgmstream->meta_type=meta_PS2_VAG1; - start_offset=0x40; /* 0x30 is extra data in VAG1 */ - break; - case '2': // VAG2 - vgmstream->layout_type=layout_interleave; - vgmstream->num_samples = datasize / 16 * 28; /* datasize is for 1 channel only in VAG2 */ - interleave = 0x800; - vgmstream->meta_type=meta_PS2_VAG2; - start_offset=0x40; /* 0x30 is extra data in VAG2 */ - break; - case 'i': // VAGi - vgmstream->layout_type=layout_interleave; - vgmstream->num_samples = datasize / 16 * 28; - interleave = read_32bitLE(0x08,streamFile); - vgmstream->meta_type=meta_PS2_VAGi; - start_offset=0x800; - break; - case 'p': // VAGp - interleave=0x10; + read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); /* always, can be null */ - if (check_extensions(streamFile,"vig")) { /* MX vs. ATV Untamed PS2 */ - vgmstream->layout_type=layout_interleave; - vgmstream->meta_type=meta_PS2_VAGp; - - vgmstream->num_samples = (datasize - 0x10*channel_count) / 16 * 28; - start_offset = 0x800; - } - else if (read_32bitBE(0x6000,streamFile) == 0x56414770) { /* interleaved "VAGp" */ - interleave = 0x6000; /* The Simpsons Wrestling PSX interleave, includes header */ - vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = meta_PS2_VAGs; - - vgmstream->num_samples = datasize / 16 * 28; - start_offset = 0x30; - } - else if ((version == 0x00000004) && (datasize < filesize / 2)) { - vgmstream->channels=2; - vgmstream->layout_type=layout_interleave; - vgmstream->meta_type=meta_PS2_VAGs; - - if (is_swag) { - start_offset = 0x30; - interleave = datasize; - vgmstream->num_samples = datasize / 16 * 28; - vgmstream->loop_start_sample = (loopStart-start_offset) / 16 * 28; - vgmstream->loop_end_sample = (loopEnd-start_offset) / 16 * 28; - loop_samples_found = 1; - - } else { - start_offset=0x80; - vgmstream->num_samples = datasize; /* todo test if datasize/16*28? */ - if(loop_flag) { - vgmstream->loop_start_sample=read_32bitBE(0x14,streamFile); - vgmstream->loop_end_sample =read_32bitBE(0x18,streamFile); - loop_samples_found = 1; - // Double VAG Header @ 0x0000 & 0x1000 - if(read_32bitBE(0,streamFile)==read_32bitBE(0x1000,streamFile)) { - vgmstream->num_samples = datasize / 16 * 28; - interleave=0x1000; - start_offset=0; - } - } - } - } - else if (version == 0x40000000) { /* Guerilla VAG (little endian) */ - datasize = read_32bitLE(0x0c,streamFile); - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->layout_type=layout_none; - vgmstream->meta_type=meta_PS2_VAGp; - - vgmstream->num_samples = datasize / channel_count / 16 * 28; - start_offset = 0x30; - } - else if (version == 0x00020001) { /* HEVAG */ - vgmstream->coding_type = coding_HEVAG; - vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = meta_PS2_VAGs; - - vgmstream->num_samples = datasize / channel_count / 16 * 28; - start_offset = 0x30; - } - else { /* VAGp, usually separate L/R files */ - vgmstream->layout_type=layout_none; - vgmstream->meta_type=meta_PS2_VAGp; - - vgmstream->num_samples = datasize / channel_count / 16 * 28; - start_offset=0x30; - } - break; - case 'V': // pGAV - vgmstream->layout_type=layout_interleave; - interleave=0x2000; /* Jak 3 interleave, includes header */ - - if(read_32bitLE(0x1000,streamFile)==0x56414770) /* "pGAV" */ - interleave=0x1000; /* Jak X interleave, includes header */ - - vgmstream->sample_rate = read_32bitLE(0x10,streamFile); - vgmstream->num_samples = read_32bitLE(0x0C,streamFile)/16*14; - vgmstream->meta_type=meta_PS2_pGAV; - start_offset=0; - break; - default: - goto fail; - } - - vgmstream->interleave_block_size=interleave; - - /* Don't add the header size to loop calc points */ - if(loop_flag && !loop_samples_found) { - loopStart-=start_offset; - loopEnd-=start_offset; - - vgmstream->loop_start_sample = (int32_t)((loopStart/(interleave*channel_count))*interleave)/16*28; - vgmstream->loop_start_sample += (int32_t)(loopStart%(interleave*channel_count))/16*28; - vgmstream->loop_end_sample = (int32_t)((loopEnd/(interleave*channel_count))*interleave)/16*28; - vgmstream->loop_end_sample += (int32_t)(loopEnd%(interleave*channel_count))/16*28; - } - - /* always, but can be null or used as special string */ - read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile); - - - /* open the file for reading */ if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - return vgmstream; fail: close_vgmstream(vgmstream); return NULL; } - - -/** - * Finds loop points in VAG data using flag markers and updates loop_start and loop_end with the global offsets. - * - * returns 0 if not found - */ -static int vag_find_loop_offsets(STREAMFILE *streamFile, off_t start_offset, off_t * loop_start, off_t * loop_end) { - off_t loopStart = 0; - off_t loopEnd = 0; - - /* used for loop points (todo: variations: 0x0c0700..00, 0x070077..77 ) */ - /* 'used to prevent unnecessary SPU interrupts' (optional if no IRQ or no looping) */ - uint8_t eofVAG[16]={0x00,0x07,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77}; - uint8_t eofVAG2[16]={0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t readbuf[16]; - uint8_t flag; - - /* Search for loop in VAG */ - size_t fileLength = get_streamfile_size(streamFile); - - - off_t readOffset = start_offset - 0x10; - do { - readOffset+=0x10; - - flag = read_8bit(readOffset+0x01,streamFile) & 0x0F; /* lower nibble (for HEVAG) */ - - // Loop Start ... - if (flag == 0x06 && !loopStart) { - loopStart = readOffset; - } - - // Loop End ... - if (flag == 0x03 && !loopEnd) { - loopEnd = readOffset; - - if (loopStart && loopEnd) - break; - } - - /* hack for some games that don't have loop points but play the same track on repeat - * (sometimes this will loop non-looping tracks incorrectly) - * if there is a "partial" 0x07 end flag pretend it wants to loop */ - if (flag == 0x01) { - // Check if we have a full eof tag after the loop point ... - // if so we don't loop, if not present, we loop from end to start ... - int read = read_streamfile(readbuf,readOffset+0x10,0x10,streamFile); - /* is there valid data after flag 0x1? */ - if (read > 0 - && readbuf[0] != 0x00 - && readbuf[0] != 0x0c - && readbuf[0] != 0x3c /* Ecco the Dolphin, Ratchet & Clank 2 */ - ) { - if (memcmp(readbuf,eofVAG,0x10) && (memcmp(readbuf,eofVAG2,0x10))) { /* full end flags */ - loopStart = start_offset + 0x10; /* todo proper start */ - loopEnd = readOffset; - break; - } - } - } - - } while (streamFile->get_offset(streamFile)<(off_t)fileLength); - - - if (loopStart && loopEnd) { - *loop_start = loopStart; - *loop_end = loopEnd; - return 1; - } - - return 0; -} diff --git a/src/meta/vawx.c b/src/meta/vawx.c index 32d89332..02a689ca 100644 --- a/src/meta/vawx.c +++ b/src/meta/vawx.c @@ -103,10 +103,6 @@ VGMSTREAM * init_vgmstream_vawx(STREAMFILE *streamFile) { if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) goto fail; - - if (vgmstream->layout_type == layout_blocked_vawx) - block_update_vawx(start_offset,vgmstream); - return vgmstream; fail: diff --git a/src/meta/vgs.c b/src/meta/vgs.c index ba2e66de..db55f8fb 100644 --- a/src/meta/vgs.c +++ b/src/meta/vgs.c @@ -50,25 +50,22 @@ VGMSTREAM * init_vgmstream_vgs(STREAMFILE *streamFile) { channel_count++; } + start_offset = 0x80; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - start_offset = 0x80; + vgmstream->meta_type = meta_VGS; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = ps_bytes_to_samples(channel_size*channel_count, channel_count); vgmstream->coding_type = coding_PSX_badflags; /* flag = stream/channel number */ vgmstream->layout_type = layout_blocked_vgs; - vgmstream->meta_type = meta_VGS; - - /* open files; channel offsets are updated below */ if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) goto fail; - block_update_vgs(start_offset, vgmstream); - return vgmstream; fail: close_vgmstream(vgmstream); diff --git a/src/meta/wave_segmented.c b/src/meta/wave_segmented.c index a4af548c..263b069b 100644 --- a/src/meta/wave_segmented.c +++ b/src/meta/wave_segmented.c @@ -134,6 +134,7 @@ VGMSTREAM * init_vgmstream_wave_segmented(STREAMFILE *streamFile) { break; } +#ifdef VGM_USE_VORBIS case 0x04: { /* "vorbis" */ ogg_vorbis_meta_info_t ovmi = {0}; @@ -153,6 +154,7 @@ VGMSTREAM * init_vgmstream_wave_segmented(STREAMFILE *streamFile) { break; } +#endif default: /* others: s16be/s16le/mp3 as referenced in the exe? */ VGM_LOG("WAVE: unknown codec\n"); @@ -205,10 +207,7 @@ VGMSTREAM * init_vgmstream_wave_segmented(STREAMFILE *streamFile) { /* .wave can mix codecs, usually first segment is a small ADPCM section) */ vgmstream->coding_type = (segment_count == 1 ? data->segments[0]->coding_type : data->segments[1]->coding_type); vgmstream->layout_type = layout_segmented; - vgmstream->layout_data = data; - if (loop_flag) - data->loop_segment = (loop_start_segment); return vgmstream; diff --git a/src/meta/wavebatch.c b/src/meta/wavebatch.c index bb72db1b..03cb3a50 100644 --- a/src/meta/wavebatch.c +++ b/src/meta/wavebatch.c @@ -98,6 +98,12 @@ VGMSTREAM * init_vgmstream_wavebatch(STREAMFILE *streamFile) { vgmstream->interleave_block_size = 0x02; break; + case 0x01: /* PCM8 [Cars 2 (3DS)] */ + vgmstream->coding_type = coding_PCM8; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x01; + break; + case 0x02: { /* DSP [WRC FIA World Rally Championship (3DS)] */ size_t config_size = (0x20+0x14)*channel_count + (0x0c)*channel_count; /* coefs+hist + padding */ @@ -113,7 +119,7 @@ VGMSTREAM * init_vgmstream_wavebatch(STREAMFILE *streamFile) { } default: - VGM_LOG("WAVEBATCH: unknown codec\n"); + VGM_LOG("WAVEBATCH: unknown codec %x\n", codec); goto fail; } diff --git a/src/meta/wsi.c b/src/meta/wsi.c new file mode 100644 index 00000000..3f02260d --- /dev/null +++ b/src/meta/wsi.c @@ -0,0 +1,83 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* .wsi - blocked dsp [Alone in the Dark (Wii)] */ +VGMSTREAM * init_vgmstream_wsi(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, header_offset; + size_t header_spacing; + int loop_flag, channel_count; + + + /* checks */ + if (!check_extensions(streamFile, "wsi")) + goto fail; + + channel_count = read_32bitBE(0x04,streamFile); + if (channel_count != 2) goto fail; /* assumed */ + + /* check for consistent block headers */ + { + off_t block_offset; + off_t block_size_has_been; + int i; + + block_offset = read_32bitBE(0x00,streamFile); + if (block_offset < 0x08) goto fail; + + block_size_has_been = block_offset; + + /* check 4 blocks, to get an idea */ + for (i = 0; i < 4*channel_count; i++) { + off_t block_size = read_32bitBE(block_offset,streamFile); + + if (block_size < 0x10) + goto fail; /* expect at least the block header */ + if (i%channel_count+1 != read_32bitBE(block_offset+0x08,streamFile)) + goto fail; /* expect the channel numbers to alternate */ + + if (i%channel_count==0) + block_size_has_been = block_size; + else if (block_size != block_size_has_been) + goto fail; /* expect every block in a set of channels to have the same size */ + + block_offset += block_size; + } + } + + start_offset = read_32bitBE(0x00, streamFile); + header_offset = start_offset + 0x10; + header_spacing = read_32bitBE(start_offset,streamFile); + + /* contains standard DSP header, but since it's blocked validations (start/loop ps, etc) + * will fail, so no point to handle as standard DSP */ + loop_flag = read_16bitBE(header_offset+0x0c,streamFile); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_DSP_WSI; + vgmstream->sample_rate = read_32bitBE(header_offset+0x08,streamFile); + + vgmstream->num_samples = read_32bitBE(header_offset+0x00,streamFile) / 14 * 14; /* remove incomplete last frame */ + vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bitBE(header_offset+0x10,streamFile)); + vgmstream->loop_end_sample = dsp_nibbles_to_samples(read_32bitBE(header_offset+0x14,streamFile))+1; + if (vgmstream->loop_end_sample > vgmstream->num_samples) /* ? */ + vgmstream->loop_end_sample = vgmstream->num_samples; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_blocked_wsi; + dsp_read_coefs_be(vgmstream, streamFile, header_offset+0x1c, header_spacing); + dsp_read_hist_be(vgmstream, streamFile, header_offset+0x40, header_spacing); + + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/wwise.c b/src/meta/wwise.c index c48c8c2c..fd8ddd85 100644 --- a/src/meta/wwise.c +++ b/src/meta/wwise.c @@ -172,8 +172,9 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) { else if (ww.format == 0x0002 && ww.block_align == 0x104 * ww.channels) { //ww.codec = SWITCH_ADPCM; /* unknown codec, found in Bayonetta 2 (Switch) - * frames of 0x104 per ch, possibly frame header is hist1(2)/hist2(2)/predictor(1) - * (may write 2 header samples + FF*2 nibbles = 0x200 samples per block?) */ + * frames of 0x104 per ch, possibly frame header is hist1(2)/hist2(2)/index(1) + * (may write 2 header samples + FF*2 nibbles = 0x200 samples per block?) + * index only goes up to ~0xb, may be a shift/scale value */ goto fail; } diff --git a/src/meta/xbox_xvas.c b/src/meta/xbox_xvas.c index 6ee37b86..acd09c8b 100644 --- a/src/meta/xbox_xvas.c +++ b/src/meta/xbox_xvas.c @@ -9,10 +9,10 @@ VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile) { int loop_flag, channel_count; size_t data_size; - /* check extension */ + + /* checks */ if (!check_extensions(streamFile,"xvas")) goto fail; - if (read_32bitLE(0x00,streamFile) != 0x69 && /* codec */ read_32bitLE(0x08,streamFile) != 0x48) /* block size (probably 0x24 for mono) */ goto fail; @@ -23,10 +23,12 @@ VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile) { data_size = read_32bitLE(0x24,streamFile); data_size -= (data_size / 0x20000) * 0x20; /* blocks of 0x20000 with padding */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_XBOX_XVAS; vgmstream->sample_rate = read_32bitLE(0x0c,streamFile); vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); if(loop_flag) { @@ -38,12 +40,9 @@ VGMSTREAM * init_vgmstream_xbox_xvas(STREAMFILE *streamFile) { vgmstream->coding_type = coding_XBOX_IMA; vgmstream->layout_type = layout_blocked_xvas; - vgmstream->meta_type = meta_XBOX_XVAS; if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) goto fail; - - block_update_xvas(start_offset,vgmstream); return vgmstream; fail: diff --git a/src/meta/xvag.c b/src/meta/xvag.c index 06868745..27614b61 100644 --- a/src/meta/xvag.c +++ b/src/meta/xvag.c @@ -1,37 +1,54 @@ #include "meta.h" #include "../coding/coding.h" #include "../layout/layout.h" +#include "xvag_streamfile.h" -static int ps_adpcm_find_loop_offsets(STREAMFILE *streamFile, int channel_count, off_t start_offset, off_t * loop_start, off_t * loop_end); +typedef struct { + int big_endian; + int channels; + int sample_rate; + int codec; + + int factor; + + int loop_flag; + int num_samples; + int loop_start; + int loop_end; + + int subsongs; + int layers; + + size_t data_size; + off_t stream_offset; +} xvag_header; + +static int init_xvag_atrac9(STREAMFILE *streamFile, VGMSTREAM* vgmstream, xvag_header * xvag, off_t chunk_offset); +static layered_layout_data* build_layered_xvag(STREAMFILE *streamFile, xvag_header * xvag, off_t chunk_offset, off_t start_offset); /* XVAG - Sony's Scream Tool/Stream Creator format (God of War III, Ratchet & Clank Future, The Last of Us, Uncharted) */ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; + STREAMFILE* temp_streamFile = NULL; + xvag_header xvag = {0}; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int loop_flag = 0, channel_count, codec; - int big_endian; - int sample_rate, num_samples, interleave_factor, multistreams = 0; + off_t start_offset, chunk_offset, first_offset = 0x20; + size_t chunk_size; int total_subsongs = 0, target_subsong = streamFile->stream_index; - off_t start_offset, loop_start = 0, loop_end = 0, chunk_offset; - off_t first_offset = 0x20; - size_t chunk_size, stream_size; - /* checks */ /* .xvag: standard * (extensionless): The Last Of Us (PS3) speech files */ if (!check_extensions(streamFile,"xvag,")) goto fail; - - /* check header */ if (read_32bitBE(0x00,streamFile) != 0x58564147) /* "XVAG" */ goto fail; /* endian flag (XVAGs of the same game can use BE or LE, usually when reusing from other platforms) */ - big_endian = read_8bit(0x08,streamFile) & 0x01; - if (big_endian) { + xvag.big_endian = read_8bit(0x08,streamFile) & 0x01; + if (xvag.big_endian) { read_32bit = read_32bitBE; } else { read_32bit = read_32bitLE; @@ -45,29 +62,31 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { /* "fmat": base format (always first) */ - if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,&chunk_size, big_endian, 1)) /*"fmat"*/ + if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,&chunk_size, xvag.big_endian, 1)) /*"fmat"*/ goto fail; - channel_count = read_32bit(chunk_offset+0x00,streamFile); - codec = read_32bit(chunk_offset+0x04,streamFile); - num_samples = read_32bit(chunk_offset+0x08,streamFile); - /* 0x0c: samples again? playable section? */ - VGM_ASSERT(num_samples != read_32bit(chunk_offset+0x0c,streamFile), "XVAG: num_samples values don't match\n"); + xvag.channels = read_32bit(chunk_offset+0x00,streamFile); + xvag.codec = read_32bit(chunk_offset+0x04,streamFile); + xvag.num_samples = read_32bit(chunk_offset+0x08,streamFile); + /* 0x0c: samples again? */ + VGM_ASSERT(xvag.num_samples != read_32bit(chunk_offset+0x0c,streamFile), "XVAG: num_samples values don't match\n"); - interleave_factor = read_32bit(chunk_offset+0x10,streamFile); - sample_rate = read_32bit(chunk_offset+0x14,streamFile); - stream_size = read_32bit(chunk_offset+0x18,streamFile); + xvag.factor = read_32bit(chunk_offset+0x10,streamFile); /* for interleave */ + xvag.sample_rate = read_32bit(chunk_offset+0x14,streamFile); + xvag.data_size = read_32bit(chunk_offset+0x18,streamFile); /* not always accurate */ /* extra data, seen in versions 0x61+ */ if (chunk_size > 0x1c) { - /* number of interleaved subsong layers */ - total_subsongs = read_32bit(chunk_offset+0x1c,streamFile); - /* number of interleaved streams per layer (multistreams * channels_per_stream = channels) */ - multistreams = read_32bit(chunk_offset+0x20,streamFile); + /* number of interleaved subsongs */ + xvag.subsongs = read_32bit(chunk_offset+0x1c,streamFile); + /* number of interleaved layers (layers * channels_per_layer = channels) */ + xvag.layers = read_32bit(chunk_offset+0x20,streamFile); } else { - total_subsongs = 1; - multistreams = 1; + xvag.subsongs = 1; + xvag.layers = 1; } + + total_subsongs = xvag.subsongs; if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; @@ -75,69 +94,72 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { /* other chunks: */ /* "cpan": pan/volume per channel */ /* "cues": cue/labels (rare) */ + /* "md5 ": hash (rare) */ /* "0000": end chunk before start_offset */ - /* some XVAG seem to do full loops, this should detect them as looping (basically tests is last frame is empty) */ - //todo remove, looping seems external and specified in Scream Tool's bank formats - if (codec == 0x06 && total_subsongs == 1) { - loop_flag = ps_adpcm_find_loop_offsets(streamFile, channel_count, start_offset, &loop_start, &loop_end); + /* XVAG has no looping, but some PS3 PS-ADPCM seems to do full loops (without data flags) */ + if (xvag.codec == 0x06 && xvag.subsongs == 1) { + size_t file_size = get_streamfile_size(streamFile); + /* simply test if last frame is not empty = may loop */ + xvag.loop_flag = (read_8bit(file_size - 0x01, streamFile) != 0); + xvag.loop_start = 0; + xvag.loop_end = ps_bytes_to_samples(file_size - start_offset, xvag.channels); } /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(xvag.channels,xvag.loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; - vgmstream->num_streams = total_subsongs; - vgmstream->stream_size = (stream_size / total_subsongs); vgmstream->meta_type = meta_XVAG; + vgmstream->sample_rate = xvag.sample_rate; + vgmstream->num_samples = xvag.num_samples; + if (xvag.loop_flag) { + vgmstream->loop_start_sample = xvag.loop_start; + vgmstream->loop_end_sample = xvag.loop_end; + } + vgmstream->num_streams = total_subsongs; + vgmstream->stream_size = (xvag.data_size / total_subsongs); - switch (codec) { - case 0x06: /* VAG (PS-ADPCM): God of War III (PS3), Uncharted 1/2 (PS3), Ratchet and Clank Future (PS3) */ - case 0x07: /* SVAG? (PS-ADPCM with extended table?): inFamous 1 (PS3) */ - if (multistreams > 1 && multistreams != vgmstream->channels) goto fail; - if (total_subsongs > 1 && multistreams > 1) goto fail; - if (total_subsongs > 1 && vgmstream->channels > 1) goto fail; /* unknown layout */ + switch (xvag.codec) { + case 0x06: /* VAG (PS-ADPCM): God of War III (PS3), Uncharted 1/2 (PS3), Ratchet and Clank Future (PS3) */ + case 0x07: /* SVAG? (PS-ADPCM with extended table?): inFamous 1 (PS3) */ + if (xvag.subsongs > 1 && xvag.layers > 1) goto fail; + if (xvag.layers > 1 && xvag.layers != xvag.channels) goto fail; + if (xvag.subsongs > 1 && xvag.channels > 1) goto fail; /* unknown layout */ vgmstream->coding_type = coding_PSX; - if (total_subsongs > 1) { /* God of War 3 (PS4) */ + if (xvag.subsongs > 1) { /* God of War 3 (PS4) */ vgmstream->layout_type = layout_blocked_xvag_subsong; vgmstream->interleave_block_size = 0x10; - vgmstream->full_block_size = 0x10 * interleave_factor * total_subsongs; - vgmstream->current_block_size = 0x10 * interleave_factor; - start_offset += 0x10 * interleave_factor * (target_subsong-1); + vgmstream->full_block_size = 0x10 * xvag.factor * xvag.subsongs; + vgmstream->current_block_size = 0x10 * xvag.factor; + start_offset += vgmstream->current_block_size * (target_subsong-1); } else { vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10 * interleave_factor; /* usually 1, bigger in GoW3 PS4 */ + vgmstream->interleave_block_size = 0x10 * xvag.factor; /* usually 1, bigger in GoW3 PS4 */ } - - if (loop_flag) { - vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels); - vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels); - } - break; #ifdef VGM_USE_MPEG - case 0x08: { /* MPEG: The Last of Us (PS3), Uncharted 3 (PS3), Medieval Moves (PS3) */ + case 0x08: { /* MPEG: The Last of Us (PS3), Uncharted 3 (PS3), Medieval Moves (PS3) */ mpeg_custom_config cfg = {0}; + if (xvag.subsongs > 1) goto fail; + if (xvag.subsongs > 1 && xvag.layers > 1) goto fail; /* often 2ch per MPEG and rarely 1ch (GoW3 PS4) */ - if (multistreams > 1 && !(multistreams*1 == vgmstream->channels || multistreams*2 == vgmstream->channels)) goto fail; - if (total_subsongs > 1) goto fail; - //todo rare test file in The Last of Us PS4 uses 6ch with 1 2ch stream, surround MPEG/mp3pro? + if (xvag.layers > 1 && !(xvag.layers*1 == vgmstream->channels || xvag.layers*2 == vgmstream->channels)) goto fail; + //todo rare test file in The Last of Us PS4 uses 6ch with one 2ch stream, surround MPEG/mp3pro? (decoded samples map to 6ch) /* "mpin": mpeg info */ /* 0x00/04: mpeg version/layer? other: unknown or repeats of "fmat" */ - if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, big_endian, 1)) /*"mpin"*/ + if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, xvag.big_endian, 1)) /*"mpin"*/ goto fail; cfg.chunk_size = read_32bit(chunk_offset+0x1c,streamFile); /* fixed frame size */ - cfg.interleave = cfg.chunk_size * interleave_factor; + cfg.interleave = cfg.chunk_size * xvag.factor; vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg); if (!vgmstream->codec_data) goto fail; @@ -148,140 +170,125 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) { #ifdef VGM_USE_ATRAC9 case 0x09: { /* ATRAC9: Sly Cooper and the Thievius Raccoonus (Vita), The Last of Us Remastered (PS4) */ - atrac9_config cfg = {0}; - size_t frame_size; + if (xvag.subsongs > 1 && xvag.layers > 1) goto fail; /* "a9in": ATRAC9 info */ /* 0x00: frame size, 0x04: samples per frame, 0x0c: fact num_samples (no change), 0x10: encoder delay1 */ - if (!find_chunk(streamFile, 0x6139696E,first_offset,0, &chunk_offset,NULL, big_endian, 1)) /*"a9in"*/ + if (!find_chunk(streamFile, 0x6139696E,first_offset,0, &chunk_offset,NULL, xvag.big_endian, 1)) /*"a9in"*/ goto fail; - frame_size = read_32bit(chunk_offset+0x00,streamFile); + if (xvag.layers > 1) { + /* some Vita/PS4 multichannel [flower (Vita), Uncharted Collection (PS4)]. PS4 ATRAC9 also + * does single-stream >2ch, but this can do configs ATRAC9 can't, like 5ch/14ch/etc */ + vgmstream->layout_data = build_layered_xvag(streamFile, &xvag, chunk_offset, start_offset); + if (!vgmstream->layout_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_layered; - cfg.type = ATRAC9_XVAG; - cfg.channels = vgmstream->channels; - cfg.config_data = read_32bitBE(chunk_offset+0x08,streamFile); - cfg.encoder_delay = read_32bit(chunk_offset+0x14,streamFile); + break; + } + else { + /* interleaved subsongs (section layers) */ + size_t frame_size = read_32bit(chunk_offset+0x00,streamFile); - if (total_subsongs > 1 && multistreams > 1) { - VGM_LOG("XVAG: unknown %i subsongs and %i multistreams\n", total_subsongs, multistreams); - goto fail; /* not known */ + if (!init_xvag_atrac9(streamFile, vgmstream, &xvag, chunk_offset)) + goto fail; + temp_streamFile = setup_xvag_streamfile(streamFile, start_offset, frame_size*xvag.factor,frame_size, (target_subsong-1), total_subsongs); + if (!temp_streamFile) goto fail; + start_offset = 0; } - else if (total_subsongs > 1) { - /* interleaves 'multiplier' superframes per subsong (all share config_data) */ - cfg.interleave_skip = frame_size * interleave_factor; - cfg.subsong_skip = total_subsongs; - /* start in subsong's first superframe */ - start_offset += (target_subsong-1) * cfg.interleave_skip * (cfg.subsong_skip-1); - } - else if (multistreams > 1) { - /* Vita multichannel (flower) interleaves streams like MPEG - * PS4 (The Last of Us) uses ATRAC9's multichannel directly instead (multistreams==1) */ - VGM_LOG("XVAG: unknown %i multistreams of size %x\n", multistreams, frame_size * interleave_factor); - goto fail;//todo add - } - //if (multistreams == vgmstream->channels) goto fail; - vgmstream->codec_data = init_atrac9(&cfg); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_ATRAC9; - vgmstream->layout_type = layout_none; break; } #endif - //case 0x??: /* PCM? */ default: goto fail; } - /* open the file for reading */ - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream,temp_streamFile ? temp_streamFile : streamFile,start_offset)) goto fail; - - if (vgmstream->layout_type == layout_blocked_xvag_subsong) - block_update_xvag_subsong(start_offset, vgmstream); - + close_streamfile(temp_streamFile); return vgmstream; fail: - if (vgmstream) close_vgmstream(vgmstream); + close_streamfile(temp_streamFile); + close_vgmstream(vgmstream); return NULL; } +#ifdef VGM_USE_ATRAC9 +static int init_xvag_atrac9(STREAMFILE *streamFile, VGMSTREAM* vgmstream, xvag_header * xvag, off_t chunk_offset) { + int32_t (*read_32bit)(off_t,STREAMFILE*) = xvag->big_endian ? read_32bitBE : read_32bitLE; + atrac9_config cfg = {0}; -static int ps_adpcm_find_loop_offsets(STREAMFILE *streamFile, int channel_count, off_t start_offset, off_t * loop_start, off_t * loop_end) { - uint8_t testBuffer[0x10]; - int loopStartPointsCount=0; - int loopEndPointsCount=0; - off_t readOffset = 0; - off_t loopStartPoints[0x10]; - off_t loopEndPoints[0x10]; + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bitBE(chunk_offset+0x08,streamFile); + cfg.encoder_delay = read_32bit(chunk_offset+0x14,streamFile); - off_t loopStart = 0; - off_t loopEnd = 0; - off_t fileLength; - int loop_flag = 0; + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; - readOffset=start_offset; - fileLength = get_streamfile_size(streamFile); - - // get the loops the same way we get on .MIB - do { - readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); - - // Loop Start ... - if(testBuffer[0x01]==0x06) { - if(loopStartPointsCount<0x10) { - loopStartPoints[loopStartPointsCount] = readOffset-0x10; - loopStartPointsCount++; - } - } - - // Loop End ... - if(((testBuffer[0x01]==0x03) && (testBuffer[0x03]!=0x77)) || (testBuffer[0x01]==0x01)) { - if(loopEndPointsCount<0x10) { - loopEndPoints[loopEndPointsCount] = readOffset; //-0x10; - loopEndPointsCount++; - } - } - - } while (readOffset<((int32_t)fileLength)); - - // Calc Loop Points & Interleave ... - if(loopStartPointsCount>=channel_count) { - // can't get more then 0x10 loop point ! - if((loopStartPointsCount<=0x0F) && (loopStartPointsCount>=2)) { - // Always took the first 2 loop points - loopStart=loopStartPoints[1]-start_offset; - loop_flag=1; - } else { - loopStart=0; - } - } - - if(loopEndPointsCount>=channel_count) { - // can't get more then 0x10 loop point ! - if((loopEndPointsCount<=0x0F) && (loopEndPointsCount>=2)) { - loop_flag=1; - loopEnd=loopEndPoints[loopEndPointsCount-1]-start_offset; - } else { - loopEnd=0; - } - } - - // as i can get on the header if a song is looped or not - // if try to take the loop marker on the file - // if the last byte of the file is = 00 is assume that the song is not looped - // i know that i can cover 95% of the file, but can't work on some of them - if(read_8bit((fileLength-1),streamFile)==0) - loop_flag=0; - - if (loop_flag) { - *loop_start = loopStart; - *loop_end = loopEnd; - } - - return loop_flag; + return 1; +fail: + return 0; +} +#endif + +static layered_layout_data* build_layered_xvag(STREAMFILE *streamFile, xvag_header * xvag, off_t chunk_offset, off_t start_offset) { + layered_layout_data* data = NULL; + STREAMFILE* temp_streamFile = NULL; + int32_t (*read_32bit)(off_t,STREAMFILE*) = xvag->big_endian ? read_32bitBE : read_32bitLE; + int i, layers = xvag->layers; + + + /* init layout */ + data = init_layout_layered(layers); + if (!data) goto fail; + + /* interleaves frames per substreams */ + for (i = 0; i < layers; i++) { + int layer_channels = xvag->channels / layers; /* all streams must be equal (XVAG limitation) */ + + /* build the layer VGMSTREAM */ + data->layers[i] = allocate_vgmstream(layer_channels, xvag->loop_flag); + if (!data->layers[i]) goto fail; + + data->layers[i]->sample_rate = xvag->sample_rate; + data->layers[i]->num_samples = xvag->num_samples; + + switch(xvag->codec) { +#ifdef VGM_USE_ATRAC9 + case 0x09: { + size_t frame_size = read_32bit(chunk_offset+0x00,streamFile); + + if (!init_xvag_atrac9(streamFile, data->layers[i], xvag, chunk_offset)) + goto fail; + temp_streamFile = setup_xvag_streamfile(streamFile, start_offset, frame_size*xvag->factor,frame_size, i, layers); + if (!temp_streamFile) goto fail; + break; + } +#endif + default: + goto fail; + } + + if ( !vgmstream_open_stream(data->layers[i], temp_streamFile, 0x00) ) { + goto fail; + } + } + + /* setup layered VGMSTREAMs */ + if (!setup_layout_layered(data)) + goto fail; + close_streamfile(temp_streamFile); + return data; + +fail: + close_streamfile(temp_streamFile); + free_layout_layered(data); + return NULL; } diff --git a/src/meta/xvag_streamfile.h b/src/meta/xvag_streamfile.h new file mode 100644 index 00000000..bb9820ac --- /dev/null +++ b/src/meta/xvag_streamfile.h @@ -0,0 +1,163 @@ +#ifndef _XVAG_STREAMFILE_H_ +#define _XVAG_STREAMFILE_H_ +#include "../streamfile.h" + + +typedef struct { + /* config */ + int stream_number; + int stream_count; + size_t interleave_size; + size_t frame_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; +} xvag_io_data; + + +static size_t xvag_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, xvag_io_data* data) { + size_t total_read = 0; + + /* 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) { + data->skip_size = data->interleave_size * data->stream_number; + data->data_size = data->interleave_size; + + /* some ATRAC9 XVAG have padding+RIFF at start [The Last of Us (PS4), Farpoint (PS4)] */ + if (data->logical_offset == 0 && read_32bitBE(data->physical_offset+data->skip_size,streamfile) == 0) { + data->skip_size += data->frame_size; + data->data_size -= data->frame_size; + } + } + + /* move to next block */ + if (offset >= data->logical_offset + data->data_size) { + data->physical_offset += data->interleave_size*data->stream_count; + 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 xvag_io_size(STREAMFILE *streamfile, xvag_io_data* data) { + off_t physical_offset = data->stream_offset; + off_t max_physical_offset = get_streamfile_size(streamfile); + size_t logical_size = 0; + + if (data->logical_size) + return data->logical_size; + + /* get size of the logical stream */ + while (physical_offset < max_physical_offset) { + size_t skip_size = data->interleave_size * data->stream_number; + size_t data_size = data->interleave_size; + + /* some ATRAC9 XVAG have padding+RIFF at start [The Last of Us (PS4), Farpoint (PS4)] */ + if (logical_size == 0 && read_32bitBE(physical_offset+skip_size,streamfile) == 0) { + skip_size += data->frame_size; + data_size -= data->frame_size; + } + + logical_size += data_size; + physical_offset += data->interleave_size*data->stream_count; + } + + if (logical_size > max_physical_offset) + return 0; + data->logical_size = logical_size; + return data->logical_size; +} + +/* Prepares custom IO for XVAG, which interleaves many superframes per subsong/layer. + * May have start padding, even with only one subsong. All layers share config_data too. */ +static STREAMFILE* setup_xvag_streamfile(STREAMFILE *streamFile, off_t stream_offset, size_t interleave_size, size_t frame_size, int stream_number, int stream_count) { + STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL; + xvag_io_data io_data = {0}; + size_t io_data_size = sizeof(xvag_io_data); + + io_data.stream_number = stream_number; + io_data.stream_count = stream_count; + io_data.stream_offset = stream_offset; + //io_data.stream_size = stream_size; + io_data.interleave_size = interleave_size; + io_data.frame_size = frame_size; + io_data.physical_offset = stream_offset; + io_data.logical_size = xvag_io_size(streamFile, &io_data); /* force init */ + + if (io_data.logical_size == 0) { + VGM_LOG("XVAG: 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, xvag_io_read,xvag_io_size); + if (!new_streamFile) goto fail; + temp_streamFile = new_streamFile; + + return temp_streamFile; + +fail: + close_streamfile(temp_streamFile); + return NULL; +} + + +#endif /* _XVAG_STREAMFILE_H_ */ diff --git a/src/meta/xwb.c b/src/meta/xwb.c index 8f7ad6ab..bad57882 100644 --- a/src/meta/xwb.c +++ b/src/meta/xwb.c @@ -83,13 +83,15 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile,"xwb")) + /* .xwb: standard + * .xna: Touhou Makukasai ~ Fantasy Danmaku Festival (PC) */ + if (!check_extensions(streamFile,"xwb,xna")) goto fail; if ((read_32bitBE(0x00,streamFile) != 0x57424E44) && /* "WBND" (LE) */ (read_32bitBE(0x00,streamFile) != 0x444E4257)) /* "DNBW" (BE) */ goto fail; - xwb.little_endian = read_32bitBE(0x00,streamFile) == 0x57424E44;/* WBND */ + xwb.little_endian = read_32bitBE(0x00,streamFile) == 0x57424E44; /* WBND */ if (xwb.little_endian) { read_32bit = read_32bitLE; } else { diff --git a/src/streamfile.c b/src/streamfile.c index e81bc3aa..ae9e615e 100644 --- a/src/streamfile.c +++ b/src/streamfile.c @@ -9,13 +9,15 @@ /* a STREAMFILE that operates via standard IO using a buffer */ typedef struct { STREAMFILE sf; /* callbacks */ + FILE * infile; /* actual FILE */ - char name[PATH_LIMIT]; - off_t offset; /* current offset */ - size_t validsize; /* current buffer size */ + char name[PATH_LIMIT]; /* FILE filename */ + off_t offset; /* last read offset (info) */ + off_t buffer_offset; /* current buffer data start */ uint8_t * buffer; /* data buffer */ size_t buffersize; /* max buffer size */ - size_t filesize; /* cached file size (max offset) */ + size_t validsize; /* current buffer size */ + size_t filesize; /* buffered file size */ } STDIOSTREAMFILE; static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize); @@ -28,9 +30,9 @@ static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offse return 0; /* is the part of the requested length in the buffer? */ - if (offset >= streamfile->offset && offset < streamfile->offset + streamfile->validsize) { + if (offset >= streamfile->buffer_offset && offset < streamfile->buffer_offset + streamfile->validsize) { size_t length_to_read; - off_t offset_into_buffer = offset - streamfile->offset; + off_t offset_into_buffer = offset - streamfile->buffer_offset; length_to_read = streamfile->validsize - offset_into_buffer; if (length_to_read > length) @@ -43,32 +45,34 @@ static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offse dest += length_to_read; } - /* What would make more sense here is to read the whole request - * at once into the dest buffer, as it must be large enough, and then - * copy some part of that into our own buffer. - * The destination buffer is supposed to be much smaller than the - * STREAMFILE buffer, though. Maybe we should only ever return up - * to the buffer size to avoid having to deal with things like this - * which are outside of my intended use. */ /* read the rest of the requested length */ while (length > 0) { - size_t length_to_read, length_read; - streamfile->validsize = 0; /* buffer is empty now */ + size_t length_to_read; - /* request outside file: ignore to avoid seek/read */ - if (offset > streamfile->filesize) { - streamfile->offset = streamfile->filesize; - VGM_LOG_ONCE("ERROR: reading over filesize 0x%x @ 0x%lx + 0x%x (buggy meta?)\n", streamfile->filesize, offset, length); - return length_read_total; /* partially-read buffer */ + /* ignore requests at EOF */ + if (offset >= streamfile->filesize) { + //offset = streamfile->filesize; /* seems fseek doesn't clamp offset */ + VGM_ASSERT_ONCE(offset > streamfile->filesize, "STDIO: reading over filesize 0x%x @ 0x%lx + 0x%x\n", streamfile->filesize, offset, length); + break; } /* position to new offset */ if (fseeko(streamfile->infile,offset,SEEK_SET)) { - streamfile->offset = streamfile->filesize; - return 0; /* fail miserably (fseek shouldn't fail and reach this) */ + break; /* this shouldn't happen in our code */ } - streamfile->offset = offset; + +#ifdef _MSC_VER + /* Workaround a bug that appears when compiling with MSVC (later versions). + * This bug is deterministic and seemingly appears randomly after seeking. + * It results in fread returning data from the wrong area of the file. + * HPS is one format that is almost always affected by this. */ + fseek(streamfile->infile, ftell(streamfile->infile), SEEK_SET); +#endif + + /* fill the buffer (offset now is beyond buffer_offset) */ + streamfile->buffer_offset = offset; + streamfile->validsize = fread(streamfile->buffer,sizeof(uint8_t),streamfile->buffersize,streamfile->infile); /* decide how much must be read this time */ if (length > streamfile->buffersize) @@ -76,57 +80,41 @@ static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offse else length_to_read = length; -#ifdef _MSC_VER - /* Workaround a bug that appears when compiling witn MSVC. - * This bug is dertiministic and seemingly appears randomly - * after seeking. - * It results in fread returning data from the wrong - * area of the file. - * HPS is one format that is almost always affected - * by this. */ - fseek(streamfile->infile, ftell(streamfile->infile), SEEK_SET); -#endif - - /* fill the buffer */ - length_read = fread(streamfile->buffer,sizeof(uint8_t),streamfile->buffersize,streamfile->infile); - streamfile->validsize = length_read; - - /* if we can't get enough to satisfy the request (EOF) we give up */ - if (length_read < length_to_read) { - memcpy(dest,streamfile->buffer,length_read); - return length_read_total + length_read; /* partially-read buffer */ + /* give up on partial reads (EOF) */ + if (streamfile->validsize < length_to_read) { + memcpy(dest,streamfile->buffer,streamfile->validsize); + offset += streamfile->validsize; + length_read_total += streamfile->validsize; + break; } /* use the new buffer */ memcpy(dest,streamfile->buffer,length_to_read); + offset += length_to_read; length_read_total += length_to_read; length -= length_to_read; dest += length_to_read; - offset += length_to_read; } + streamfile->offset = offset; /* last fread offset */ return length_read_total; } - +static size_t get_size_stdio(STDIOSTREAMFILE * streamfile) { + return streamfile->filesize; +} +static off_t get_offset_stdio(STDIOSTREAMFILE *streamfile) { + return streamfile->offset; +} +static void get_name_stdio(STDIOSTREAMFILE *streamfile,char *buffer,size_t length) { + strncpy(buffer,streamfile->name,length); + buffer[length-1]='\0'; +} static void close_stdio(STDIOSTREAMFILE * streamfile) { fclose(streamfile->infile); free(streamfile->buffer); free(streamfile); } -static size_t get_size_stdio(STDIOSTREAMFILE * streamfile) { - return streamfile->filesize; -} - -static off_t get_offset_stdio(STDIOSTREAMFILE *streamfile) { - return streamfile->offset; -} - -static void get_name_stdio(STDIOSTREAMFILE *streamfile,char *buffer,size_t length) { - strncpy(buffer,streamfile->name,length); - buffer[length-1]='\0'; -} - static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const filename,size_t buffersize) { int newfd; FILE *newfile; @@ -264,12 +252,12 @@ static size_t buffer_read(BUFFER_STREAMFILE *streamfile, uint8_t * dest, off_t o /* ignore requests at EOF */ if (offset >= streamfile->filesize) { - //offset = streamfile->filesize; /* seems fseek doesn't clamp offset */ //todo once + //offset = streamfile->filesize; /* seems fseek doesn't clamp offset */ VGM_ASSERT_ONCE(offset > streamfile->filesize, "BUFFER: reading over filesize 0x%x @ 0x%lx + 0x%x\n", streamfile->filesize, offset, length); break; } - /* fill the buffer (being here means offset is outside buffer thus empty) */ + /* fill the buffer (offset now is beyond buffer_offset) */ streamfile->buffer_offset = offset; streamfile->validsize = streamfile->inner_sf->read(streamfile->inner_sf, streamfile->buffer, streamfile->buffer_offset, streamfile->buffersize); diff --git a/src/vgmstream.c b/src/vgmstream.c index 139829ae..89e8558f 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -32,7 +32,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ngc_dsp_std, init_vgmstream_ngc_dsp_std_le, init_vgmstream_ngc_mdsp_std, - init_vgmstream_ngc_dsp_csmp, + init_vgmstream_csmp, + init_vgmstream_rfrm, init_vgmstream_cstr, init_vgmstream_gcsw, init_vgmstream_ps2_ads, @@ -41,16 +42,14 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_cdxa, init_vgmstream_ps2_rxws, init_vgmstream_ps2_rxw, - init_vgmstream_ps2_int, init_vgmstream_ngc_dsp_stm, init_vgmstream_ps2_exst, init_vgmstream_ps2_svag, - init_vgmstream_ps2_mib, + init_vgmstream_mib_mih, init_vgmstream_ngc_mpdsp, init_vgmstream_ps2_mic, init_vgmstream_ngc_dsp_std_int, - init_vgmstream_raw, - init_vgmstream_ps2_vag, + init_vgmstream_vag, init_vgmstream_psx_gms, init_vgmstream_ps2_ild, init_vgmstream_ps2_pnb, @@ -62,9 +61,9 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_genh, #ifdef VGM_USE_VORBIS init_vgmstream_ogg_vorbis, - init_vgmstream_sli_ogg, - init_vgmstream_sfl, #endif + init_vgmstream_sli_ogg, + init_vgmstream_sfl_ogg, #if 0 init_vgmstream_mp4_aac, #endif @@ -294,7 +293,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_strlr, init_vgmstream_lsf_n1nj4n, init_vgmstream_vawx, - init_vgmstream_pc_snds, init_vgmstream_ps2_wmus, init_vgmstream_hyperscan_kvag, init_vgmstream_ios_psnd, @@ -302,7 +300,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_pc_adp_otns, init_vgmstream_eb_sfx, init_vgmstream_eb_sf0, - init_vgmstream_ps3_klbs, init_vgmstream_ps2_mtaf, init_vgmstream_tun, init_vgmstream_wpd, @@ -426,9 +423,14 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = { init_vgmstream_nus3bank, init_vgmstream_scd_sscf, - init_vgmstream_txth, /* should go at the end (lower priority) */ + /* lowest priority metas (TXTH should go before raw formats) */ + init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ + init_vgmstream_ps2_int, /* .int raw PS-ADPCM */ + init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */ + init_vgmstream_pc_snds, /* .snds PC, after ps_headerless */ + init_vgmstream_raw, /* .raw PCM */ #ifdef VGM_USE_FFMPEG - init_vgmstream_ffmpeg, /* should go at the end (lowest priority) */ + init_vgmstream_ffmpeg, /* may play anything incorrectly, since FFmpeg doesn't check extensions */ #endif }; @@ -473,23 +475,7 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) { } /* test if candidate for dual stereo */ - if (vgmstream->channels == 1 && ( - (vgmstream->meta_type == meta_DSP_STD) || - (vgmstream->meta_type == meta_PS2_VAGp) || - (vgmstream->meta_type == meta_GENH) || - (vgmstream->meta_type == meta_TXTH) || - (vgmstream->meta_type == meta_KRAW) || - (vgmstream->meta_type == meta_PS2_MIB) || - (vgmstream->meta_type == meta_NGC_LPS) || - (vgmstream->meta_type == meta_DSP_YGO) || - (vgmstream->meta_type == meta_DSP_AGSC) || - (vgmstream->meta_type == meta_PS2_SMPL) || - (vgmstream->meta_type == meta_NGCA) || - (vgmstream->meta_type == meta_NUB_VAG) || - (vgmstream->meta_type == meta_SPT_SPD) || - (vgmstream->meta_type == meta_EB_SFX) || - (vgmstream->meta_type == meta_CWAV) - )) { + if (vgmstream->channels == 1 && vgmstream->allow_dual_stereo == 1) { try_dual_file_stereo(vgmstream, streamFile, init_vgmstream_functions[i]); } @@ -955,7 +941,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre render_vgmstream_interleave(buffer,sample_count,vgmstream); break; case layout_none: - render_vgmstream_nolayout(buffer,sample_count,vgmstream); + render_vgmstream_flat(buffer,sample_count,vgmstream); break; case layout_blocked_mxch: case layout_blocked_ast: @@ -1069,10 +1055,10 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_PCM16BE: case coding_PCM16_int: case coding_PCM8: - case coding_PCM8_U: case coding_PCM8_int: - case coding_PCM8_SB_int: + case coding_PCM8_U: case coding_PCM8_U_int: + case coding_PCM8_SB: case coding_ULAW: case coding_ULAW_int: case coding_ALAW: @@ -1253,10 +1239,10 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_PCM16_int: return 0x02; case coding_PCM8: - case coding_PCM8_U: case coding_PCM8_int: - case coding_PCM8_SB_int: + case coding_PCM8_U: case coding_PCM8_U_int: + case coding_PCM8_SB: case coding_ULAW: case coding_ULAW_int: case coding_ALAW: @@ -1406,455 +1392,397 @@ int get_vgmstream_shortframe_size(VGMSTREAM * vgmstream) { /* Decode samples into the buffer. Assume that we have written samples_written into the * buffer already, and we have samples_to_do consecutive samples ahead of us. */ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to_do, sample * buffer) { - int chan; + int ch; switch (vgmstream->coding_type) { case coding_CRI_ADX: - for (chan=0;chanchannels;chan++) { - decode_adx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_adx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size); } break; case coding_CRI_ADX_exp: - for (chan=0;chanchannels;chan++) { - decode_adx_exp(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_adx_exp(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size); } break; case coding_CRI_ADX_fixed: - for (chan=0;chanchannels;chan++) { - decode_adx_fixed(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_adx_fixed(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size); } break; case coding_CRI_ADX_enc_8: case coding_CRI_ADX_enc_9: - for (chan=0;chanchannels;chan++) { - decode_adx_enc(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_adx_enc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size); } break; case coding_NGC_DSP: - for (chan=0;chanchannels;chan++) { - decode_ngc_dsp(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_dsp(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_NGC_DSP_subint: - for (chan=0;chanchannels;chan++) { - decode_ngc_dsp_subint(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, - chan, vgmstream->interleave_block_size); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_dsp_subint(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, + ch, vgmstream->interleave_block_size); } break; + case coding_PCM16LE: - for (chan=0;chanchannels;chan++) { - decode_pcm16LE(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm16le(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_PCM16BE: - for (chan=0;chanchannels;chan++) { - decode_pcm16BE(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm16be(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_PCM16_int: - for (chan=0;chanchannels;chan++) { - decode_pcm16_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm16_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->codec_endian); } break; case coding_PCM8: - for (chan=0;chanchannels;chan++) { - decode_pcm8(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); - } - break; - case coding_PCM8_U: - for (chan=0;chanchannels;chan++) { - decode_pcm8_unsigned(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_PCM8_int: - for (chan=0;chanchannels;chan++) { - decode_pcm8_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; - case coding_PCM8_SB_int: - for (chan=0;chanchannels;chan++) { - decode_pcm8_sb_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + case coding_PCM8_U: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_unsigned(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_PCM8_U_int: - for (chan=0;chanchannels;chan++) { - decode_pcm8_unsigned_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_unsigned_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; + case coding_PCM8_SB: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcm8_sb(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); + } + break; + case coding_ULAW: - for (chan=0;chanchannels;chan++) { - decode_ulaw(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ulaw(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_ULAW_int: - for (chan=0;chanchannels;chan++) { - decode_ulaw_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ulaw_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_ALAW: - for (chan=0;chanchannels;chan++) { - decode_alaw(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_alaw(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_PCMFLOAT: - for (chan=0;chanchannels;chan++) { - decode_pcmfloat(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_pcmfloat(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->codec_endian); } break; case coding_NDS_IMA: - for (chan=0;chanchannels;chan++) { - decode_nds_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_nds_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_DAT4_IMA: - for (chan=0;chanchannels;chan++) { - decode_dat4_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_dat4_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_XBOX_IMA: - for (chan=0;chanchannels;chan++) { - decode_xbox_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xbox_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_XBOX_IMA_mch: - for (chan=0;chanchannels;chan++) { - decode_xbox_ima_mch(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xbox_ima_mch(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_XBOX_IMA_int: - for (chan=0;chanchannels;chan++) { - decode_xbox_ima_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xbox_ima_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_MS_IMA: - for (chan=0;chanchannels;chan++) { - decode_ms_ima(vgmstream,&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ms_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_RAD_IMA: - for (chan=0;chanchannels;chan++) { - decode_rad_ima(vgmstream,&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_rad_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_RAD_IMA_mono: - for (chan=0;chanchannels;chan++) { - decode_rad_ima_mono(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_rad_ima_mono(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_NGC_DTK: - for (chan=0;chanchannels;chan++) { - decode_ngc_dtk(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_dtk(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_G721: - for (chan=0;chanchannels;chan++) { - decode_g721(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_g721(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_NGC_AFC: - for (chan=0;chanchannels;chan++) { - decode_ngc_afc(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ngc_afc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_PSX: - for (chan=0;chanchannels;chan++) { - decode_psx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, 0); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 0); } break; case coding_PSX_badflags: - for (chan=0;chanchannels;chan++) { - decode_psx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, 1); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 1); } break; case coding_PSX_cfg: - for (chan=0;chanchannels;chan++) { - decode_psx_configurable(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, vgmstream->interleave_block_size); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_psx_configurable(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, vgmstream->interleave_block_size); } break; case coding_HEVAG: - for (chan=0;chanchannels;chan++) { - decode_hevag(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_hevag(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_XA: - for (chan=0;chanchannels;chan++) { - decode_xa(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_EA_XA: - for (chan=0;chanchannels;chan++) { - decode_ea_xa(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_EA_XA_int: - for (chan=0;chanchannels;chan++) { - decode_ea_xa_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xa_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_EA_XA_V2: - for (chan=0;chanchannels;chan++) { - decode_ea_xa_v2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xa_v2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_MAXIS_XA: - for (chan=0;chanchannels;chan++) { - decode_maxis_xa(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_maxis_xa(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_EA_XAS: - for (chan=0;chanchannels;chan++) { - decode_ea_xas(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_xas(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; #ifdef VGM_USE_VORBIS case coding_OGG_VORBIS: - decode_ogg_vorbis(vgmstream->codec_data, - buffer+samples_written*vgmstream->channels,samples_to_do, - vgmstream->channels); + decode_ogg_vorbis(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); break; case coding_VORBIS_custom: - decode_vorbis_custom(vgmstream, - buffer+samples_written*vgmstream->channels,samples_to_do, - vgmstream->channels); + decode_vorbis_custom(vgmstream, buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); break; #endif case coding_CRI_HCA: - decode_hca(vgmstream->codec_data, - buffer+samples_written*vgmstream->channels,samples_to_do, - vgmstream->channels); + decode_hca(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); break; #ifdef VGM_USE_FFMPEG case coding_FFmpeg: decode_ffmpeg(vgmstream, - buffer+samples_written*vgmstream->channels, - samples_to_do, - vgmstream->channels); + buffer+samples_written*vgmstream->channels,samples_to_do,vgmstream->channels); break; #endif #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) case coding_MP4_AAC: - decode_mp4_aac(vgmstream->codec_data, - buffer+samples_written*vgmstream->channels,samples_to_do, - vgmstream->channels); + decode_mp4_aac(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); break; #endif case coding_SDX2: - for (chan=0;chanchannels;chan++) { - decode_sdx2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_sdx2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_SDX2_int: - for (chan=0;chanchannels;chan++) { - decode_sdx2_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_sdx2_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_CBD2: - for (chan=0;chanchannels;chan++) { - decode_cbd2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_cbd2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_CBD2_int: - for (chan=0;chanchannels;chan++) { - decode_cbd2_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_cbd2_int(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_IMA: case coding_IMA_int: case coding_DVI_IMA: case coding_DVI_IMA_int: - for (chan=0;chanchannels;chan++) { + for (ch = 0; ch < vgmstream->channels; ch++) { int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_IMA) || (vgmstream->channels > 1 && vgmstream->coding_type == coding_DVI_IMA); int is_high_first = vgmstream->coding_type == coding_DVI_IMA || vgmstream->coding_type == coding_DVI_IMA_int; - decode_standard_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, chan, is_stereo, is_high_first); + decode_standard_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, + is_stereo, is_high_first); } break; case coding_3DS_IMA: - for (chan=0;chanchannels;chan++) { - decode_3ds_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_3ds_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_WV6_IMA: - for (chan=0;chanchannels;chan++) { - decode_wv6_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_wv6_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_APPLE_IMA4: - for (chan=0;chanchannels;chan++) { - decode_apple_ima4(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_apple_ima4(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_SNDS_IMA: - for (chan=0;chanchannels;chan++) { - decode_snds_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_snds_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_OTNS_IMA: - for (chan=0;chanchannels;chan++) { - decode_otns_ima(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_otns_ima(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_FSB_IMA: - for (chan=0;chanchannels;chan++) { - decode_fsb_ima(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_fsb_ima(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_WWISE_IMA: - for (chan=0;chanchannels;chan++) { - decode_wwise_ima(vgmstream,&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_wwise_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_REF_IMA: - for (chan=0;chanchannels;chan++) { - decode_ref_ima(vgmstream,&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ref_ima(vgmstream,&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_AWC_IMA: - for (chan=0;chanchannels;chan++) { - decode_awc_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_awc_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_UBI_IMA: - for (chan=0;chanchannels;chan++) { - decode_ubi_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ubi_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_H4M_IMA: - for (chan=0;chanchannels;chan++) { - uint16_t frame_format = (uint16_t)((vgmstream->codec_version >> 8) & 0xFFFF); + for (ch = 0; ch < vgmstream->channels; ch++) { + uint16_t frame_format = (uint16_t)((vgmstream->codec_config >> 8) & 0xFFFF); - decode_h4m_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, chan, frame_format); + decode_h4m_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, + frame_format); } break; case coding_WS: - for (chan=0;chanchannels;chan++) { - decode_ws(vgmstream,chan,buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ws(vgmstream,ch,buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; @@ -1864,194 +1792,159 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to case coding_MPEG_layer1: case coding_MPEG_layer2: case coding_MPEG_layer3: - decode_mpeg( - vgmstream, - buffer+samples_written*vgmstream->channels, - samples_to_do, - vgmstream->channels); + decode_mpeg(vgmstream,buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); break; #endif #ifdef VGM_USE_G7221 case coding_G7221: case coding_G7221C: - for (chan=0;chanchannels;chan++) { - decode_g7221(vgmstream, - buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, - samples_to_do, - chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_g7221(vgmstream, buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,samples_to_do, ch); } break; #endif #ifdef VGM_USE_G719 case coding_G719: - for (chan=0;chanchannels;chan++) { - decode_g719(vgmstream, - buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, - samples_to_do, - chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_g719(vgmstream, buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,samples_to_do, ch); } break; #endif #ifdef VGM_USE_MAIATRAC3PLUS case coding_AT3plus: - for (chan=0;chanchannels;chan++) { - decode_at3plus(vgmstream, - buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, - samples_to_do, - chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_at3plus(vgmstream, buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,samples_to_do, ch); } break; #endif #ifdef VGM_USE_ATRAC9 case coding_ATRAC9: - decode_atrac9(vgmstream, - buffer+samples_written*vgmstream->channels, - samples_to_do, - vgmstream->channels); + decode_atrac9(vgmstream, buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); break; #endif #ifdef VGM_USE_CELT case coding_CELT_FSB: - decode_celt_fsb(vgmstream, - buffer+samples_written*vgmstream->channels, - samples_to_do, - vgmstream->channels); + decode_celt_fsb(vgmstream, buffer+samples_written*vgmstream->channels, + samples_to_do,vgmstream->channels); break; #endif case coding_ACM: - decode_acm(vgmstream->codec_data, - buffer+samples_written*vgmstream->channels, + decode_acm(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, samples_to_do, vgmstream->channels); break; case coding_NWA: decode_nwa(((nwa_codec_data*)vgmstream->codec_data)->nwa, - buffer+samples_written*vgmstream->channels, - samples_to_do - ); + buffer+samples_written*vgmstream->channels, samples_to_do); break; case coding_MSADPCM: if (vgmstream->channels == 2) { decode_msadpcm_stereo(vgmstream,buffer+samples_written*vgmstream->channels, - vgmstream->samples_into_block, - samples_to_do); + vgmstream->samples_into_block,samples_to_do); } else if (vgmstream->channels == 1) { decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do,0); + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 0); } break; case coding_MSADPCM_ck: - for (chan=0;chanchannels;chan++) { - decode_msadpcm_ck(vgmstream,buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_msadpcm_ck(vgmstream,buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch); } break; case coding_AICA: case coding_AICA_int: - for (chan=0;chanchannels;chan++) { + for (ch = 0; ch < vgmstream->channels; ch++) { int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_AICA); - decode_aica(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, chan, is_stereo); + decode_aica(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch, + is_stereo); } break; case coding_YAMAHA: - for (chan=0;chanchannels;chan++) { - decode_yamaha(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_yamaha(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch); } break; case coding_YAMAHA_NXAP: - for (chan=0;chanchannels;chan++) { - decode_yamaha_nxap(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_yamaha_nxap(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_NDS_PROCYON: - for (chan=0;chanchannels;chan++) { - decode_nds_procyon(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_nds_procyon(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_L5_555: - for (chan=0;chanchannels;chan++) { - decode_l5_555(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_l5_555(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } - break; case coding_SASSC: - for (chan=0;chanchannels;chan++) { - decode_SASSC(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_SASSC(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_LSF: - for (chan=0;chanchannels;chan++) { - decode_lsf(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_lsf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_MTAF: - for (chan=0;chanchannels;chan++) { - decode_mtaf(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, - chan, vgmstream->channels); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mtaf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); } break; case coding_MTA2: - for (chan=0;chanchannels;chan++) { - decode_mta2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, - chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mta2(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); } break; case coding_MC3: - for (chan=0;chanchannels;chan++) { - decode_mc3(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, - chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_mc3(vgmstream, &vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); } break; case coding_FADPCM: - for (chan=0;chanchannels;chan++) { - decode_fadpcm(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_fadpcm(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_ASF: - for (chan=0;chanchannels;chan++) { - decode_asf(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_asf(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do); } break; case coding_XMD: - for (chan=0;chanchannels;chan++) { - decode_xmd(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels,vgmstream->samples_into_block, - samples_to_do, vgmstream->interleave_block_size); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_xmd(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels,vgmstream->samples_into_block,samples_to_do, + vgmstream->interleave_block_size); } break; case coding_EA_MT: - for (chan=0;chanchannels;chan++) { - decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+chan, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, - chan); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); } break; default: @@ -2263,8 +2156,14 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { switch (vgmstream->coding_type) { #ifdef VGM_USE_FFMPEG case coding_FFmpeg: { - ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; - if (vgmstream->codec_data) { + ffmpeg_codec_data *data = (ffmpeg_codec_data *)vgmstream->codec_data; + if (!data && vgmstream->layout_data) { + layered_layout_data* layout_data = vgmstream->layout_data; + if (layout_data->layers[0]->coding_type == coding_FFmpeg) + data = layout_data->layers[0]->codec_data; + } + + if (data) { if (data->codec && data->codec->long_name) { snprintf(temp,TEMPSIZE,"%s",data->codec->long_name); } else if (data->codec && data->codec->name) { @@ -2304,7 +2203,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { "\n"); concatn(length,desc,temp); - if (vgmstream->layout_type == layout_interleave) { + if (vgmstream->layout_type == layout_interleave && vgmstream->channels > 1) { snprintf(temp,TEMPSIZE, "interleave: %#x bytes\n", (int32_t)vgmstream->interleave_block_size); @@ -2318,7 +2217,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { } } - /* codecs with blocks + headers (there are more, this is a start) */ + /* codecs with configurable frame size */ if (vgmstream->layout_type == layout_none && vgmstream->interleave_block_size > 0) { switch (vgmstream->coding_type) { case coding_MSADPCM: @@ -2327,8 +2226,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case coding_MC3: case coding_WWISE_IMA: case coding_REF_IMA: + case coding_PSX_cfg: snprintf(temp,TEMPSIZE, - "block size: %#x bytes\n", + "frame size: %#x bytes\n", (int32_t)vgmstream->interleave_block_size); concatn(length,desc,temp); break; @@ -2388,8 +2288,8 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea {"left","right"}, {"Left","Right"}, {".V0",".V1"}, /* Homura (PS2) */ - {".L",".R"}, /* Crash Nitro Racing (PS2) */ - {"_0","_1"}, //unneeded? + {".L",".R"}, /* Crash Nitro Racing (PS2), Gradius V (PS2) */ + {"_0","_1"}, //fake for Homura/unneeded? }; char new_filename[PATH_LIMIT]; char * ext; @@ -2690,7 +2590,7 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) { * Should be called in metas before returning the VGMSTREAM. */ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset) { - STREAMFILE * file; + STREAMFILE * file = NULL; char filename[PATH_LIMIT]; int ch; int use_streamfile_per_channel = 0; @@ -2759,6 +2659,9 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s } } + /* init first block for blocked layout (if not blocked this will do nothing) */ + block_update(start_offset, vgmstream); + /* EA-MT decoder is a bit finicky and needs this when channel offsets change */ if (vgmstream->coding_type == coding_EA_MT) { flush_ea_mt(vgmstream); diff --git a/src/vgmstream.h b/src/vgmstream.h index 0ecf8e6e..8f179508 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -10,19 +10,10 @@ enum { STREAM_NAME_SIZE = 255 }; /* reasonable max */ #include "streamfile.h" -/* Due mostly to licensing issues, Vorbis, MPEG, G.722.1, etc decoding is - * done by external libraries. - * If someone wants to do a standalone build, they can do it by simply - * removing these defines (and the references to the libraries in the Makefile) */ -#ifndef VGM_DISABLE_VORBIS -#define VGM_USE_VORBIS -#endif - -#ifndef VGM_DISABLE_MPEG -#define VGM_USE_MPEG -#endif - -/* disabled by default, defined on compile-time for builds that support it */ +/* Due mostly to licensing issues, Vorbis, MPEG, G.722.1, etc decoding is done by external libraries. + * Libs are disabled by default, defined on compile-time for builds that support it */ +//#define VGM_USE_VORBIS +//#define VGM_USE_MPEG //#define VGM_USE_G7221 //#define VGM_USE_G719 //#define VGM_USE_MP4V2 @@ -86,7 +77,7 @@ typedef enum { coding_PCM8_int, /* 8-bit PCM with sample-level interleave (for blocks) */ coding_PCM8_U, /* 8-bit PCM, unsigned (0x80 = 0) */ coding_PCM8_U_int, /* 8-bit PCM, unsigned (0x80 = 0) with sample-level interleave (for blocks) */ - coding_PCM8_SB_int, /* 8-bit PCM, sign bit (others are 2's complement) with sample-level interleave (for blocks) */ + coding_PCM8_SB, /* 8-bit PCM, sign bit (others are 2's complement) */ coding_ULAW, /* 8-bit u-Law (non-linear PCM) */ coding_ULAW_int, /* 8-bit u-Law (non-linear PCM) with sample-level interleave (for blocks) */ @@ -277,13 +268,14 @@ typedef enum { /* The meta type specifies how we know what we know about the file. * We may know because of a header we read, some of it may have been guessed from filenames, etc. */ typedef enum { - /* DSP-specific */ + meta_DSP_STD, /* Nintendo standard GC ADPCM (DSP) header */ - meta_DSP_CSMP, /* Retro: Metroid Prime 3, Donkey Kong Country Returns */ meta_DSP_CSTR, /* Star Fox Assault "Cstr" */ meta_DSP_RS03, /* Retro: Metroid Prime 2 "RS03" */ meta_DSP_STM, /* Paper Mario 2 STM */ - meta_DSP_AGSC, /* Retro: Metroid Prime 2 title */ + meta_AGSC, /* Retro: Metroid Prime 2 title */ + meta_CSMP, /* Retro: Metroid Prime 3 (Wii), Donkey Kong Country Returns (Wii) */ + meta_RFRM, /* Retro: Donkey Kong Country Tropical Freeze (Wii U) */ meta_DSP_MPDSP, /* Monopoly Party single header stereo */ meta_DSP_JETTERS, /* Bomberman Jetters .dsp */ meta_DSP_MSS, /* Free Radical GC games */ @@ -298,7 +290,6 @@ typedef enum { meta_DSP_YGO, /* Konami: Yu-Gi-Oh! The Falsebound Kingdom (NGC), Hikaru no Go 3 (NGC) */ meta_DSP_SADF, /* Procyon Studio SADF - Xenoblade Chronicles 2 (Switch) */ - /* Nintendo */ meta_STRM, /* Nintendo STRM */ meta_RSTM, /* Nintendo RSTM (Revolution Stream, similar to STRM) */ meta_AFC, /* AFC */ @@ -340,18 +331,16 @@ typedef enum { meta_PS2_RAW, /* RAW Interleaved Format */ meta_PS2_EXST, /* Shadow of Colossus EXST */ meta_PS2_SVAG, /* Konami SVAG */ - meta_PS2_MIB, /* MIB File */ + meta_PS_HEADERLESS, /* headerless PS-ADPCM */ meta_PS2_MIB_MIH, /* MIB File + MIH Header*/ meta_PS2_MIC, /* KOEI MIC File */ meta_PS2_VAGi, /* VAGi Interleaved File */ meta_PS2_VAGp, /* VAGp Mono File */ - meta_PS2_VAGm, /* VAGp Mono File */ meta_PS2_pGAV, /* VAGp with Little Endian Header */ meta_PSX_GMS, /* GMS File (used in PS1 & PS2) [no header_id] */ meta_STR_WAV, /* Blitz Games STR+WAV files */ meta_PS2_ILD, /* ILD File */ meta_PS2_PNB, /* PsychoNauts Bgm File */ - meta_PS2_VAGs, /* VAG Stereo from Kingdom Hearts */ meta_VPK, /* VPK Audio File */ meta_PS2_BMDX, /* Beatmania thing */ meta_PS2_IVB, /* Langrisser 3 IVB */ @@ -592,7 +581,6 @@ typedef enum { meta_OTNS_ADP, /* Omikron: The Nomad Soul .adp (PC/DC) */ meta_EB_SFX, /* Excitebots .sfx */ meta_EB_SF0, /* Excitebots .sf0 */ - meta_PS3_KLBS, /* L@VE ONCE (PS3) */ meta_PS2_MTAF, /* Metal Gear Solid 3 MTAF */ meta_PS2_VAG1, /* Metal Gear Solid 3 VAG1 */ meta_PS2_VAG2, /* Metal Gear Solid 3 VAG2 */ @@ -769,10 +757,11 @@ typedef struct { int num_streams; /* for multi-stream formats (0=not set/one stream, 1=one stream) */ int stream_index; /* selected stream (also 1-based) */ char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */ - size_t stream_size; /* info to properly calculate bitrate */ + size_t stream_size; /* info to properly calculate bitrate in case of subsongs */ uint32_t channel_mask; /* to silence crossfading subsongs/layers */ int channel_mappings_on; /* channel mappings are active */ int channel_mappings[32]; /* swap channel "i" with "[i]" */ + int allow_dual_stereo; /* search for dual stereo (file_L.ext + file_R.ext = single stereo file) */ /* looping */ int loop_flag; /* is this stream looped? */ @@ -790,8 +779,8 @@ typedef struct { /* layout/block state */ size_t full_block_size; /* actual data size of an entire block (ie. may be fixed, include padding/headers, etc) */ - int32_t current_sample; /* number of samples we've passed */ - int32_t samples_into_block; /* number of samples into the current block */ + int32_t current_sample; /* number of samples we've passed (for loop detection) */ + int32_t samples_into_block; /* number of samples into the current block/interleave/segment/etc */ off_t current_block_offset; /* start of this block (offset of block header) */ size_t current_block_size; /* size in usable bytes of the block we're in now (used to calculate num_samples per block) */ size_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */ @@ -812,11 +801,11 @@ typedef struct { /* decoder specific */ int codec_endian; /* little/big endian marker; name is left vague but usually means big endian */ - int codec_version; /* flag for codecs with minor variations */ + int codec_config; /* flags for codecs or layouts with minor variations; meaning is up to the user */ int32_t ws_output_size; /* WS ADPCM: output bytes for this block */ - void * start_vgmstream; /* a copy of the VGMSTREAM as it was at the beginning of the stream (for AAX/AIX/SCD) */ + void * start_vgmstream; /* a copy of the VGMSTREAM as it was at the beginning of the stream (for custom layouts) */ /* Data the codec needs for the whole stream. This is for codecs too * different from vgmstream's structure to be reasonably shoehorned into @@ -824,9 +813,7 @@ typedef struct { * Note also that support must be added for resetting, looping and * closing for every codec that uses this, as it will not be handled. */ void * codec_data; - /* Same, for special layouts. - * Reusing the above pointer causes bugs when it's using special layout + codec - * (vgmstream may try to free/loop/etc codec_data). */ + /* Same, for special layouts. layout_data + codec_data may exist at the same time. */ void * layout_data; } VGMSTREAM; @@ -1032,39 +1019,13 @@ typedef struct { #endif #ifdef VGM_USE_ATRAC9 - -/* custom ATRAC9 modes */ -typedef enum { - ATRAC9_DEFAULT = 0, /* ATRAC9 standard */ - ATRAC9_XVAG, /* Sony XVAG: interleaved subsongs, Vita multichannel interleaves 2ch xN superframes */ - ATRAC9_KMA9, /* Koei Tecmo KMA9: interleaved subsongs */ -} atrac9_custom_t; - +/* ATRAC9 config */ typedef struct { - atrac9_custom_t type; - - int channels; /* to detect weird multichannel */ - uint32_t config_data; /* ATRAC9 config header */ - int encoder_delay; /* initial samples to discard */ - - size_t interleave_skip; /* XVAG */ - size_t subsong_skip; /* XVAG */ + int channels; /* to detect weird multichannel */ + uint32_t config_data; /* ATRAC9 config header */ + int encoder_delay; /* initial samples to discard */ } atrac9_config; - -typedef struct { - uint8_t *data_buffer; - size_t data_buffer_size; - - sample *sample_buffer; - size_t samples_filled; /* number of samples in the buffer */ - size_t samples_used; /* number of samples extracted from the buffer */ - - int samples_to_discard; - - atrac9_config config; - - void *handle; /* decoder handle */ -} atrac9_codec_data; +typedef struct atrac9_codec_data atrac9_codec_data; #endif #ifdef VGM_USE_CELT @@ -1096,7 +1057,6 @@ typedef struct { int segment_count; VGMSTREAM **segments; int current_segment; - int loop_segment; } segmented_layout_data; /* for files made of "horizontal" layers, one per group of channels (using a complete sub-VGMSTREAM) */ @@ -1128,7 +1088,6 @@ typedef struct { typedef enum { FFMPEG_STANDARD, /* default FFmpeg */ FFMPEG_SWITCH_OPUS, /* Opus without Ogg layer */ - FFMPEG_EA_XMA, /* XMA with padding removed and custom streams in SNS blocks */ } ffmpeg_custom_t; /* config for the above modes */ diff --git a/winamp/Makefile b/winamp/Makefile index d1251d92..ba727e96 100644 --- a/winamp/Makefile +++ b/winamp/Makefile @@ -2,38 +2,35 @@ # Winamp plugin # -ifneq ($(TARGET_OS),WIN32) -$(error option must be built with TARGET_OS = WIN32) + +ifneq ($(TARGET_OS),Windows_NT) +$(error option must be built with TARGET_OS = Windows_NT) endif ### main defs OUTPUT_WINAMP = in_vgmstream.dll -CFLAGS += -Wall -O3 -DUSE_ALLOCA -DWIN32 -I../ext_includes $(EXTRA_CFLAGS) +CFLAGS += -Wall -Werror=format-security -Wdeclaration-after-statement -Wvla -O3 -DUSE_ALLOCA -DWIN32 -I../ext_includes $(EXTRA_CFLAGS) LDFLAGS += -L../src -L../ext_libs -lm -lvgmstream $(EXTRA_LDFLAGS) TARGET_EXT_LIBS = ### external libs -ifeq ($(TARGET_OS),WIN32) +ifeq ($(TARGET_OS),Windows_NT) VGM_ENABLE_VORBIS = 1 ifeq ($(VGM_ENABLE_VORBIS),1) - #CFLAGS += -DVGM_USE_VORBIS + CFLAGS += -DVGM_USE_VORBIS LDFLAGS += -lvorbis TARGET_EXT_LIBS += libvorbis.a -else - CFLAGS += -DVGM_DISABLE_VORBIS endif VGM_ENABLE_MPEG = 1 ifeq ($(VGM_ENABLE_MPEG),1) - #CFLAGS += -DVGM_USE_MPEG + CFLAGS += -DVGM_USE_MPEG LDFLAGS += -lmpg123-0 TARGET_EXT_LIBS += libmpg123-0.a -else - CFLAGS += -DVGM_DISABLE_MPEG endif VGM_ENABLE_G7221 = 1 diff --git a/winamp/in_vgmstream.vcproj b/winamp/in_vgmstream.vcproj index 33c37853..b453e102 100644 --- a/winamp/in_vgmstream.vcproj +++ b/winamp/in_vgmstream.vcproj @@ -115,7 +115,7 @@ Disabled ../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories) - WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions) + 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) true EnableFastChecks MultiThreadedDebug @@ -99,7 +99,7 @@ ../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories) - WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions) + 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) MultiThreaded diff --git a/xmplay/Makefile b/xmplay/Makefile index 4253c275..c7115de5 100644 --- a/xmplay/Makefile +++ b/xmplay/Makefile @@ -2,38 +2,35 @@ # XMPlay plugin # -ifneq ($(TARGET_OS),WIN32) -$(error option must be built with TARGET_OS = WIN32) + +ifneq ($(TARGET_OS),Windows_NT) +$(error option must be built with TARGET_OS = Windows_NT) endif ### main defs OUTPUT_XMPLAY = xmp-vgmstream.dll -CFLAGS += -Wall -O3 -DUSE_ALLOCA -DWIN32 -I../ext_includes $(EXTRA_CFLAGS) +CFLAGS += -Wall -Werror=format-security -Wdeclaration-after-statement -Wvla -O3 -DUSE_ALLOCA -DWIN32 -I../ext_includes $(EXTRA_CFLAGS) LDFLAGS += -L../src -L../ext_libs -lm -lvgmstream $(EXTRA_LDFLAGS) TARGET_EXT_LIBS = ### external libs -ifeq ($(TARGET_OS),WIN32) +ifeq ($(TARGET_OS),Windows_NT) VGM_ENABLE_VORBIS = 1 ifeq ($(VGM_ENABLE_VORBIS),1) - #CFLAGS += -DVGM_USE_VORBIS + CFLAGS += -DVGM_USE_VORBIS LDFLAGS += -lvorbis TARGET_EXT_LIBS += libvorbis.a -else - CFLAGS += -DVGM_DISABLE_VORBIS endif VGM_ENABLE_MPEG = 1 ifeq ($(VGM_ENABLE_MPEG),1) - #CFLAGS += -DVGM_USE_MPEG + CFLAGS += -DVGM_USE_MPEG LDFLAGS += -lmpg123-0 TARGET_EXT_LIBS += libmpg123-0.a -else - CFLAGS += -DVGM_DISABLE_MPEG endif VGM_ENABLE_G7221 = 1 diff --git a/xmplay/xmp-vgmstream.vcxproj b/xmplay/xmp-vgmstream.vcxproj index 41fd6020..56bc7e63 100644 --- a/xmplay/xmp-vgmstream.vcxproj +++ b/xmplay/xmp-vgmstream.vcxproj @@ -62,7 +62,7 @@ Disabled ../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories) - WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;VGM_USE_ATRAC9;VGM_USE_CELT;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions) + 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) true EnableFastChecks MultiThreadedDebug @@ -87,7 +87,7 @@ ../ext_includes;$(DependenciesDir)/qaac/mp4v2/include;$(DependenciesDir)/fdk-aac/libSYS/include;$(DependenciesDir)/fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories) - _WIN32_WINNT=0x501;WIN32;VGM_USE_FFMPEG;VGM_USE_G7221;VGM_USE_MP4V2;VGM_USE_FDKAAC;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(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) MultiThreaded Level3 ProgramDatabase diff --git a/xmplay/xmp_vgmstream.c b/xmplay/xmp_vgmstream.c index eb26f6ce..3d0a48e8 100644 --- a/xmplay/xmp_vgmstream.c +++ b/xmplay/xmp_vgmstream.c @@ -331,20 +331,20 @@ char * WINAPI xmplay_GetTags() { void WINAPI xmplay_GetInfoText(char* format, char* length) { if (!format) return; - if (!vgmstream) - return; + if (!vgmstream) + return; - int rate = vgmstream->sample_rate; - int samples = vgmstream->num_samples; - int bps = get_vgmstream_average_bitrate(vgmstream) / 1000; - char* fmt = get_vgmstream_coding_description(vgmstream->coding_type); - - int t = samples / rate; - int tmin = t / 60; - int tsec = t % 60; + int rate = vgmstream->sample_rate; + int samples = vgmstream->num_samples; + int bps = get_vgmstream_average_bitrate(vgmstream) / 1000; + const char* fmt = get_vgmstream_coding_description(vgmstream->coding_type); - sprintf(format, "%s", fmt); - sprintf(length, "%d:%02d - %dKb/s - %dHz", tmin, tsec, bps, rate); + int t = samples / rate; + int tmin = t / 60; + int tsec = t % 60; + + sprintf(format, "%s", fmt); + sprintf(length, "%d:%02d - %dKb/s - %dHz", tmin, tsec, bps, rate); } /* info for the "General" window/tab (buf is ~40K) */