Merge pull request #940 from bnnm/bigfile-etc

- Cleanup and logs
- Add .psb/dyx extensions
- Tweak STREAMFILES to read +2GB files
- Handle +2GB .fsb and .ktsl2asbin [Nioh 2 (PC)]
This commit is contained in:
bnnm 2021-09-05 18:08:36 +02:00 committed by GitHub
commit dfa3779ad4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1240 additions and 1116 deletions

View File

@ -221,6 +221,7 @@ export DEF_CFLAGS LIBS_CFLAGS LIBS_LDFLAGS LIBS_TARGET_EXT_LIBS
############################################################################### ###############################################################################
### internal defs ### internal defs
ifeq ($(TARGET_OS),Windows_NT) ifeq ($(TARGET_OS),Windows_NT)
BIN_FILE = vgmstream-$(VGMSTREAM_VERSION)-win.zip
ZIP_FILES = COPYING ZIP_FILES = COPYING
ZIP_FILES += README.md ZIP_FILES += README.md
ZIP_FILES += cli/test.exe ZIP_FILES += cli/test.exe
@ -231,6 +232,7 @@ ifeq ($(TARGET_OS),Windows_NT)
ZIP_FILES_AO = cli/vgmstream123.exe ZIP_FILES_AO = cli/vgmstream123.exe
ZIP_FILES_AO += $(LIBAO_DLL_PATH)/*.dll ZIP_FILES_AO += $(LIBAO_DLL_PATH)/*.dll
else else
BIN_FILE = vgmstream-$(VGMSTREAM_VERSION)-bin.zip
ZIP_FILES = COPYING ZIP_FILES = COPYING
ZIP_FILES += README.md ZIP_FILES += README.md
ZIP_FILES += cli/vgmstream-cli ZIP_FILES += cli/vgmstream-cli
@ -245,25 +247,28 @@ buildrelease-ex: clean bin-ex
buildfullrelease: clean sourceball bin buildfullrelease: clean sourceball bin
# make a tmp copy of git's index to avoid including dev stuff
sourceball: sourceball:
rm -rf vgmstream-$(VGMSTREAM_VERSION) rm -rf vgmstream-$(VGMSTREAM_VERSION)
git checkout-index -f -a --prefix=vgmstream-$(VGMSTREAM_VERSION)/ git checkout-index -f -a --prefix=vgmstream-$(VGMSTREAM_VERSION)/
# git archive --format zip --output vgmstream-$(VGMSTREAM_VERSION).zip master # echo "#!/bin/sh" > vgmstream-$(VGMSTREAM_VERSION)/version-get.sh
echo "#!/bin/sh" > vgmstream-$(VGMSTREAM_VERSION)/version-get.sh # echo "echo \"$(VGMSTREAM_VERSION)\"" >> vgmstream-$(VGMSTREAM_VERSION)/version-get.sh
echo "echo \"$(VGMSTREAM_VERSION)\"" >> vgmstream-$(VGMSTREAM_VERSION)/version-get.sh tar cvzf "bin/vgmstream-$(VGMSTREAM_VERSION)-src.tar.gz" vgmstream-$(VGMSTREAM_VERSION)/*
tar cvzf "vgmstream-$(VGMSTREAM_VERSION)-src.tar.gz" vgmstream-$(VGMSTREAM_VERSION)/* # git archive --format zip --output bin/vgmstream-$(VGMSTREAM_VERSION)-src.zip master
rm -rf vgmstream-$(VGMSTREAM_VERSION) rm -rf vgmstream-$(VGMSTREAM_VERSION)
bin: vgmstream_cli winamp xmplay bin: vgmstream-cli winamp xmplay
mkdir -p bin mkdir -p bin
zip -FS -j "bin/vgmstream-$(VGMSTREAM_VERSION).zip" $(ZIP_FILES) zip -FS -j "bin/$(BIN_FILE).zip" $(ZIP_FILES)
#separate since vgmstream123 is kinda untested #separate since vgmstream123 is kinda untested
bin-ex: vgmstream_cli winamp xmplay vgmstream123 bin-ex: vgmstream-cli winamp xmplay vgmstream123
mkdir -p bin mkdir -p bin
zip -FS -j "bin/vgmstream-$(VGMSTREAM_VERSION).zip" $(ZIP_FILES) $(ZIP_FILES_AO) zip -FS -j "bin/$(BIN_FILE).zip" $(ZIP_FILES) $(ZIP_FILES_AO)
vgmstream_cli: version vgmstream_cli: vgmstream-cli
vgmstream-cli: version
$(MAKE) -C cli vgmstream_cli $(MAKE) -C cli vgmstream_cli
vgmstream123: version vgmstream123: version
@ -286,4 +291,4 @@ clean:
$(MAKE) -C xmplay clean $(MAKE) -C xmplay clean
$(MAKE) -C ext_libs clean $(MAKE) -C ext_libs clean
.PHONY: clean buildfullrelease buildrelease sourceball bin vgmstream_cli winamp .PHONY: clean buildfullrelease buildrelease sourceball bin vgmstream-cli vgmstream_cli vgmstream123 winamp xmplay version

View File

@ -20,6 +20,8 @@ Latest development is here: https://github.com/vgmstream/vgmstream/
Automated builds with the latest changes: https://vgmstream.org/downloads Automated builds with the latest changes: https://vgmstream.org/downloads
Common releases: https://github.com/vgmstream/vgmstream/releases
Help can be found here: https://www.hcs64.com/ Help can be found here: https://www.hcs64.com/
More technical docs: https://github.com/vgmstream/vgmstream/tree/master/doc More technical docs: https://github.com/vgmstream/vgmstream/tree/master/doc
@ -47,8 +49,11 @@ If the above link fails you may find alt, recent-ish versions here:
https://github.com/bnnm/vgmstream-builds/raw/master/bin/vgmstream-latest-test-u.zip https://github.com/bnnm/vgmstream-builds/raw/master/bin/vgmstream-latest-test-u.zip
You may compile them from source as well. You may compile them from source as well.
For Linux and other O.S., you need to build vgmstream manually (see *vgmstream/doc/BUILD.md* For Linux and other O.S., generally you need to build vgmstream manually (see
in source). *vgmstream/doc/BUILD.md* in source). For a quick build call `/make-build-cmake.sh`
(for Debian/Ubuntu-style distros, installs various deps first so you may prefer to call
commands manually). Links above also distribute a static version of the CLI tool
(kernel v3.2+).
### Needed extra files (for Windows) ### Needed extra files (for Windows)
On Windows support for some codecs (Ogg Vorbis, MPEG audio, etc.) is done with external On Windows support for some codecs (Ogg Vorbis, MPEG audio, etc.) is done with external
@ -685,8 +690,8 @@ foobar2000 has other plugins (with write support) that may be of use:
## Virtual TXTP files ## Virtual TXTP files
Some of vgmstream's plugins allow you to use virtual `.txtp` files, that combined Some of vgmstream's plugins (and CLI) allow you to use virtual `.txtp` files, that
with playlists let you make quick song configs. combined with playlists let you make quick song configs.
Normally you can create a physical .txtp file that points to another file with Normally you can create a physical .txtp file that points to another file with
config, and `.txtp` have a "mini-txtp" mode that configures files with only the config, and `.txtp` have a "mini-txtp" mode that configures files with only the

View File

@ -12,13 +12,13 @@ extern "C" {
typedef struct { typedef struct {
STREAMFILE sf; STREAMFILE sf;
VFSFile *vfsFile; VFSFile *vfsFile;
off_t offset; offv_t offset;
char name[32768]; char name[32768];
} VFS_STREAMFILE; } VFS_STREAMFILE;
static STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path); static STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path);
static size_t read_vfs(VFS_STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length) { static size_t read_vfs(VFS_STREAMFILE *streamfile, uint8_t *dest, offv_t offset, size_t length) {
size_t bytes_read; size_t bytes_read;
if (/*!streamfile->vfsFile ||*/ !dest || length <= 0 || offset < 0) if (/*!streamfile->vfsFile ||*/ !dest || length <= 0 || offset < 0)
@ -73,9 +73,9 @@ STREAMFILE *open_vfs_by_VFSFILE(VFSFile *file, const char *path) {
// success, set our pointers // success, set our pointers
memset(streamfile, 0, sizeof(VFS_STREAMFILE)); memset(streamfile, 0, sizeof(VFS_STREAMFILE));
streamfile->sf.read = (size_t (*)(STREAMFILE *, uint8_t *, off_t, size_t))read_vfs; streamfile->sf.read = (size_t (*)(STREAMFILE *, uint8_t *, offv_t, size_t))read_vfs;
streamfile->sf.get_size = (size_t (*)(STREAMFILE *))get_size_vfs; streamfile->sf.get_size = (size_t (*)(STREAMFILE *))get_size_vfs;
streamfile->sf.get_offset = (off_t (*)(STREAMFILE *))get_offset_vfs; streamfile->sf.get_offset = (offv_t (*)(STREAMFILE *))get_offset_vfs;
streamfile->sf.get_name = (void (*)(STREAMFILE *, char *, size_t))get_name_vfs; streamfile->sf.get_name = (void (*)(STREAMFILE *, char *, size_t))get_name_vfs;
streamfile->sf.open = (STREAMFILE *(*)(STREAMFILE *, const char *, size_t))open_vfs_impl; streamfile->sf.open = (STREAMFILE *(*)(STREAMFILE *, const char *, size_t))open_vfs_impl;
streamfile->sf.close = (void (*)(STREAMFILE *))close_vfs; streamfile->sf.close = (void (*)(STREAMFILE *))close_vfs;

View File

@ -285,7 +285,7 @@ sudo apt-get install gcc g++ make git
sudo apt-get install autoconf automake libtool sudo apt-get install autoconf automake libtool
# vgmstream dependencies # vgmstream dependencies
sudo apt-get install libmpg123-dev libvorbis-dev libspeex-dev sudo apt-get install libmpg123-dev libvorbis-dev libspeex-dev
#sudo apt-get install libavformat-dev libavcodec-dev libavutil-dev libswresample-dev sudo apt-get install libavformat-dev libavcodec-dev libavutil-dev libswresample-dev
# Audacious player and dependencies # Audacious player and dependencies
sudo apt-get install audacious sudo apt-get install audacious
sudo apt-get install audacious-dev libglib2.0-dev libgtk2.0-dev libpango1.0-dev sudo apt-get install audacious-dev libglib2.0-dev libgtk2.0-dev libpango1.0-dev

View File

@ -10,7 +10,8 @@
#include <shared.h> #include <shared.h>
extern "C" { extern "C" {
#include "../src/vgmstream.h" #include "../src/streamfile.h"
//#include "../src/vgmstream.h"
#include "../src/util.h" #include "../src/util.h"
} }
#include "foo_vgmstream.h" #include "foo_vgmstream.h"
@ -18,184 +19,183 @@ extern "C" {
/* a STREAMFILE that operates via foobar's file service using a buffer */ /* a STREAMFILE that operates via foobar's file service using a buffer */
typedef struct { typedef struct {
STREAMFILE sf; /* callbacks */ STREAMFILE vt; /* callbacks */
bool m_file_opened; /* if foobar IO service opened the file */ bool m_file_opened; /* if foobar IO service opened the file */
service_ptr_t<file> m_file; /* foobar IO service */ service_ptr_t<file> m_file; /* foobar IO service */
abort_callback * p_abort; /* foobar error stuff */ abort_callback * p_abort; /* foobar error stuff */
char * name; /* IO filename */ char* name; /* IO filename */
off_t offset; /* last read offset (info) */ offv_t offset; /* last read offset (info) */
off_t buffer_offset; /* current buffer data start */ offv_t buf_offset; /* current buffer data start */
uint8_t * buffer; /* data buffer */ uint8_t* buf; /* data buffer */
size_t buffersize; /* max buffer size */ size_t buf_size; /* max buffer size */
size_t validsize; /* current buffer size */ size_t valid_size; /* current buffer size */
size_t filesize; /* buffered file size */ size_t file_size; /* buffered file size */
} FOO_STREAMFILE; } FOO_STREAMFILE;
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(const char* const filename, size_t buf_size, abort_callback* p_abort, t_filestats* stats);
static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_file, bool m_file_opened, const char * const filename, size_t buffersize, abort_callback * p_abort); static STREAMFILE* open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_file, bool m_file_opened, const char* const filename, size_t buf_size, abort_callback* p_abort);
static size_t read_foo(FOO_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) { static size_t foo_read(FOO_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) {
size_t length_read_total = 0; size_t read_total = 0;
if (!streamfile || !streamfile->m_file_opened || !dest || length <= 0 || offset < 0) if (!sf || !sf->m_file_opened || !dst || length <= 0 || offset < 0)
return 0; return 0;
/* is the part of the requested length in the buffer? */ /* is the part of the requested length in the buffer? */
if (offset >= streamfile->buffer_offset && offset < streamfile->buffer_offset + streamfile->validsize) { if (offset >= sf->buf_offset && offset < sf->buf_offset + sf->valid_size) {
size_t length_to_read; size_t buf_limit;
off_t offset_into_buffer = offset - streamfile->buffer_offset; int buf_into = (int)(offset - sf->buf_offset);
length_to_read = streamfile->validsize - offset_into_buffer; buf_limit = sf->valid_size - buf_into;
if (length_to_read > length) if (buf_limit > length)
length_to_read = length; buf_limit = length;
memcpy(dest,streamfile->buffer + offset_into_buffer,length_to_read); memcpy(dst, sf->buf + buf_into, buf_limit);
length_read_total += length_to_read; read_total += buf_limit;
length -= length_to_read; length -= buf_limit;
offset += length_to_read; offset += buf_limit;
dest += length_to_read; dst += buf_limit;
} }
/* read the rest of the requested length */ /* read the rest of the requested length */
while (length > 0) { while (length > 0) {
size_t length_to_read; size_t buf_limit;
/* ignore requests at EOF */ /* ignore requests at EOF */
if (offset >= streamfile->filesize) { if (offset >= sf->file_size) {
//offset = streamfile->filesize; /* seems fseek doesn't clamp offset */ //offset = sf->file_size; /* 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); //VGM_ASSERT_ONCE(offset > sf->file_size, "STDIO: reading over file_size 0x%x @ 0x%lx + 0x%x\n", sf->file_size, offset, length);
break; break;
} }
/* position to new offset */ /* position to new offset */
try { try {
streamfile->m_file->seek(offset,*streamfile->p_abort); sf->m_file->seek(offset, *sf->p_abort);
} catch (...) { } catch (...) {
break; /* this shouldn't happen in our code */ break; /* this shouldn't happen in our code */
} }
/* fill the buffer (offset now is beyond buffer_offset) */ /* fill the buffer (offset now is beyond buf_offset) */
try { try {
streamfile->buffer_offset = offset; sf->buf_offset = offset;
streamfile->validsize = streamfile->m_file->read(streamfile->buffer,streamfile->buffersize,*streamfile->p_abort); sf->valid_size = sf->m_file->read(sf->buf, sf->buf_size, *sf->p_abort);
} catch(...) { } catch(...) {
break; /* improbable? */ break; /* improbable? */
} }
/* decide how much must be read this time */ /* decide how much must be read this time */
if (length > streamfile->buffersize) if (length > sf->buf_size)
length_to_read = streamfile->buffersize; buf_limit = sf->buf_size;
else else
length_to_read = length; buf_limit = length;
/* give up on partial reads (EOF) */ /* give up on partial reads (EOF) */
if (streamfile->validsize < length_to_read) { if (sf->valid_size < buf_limit) {
memcpy(dest,streamfile->buffer,streamfile->validsize); memcpy(dst, sf->buf, sf->valid_size);
offset += streamfile->validsize; offset += sf->valid_size;
length_read_total += streamfile->validsize; read_total += sf->valid_size;
break; break;
} }
/* use the new buffer */ /* use the new buffer */
memcpy(dest,streamfile->buffer,length_to_read); memcpy(dst, sf->buf, buf_limit);
offset += length_to_read; offset += buf_limit;
length_read_total += length_to_read; read_total += buf_limit;
length -= length_to_read; length -= buf_limit;
dest += length_to_read; dst += buf_limit;
} }
streamfile->offset = offset; /* last fread offset */ sf->offset = offset; /* last fread offset */
return length_read_total; return read_total;
} }
static size_t get_size_foo(FOO_STREAMFILE * streamfile) { static size_t foo_get_size(FOO_STREAMFILE* sf) {
return streamfile->filesize; return sf->file_size;
} }
static off_t get_offset_foo(FOO_STREAMFILE *streamfile) { static offv_t foo_get_offset(FOO_STREAMFILE* sf) {
return streamfile->offset; return sf->offset;
} }
static void get_name_foo(FOO_STREAMFILE *streamfile,char *buffer,size_t length) { static void foo_get_name(FOO_STREAMFILE* sf, char* name, size_t name_size) {
/* Most crap only cares about the filename itself */ /* Most crap only cares about the filename itself */
size_t ourlen = strlen(streamfile->name); size_t ourlen = strlen(sf->name);
if (ourlen > length) { if (ourlen > name_size) {
if (length) strcpy(buffer, streamfile->name + ourlen - length + 1); if (name_size) strcpy(name, sf->name + ourlen - name_size + 1);
} else { }
strcpy(buffer, streamfile->name); else {
} strcpy(name, sf->name);
}
} }
static void close_foo(FOO_STREAMFILE * streamfile) { static void foo_close(FOO_STREAMFILE* sf) {
streamfile->m_file.release(); //release alloc'ed ptr sf->m_file.release(); //release alloc'ed ptr
free(streamfile->name); free(sf->name);
free(streamfile->buffer); free(sf->buf);
free(streamfile); free(sf);
} }
static STREAMFILE *open_foo(FOO_STREAMFILE *streamFile,const char * const filename,size_t buffersize) { static STREAMFILE* foo_open(FOO_STREAMFILE* sf, const char* const filename,size_t buf_size) {
service_ptr_t<file> m_file; service_ptr_t<file> m_file;
STREAMFILE *newstreamFile;
if (!filename) if (!filename)
return NULL; return NULL;
// if same name, duplicate the file pointer we already have open // if same name, duplicate the file pointer we already have open
if (streamFile->m_file_opened && !strcmp(streamFile->name,filename)) { if (sf->m_file_opened && !strcmp(sf->name,filename)) {
m_file = streamFile->m_file; //copy? m_file = sf->m_file; //copy?
{ {
newstreamFile = open_foo_streamfile_buffer_by_file(m_file, streamFile->m_file_opened, filename, buffersize, streamFile->p_abort); STREAMFILE* new_sf = open_foo_streamfile_buffer_by_file(m_file, sf->m_file_opened, filename, buf_size, sf->p_abort);
if (newstreamFile) { if (new_sf) {
return newstreamFile; return new_sf;
} }
// failure, close it and try the default path (which will probably fail a second time) // failure, close it and try the default path (which will probably fail a second time)
} }
} }
// a normal open, open a new file // a normal open, open a new file
return open_foo_streamfile_buffer(filename,buffersize,streamFile->p_abort,NULL); return open_foo_streamfile_buffer(filename,buf_size,sf->p_abort,NULL);
} }
static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_file, bool m_file_opened, const char * const filename, size_t buffersize, abort_callback * p_abort) { static STREAMFILE* open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_file, bool m_file_opened, const char* const filename, size_t buf_size, abort_callback* p_abort) {
uint8_t * buffer; uint8_t* buf;
FOO_STREAMFILE * streamfile; FOO_STREAMFILE* this_sf;
buffer = (uint8_t *) calloc(buffersize,1); buf = (uint8_t*) calloc(buf_size, sizeof(uint8_t));
if (!buffer) goto fail; if (!buf) goto fail;
streamfile = (FOO_STREAMFILE *) calloc(1,sizeof(FOO_STREAMFILE)); this_sf = (FOO_STREAMFILE*) calloc(1, sizeof(FOO_STREAMFILE));
if (!streamfile) goto fail; if (!this_sf) goto fail;
streamfile->sf.read = (size_t (__cdecl *)(_STREAMFILE *,uint8_t *,off_t,size_t)) read_foo; this_sf->vt.read = (size_t (__cdecl *)(_STREAMFILE*, uint8_t*, offv_t, size_t)) foo_read;
streamfile->sf.get_size = (size_t (__cdecl *)(_STREAMFILE *)) get_size_foo; this_sf->vt.get_size = (size_t (__cdecl *)(_STREAMFILE*)) foo_get_size;
streamfile->sf.get_offset = (off_t (__cdecl *)(_STREAMFILE *)) get_offset_foo; this_sf->vt.get_offset = (offv_t (__cdecl *)(_STREAMFILE*)) foo_get_offset;
streamfile->sf.get_name = (void (__cdecl *)(_STREAMFILE *,char *,size_t)) get_name_foo; this_sf->vt.get_name = (void (__cdecl *)(_STREAMFILE*, char*, size_t)) foo_get_name;
streamfile->sf.open = (_STREAMFILE *(__cdecl *)(_STREAMFILE *,const char *const ,size_t)) open_foo; this_sf->vt.open = (_STREAMFILE* (__cdecl *)(_STREAMFILE* ,const char* const, size_t)) foo_open;
streamfile->sf.close = (void (__cdecl *)(_STREAMFILE *)) close_foo; this_sf->vt.close = (void (__cdecl *)(_STREAMFILE* )) foo_close;
streamfile->m_file_opened = m_file_opened; this_sf->m_file_opened = m_file_opened;
streamfile->m_file = m_file; this_sf->m_file = m_file;
streamfile->p_abort = p_abort; this_sf->p_abort = p_abort;
streamfile->buffersize = buffersize; this_sf->buf_size = buf_size;
streamfile->buffer = buffer; this_sf->buf = buf;
streamfile->name = strdup(filename); this_sf->name = strdup(filename);
if (!streamfile->name) goto fail; if (!this_sf->name) goto fail;
/* cache filesize */ /* cache file_size */
if (streamfile->m_file_opened) if (this_sf->m_file_opened)
streamfile->filesize = streamfile->m_file->get_size(*streamfile->p_abort); this_sf->file_size = this_sf->m_file->get_size(*this_sf->p_abort);
else else
streamfile->filesize = 0; this_sf->file_size = 0;
return &streamfile->sf; return &this_sf->vt;
fail: fail:
free(buffer); free(buf);
free(streamfile); free(this_sf);
return NULL; return NULL;
} }
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(const char* const filename, size_t buf_size, abort_callback* p_abort, t_filestats* stats) {
STREAMFILE* sf = NULL; STREAMFILE* sf = NULL;
service_ptr_t<file> infile; service_ptr_t<file> infile;
bool infile_exists; bool infile_exists;
@ -213,7 +213,7 @@ static STREAMFILE* open_foo_streamfile_buffer(const char* const filename, size_t
if(stats) *stats = infile->get_stats(*p_abort); if(stats) *stats = infile->get_stats(*p_abort);
} }
sf = open_foo_streamfile_buffer_by_file(infile, infile_exists, filename, buffersize, p_abort); sf = open_foo_streamfile_buffer_by_file(infile, infile_exists, filename, buf_size, p_abort);
if (!sf) { if (!sf) {
//m_file.release(); //refcounted and cleaned after it goes out of scope //m_file.release(); //refcounted and cleaned after it goes out of scope
} }

View File

@ -3,6 +3,10 @@
#define SAMPLE_BUFFER_SIZE 1024 #define SAMPLE_BUFFER_SIZE 1024
extern "C" {
#include "../src/vgmstream.h"
}
class input_vgmstream : public input_stubs { class input_vgmstream : public input_stubs {
public: public:
@ -69,18 +73,18 @@ class input_vgmstream : public input_stubs {
//bool exts_unknown_on; //bool exts_unknown_on;
/* helpers */ /* helpers */
VGMSTREAM * init_vgmstream_foo(t_uint32 p_subsong, const char * const filename, abort_callback & p_abort); VGMSTREAM* init_vgmstream_foo(t_uint32 p_subsong, const char* const filename, abort_callback& p_abort);
void setup_vgmstream(abort_callback & p_abort); void setup_vgmstream(abort_callback& p_abort);
void load_settings(); void load_settings();
void get_subsong_info(t_uint32 p_subsong, pfc::string_base & title, int *length_in_ms, int *total_samples, int *loop_flag, int *loop_start, int *loop_end, int *sample_rate, int *channels, int *bitrate, pfc::string_base & description, abort_callback & p_abort); void get_subsong_info(t_uint32 p_subsong, pfc::string_base& title, int* length_in_ms, int* total_samples, int* loop_flag, int *loop_start, int* loop_end, int* sample_rate, int* channels, int* bitrate, pfc::string_base& description, abort_callback& p_abort);
bool get_description_tag(pfc::string_base & temp, pfc::string_base const& description, const char *tag, char delimiter = '\n'); bool get_description_tag(pfc::string_base& temp, pfc::string_base const& description, const char* tag, char delimiter = '\n');
void apply_config(VGMSTREAM * vgmstream); void apply_config(VGMSTREAM* vgmstream);
static void g_load_cfg(int *accept_unknown, int *accept_common); static void g_load_cfg(int* accept_unknown, int* accept_common);
}; };
/* foo_streamfile.cpp */ /* foo_streamfile.cpp */
STREAMFILE * open_foo_streamfile(const char * const filename, abort_callback * p_abort, t_filestats * stats); STREAMFILE* open_foo_streamfile(const char* const filename, abort_callback* p_abort, t_filestats* stats);
#endif /*_FOO_VGMSTREAM_*/ #endif /*_FOO_VGMSTREAM_*/

13
make-build-cmake.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/sh
# example script that builds vgmstream with most libs enabled using CMake + make
sudo apt-get update
sudo apt-get install gcc g++ make build-essential git cmake
sudo apt-get install libao-dev audacious-dev libjansson-dev
sudo apt-get install libvorbis-dev libmpg123-dev libspeex-dev libavformat-dev libavcodec-dev libavutil-dev libswresample-dev
mkdir -p build
cd build
cmake -S .. -B .
make

View File

@ -15,18 +15,25 @@ if((Test-Path $config_file)) { . $config_file }
# - toolsets: "" (default), "v140" (MSVC 2015), "v141" (MSVC 2017), "v141_xp" (XP support), "v142" (MSVC 2019), etc # - toolsets: "" (default), "v140" (MSVC 2015), "v141" (MSVC 2017), "v141_xp" (XP support), "v142" (MSVC 2019), etc
if (!$toolset) { $toolset = "" } if (!$toolset) { $toolset = "" }
# - sdks: "" (default), "7.0" (Win7 SDK), "8.1" (Win8 SDK), "10.0" (Win10 SDK), etc # - sdks: "" (default), "7.0" (Win7 SDK), "8.1" (Win8 SDK), "10.0" (Win10 SDK), etc
if (!$sdk) { $sdk = "" } if (!$sdk) { $sdk = "" }
# - platforms: "" (default), "Win32" # - platforms: "" (default), "Win32"
if (!$platform) { $platform = "" } if (!$platform) { $platform = "" }
# print compilation log # print compilation log
#$log = 1 #$log = 1
# Debug or Release, usually
if (!$configuration) { $configuration = "Release" }
############################################################################### ###############################################################################
$solution = "vgmstream_full.sln" $solution = "vgmstream_full.sln"
$dependencies = "dependencies" $dependencies = "dependencies"
$vswhere = "$dependencies/vswhere.exe" $vswhere = "$dependencies/vswhere.exe"
$config = "/p:Configuration=Release" $config = "/p:Configuration=" + $configuration
# not used ATM # not used ATM
$enable_aac = 0 $enable_aac = 0
@ -155,7 +162,7 @@ function Clean
Remove-Item -Path "cli/Release" -Recurse -ErrorAction Ignore Remove-Item -Path "cli/Release" -Recurse -ErrorAction Ignore
Remove-Item -Path "ext_libs/Debug" -Recurse -ErrorAction Ignore Remove-Item -Path "ext_libs/Debug" -Recurse -ErrorAction Ignore
Remove-Item -Path "ext_libs/Release" -Recurse -ErrorAction Ignore Remove-Item -Path "ext_libs/Release" -Recurse -ErrorAction Ignore
Remove-Item -Path "ext_libs/Getopt/Release" -Recurse -ErrorAction Ignore Remove-Item -Path "ext_libs/Getopt/Debug" -Recurse -ErrorAction Ignore
Remove-Item -Path "ext_libs/Getopt/Release" -Recurse -ErrorAction Ignore Remove-Item -Path "ext_libs/Getopt/Release" -Recurse -ErrorAction Ignore
Remove-Item -Path "fb2k/Debug" -Recurse -ErrorAction Ignore Remove-Item -Path "fb2k/Debug" -Recurse -ErrorAction Ignore
Remove-Item -Path "fb2k/Release" -Recurse -ErrorAction Ignore Remove-Item -Path "fb2k/Release" -Recurse -ErrorAction Ignore
@ -174,48 +181,48 @@ function Clean
$fb2kFiles = @( $fb2kFiles = @(
"ext_libs/*.dll", "ext_libs/*.dll",
"ext_libs/libspeex/*.dll", "ext_libs/libspeex/*.dll",
"Release/foo_input_vgmstream.dll", "$configuration/foo_input_vgmstream.dll",
"README.md" "README.md"
) )
$cliFiles = @( $cliFiles = @(
"ext_libs/*.dll", "ext_libs/*.dll",
"ext_libs/libspeex/*.dll", "ext_libs/libspeex/*.dll",
"Release/in_vgmstream.dll", "$configuration/in_vgmstream.dll",
"Release/test.exe", "$configuration/test.exe",
"Release/xmp-vgmstream.dll", "$configuration/xmp-vgmstream.dll",
"COPYING", "COPYING",
"README.md" "README.md"
) )
$fb2kPdbFiles = @( $fb2kPdbFiles = @(
"Release/foo_input_vgmstream.pdb" "$configuration/foo_input_vgmstream.pdb"
) )
$cliPdbFiles = @( $cliPdbFiles = @(
"Release/in_vgmstream.pdb", "$configuration/in_vgmstream.pdb",
"Release/test.pdb", "$configuration/test.pdb",
"Release/xmp-vgmstream.pdb" "$configuration/xmp-vgmstream.pdb"
) )
function Package function Package
{ {
Build Build
if(!(Test-Path "Release/test.exe")) { if(!(Test-Path "$configuration/test.exe")) {
Write-Error "Unable to find binaries, check for compilation errors" Write-Error "Unable to find binaries, check for compilation errors"
} }
Compress-Archive $cliFiles Release/vgmstream-win.zip -Force Compress-Archive $cliFiles $configuration/vgmstream-win.zip -Force
Compress-Archive $fb2kFiles Release/foo_input_vgmstream.zip -Force Compress-Archive $fb2kFiles $configuration/foo_input_vgmstream.zip -Force
Compress-Archive $cliPdbFiles Release/vgmstream-win.pdb.zip -Force Compress-Archive $cliPdbFiles $configuration/vgmstream-win.pdb.zip -Force
Compress-Archive $fb2kPdbFiles Release/foo_input_vgmstream.pdb.zip -Force Compress-Archive $fb2kPdbFiles $configuration/foo_input_vgmstream.pdb.zip -Force
md -Force bin md -Force bin
Move-Item Release/vgmstream-win.zip bin/vgmstream-win.zip -Force Move-Item $configuration/vgmstream-win.zip bin/vgmstream-win.zip -Force
Move-Item Release/foo_input_vgmstream.zip bin/foo_input_vgmstream.fb2k-component -Force Move-Item $configuration/foo_input_vgmstream.zip bin/foo_input_vgmstream.fb2k-component -Force
Move-Item Release/vgmstream-win.pdb.zip bin/vgmstream-win.pdb.zip -Force Move-Item $configuration/vgmstream-win.pdb.zip bin/vgmstream-win.pdb.zip -Force
Move-Item Release/foo_input_vgmstream.pdb.zip bin/foo_input_vgmstream.pdb.zip -Force Move-Item $configuration/foo_input_vgmstream.pdb.zip bin/foo_input_vgmstream.pdb.zip -Force
} }

View File

@ -682,6 +682,6 @@ int mpc_get_samples(STREAMFILE* sf, off_t offset, int32_t* p_samples, int32_t* p
/* helper to pass a wrapped, clamped, fake extension-ed, SF to another meta */ /* helper to pass a wrapped, clamped, fake extension-ed, SF to another meta */
STREAMFILE* setup_subfile_streamfile(STREAMFILE* sf, off_t subfile_offset, size_t subfile_size, const char* extension); STREAMFILE* setup_subfile_streamfile(STREAMFILE* sf, offv_t subfile_offset, size_t subfile_size, const char* extension);
#endif /*_CODING_H*/ #endif /*_CODING_H*/

View File

@ -1015,21 +1015,21 @@ size_t aac_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes) {
/* variable-sized var reader */ /* variable-sized var reader */
static int mpc_get_size(uint8_t* header, int header_size, int pos, int32_t* p_size) { static int mpc_get_size(uint8_t* header, int header_size, int pos, int32_t* p_size) {
uint8_t tmp; uint8_t tmp;
int32_t size = 0; int32_t size = 0;
do { do {
if (pos >= header_size) if (pos >= header_size)
return pos; return pos;
tmp = header[pos]; tmp = header[pos];
size = (size << 7) | (tmp & 0x7F); size = (size << 7) | (tmp & 0x7F);
pos++; pos++;
} }
while((tmp & 0x80)); while((tmp & 0x80));
*p_size = size; *p_size = size;
return pos; return pos;
} }
int mpc_get_samples(STREAMFILE* sf, off_t offset, int32_t* p_samples, int32_t* p_delay) { int mpc_get_samples(STREAMFILE* sf, off_t offset, int32_t* p_samples, int32_t* p_delay) {
@ -1092,8 +1092,8 @@ fail:
/* CUSTOM STREAMFILES */ /* CUSTOM STREAMFILES */
/* ******************************************** */ /* ******************************************** */
STREAMFILE* setup_subfile_streamfile(STREAMFILE* sf, off_t subfile_offset, size_t subfile_size, const char* extension) { STREAMFILE* setup_subfile_streamfile(STREAMFILE* sf, offv_t subfile_offset, size_t subfile_size, const char* extension) {
STREAMFILE *new_sf = NULL; STREAMFILE* new_sf = NULL;
new_sf = open_wrap_streamfile(sf); new_sf = open_wrap_streamfile(sf);
new_sf = open_clamp_streamfile_f(new_sf, subfile_offset, subfile_size); new_sf = open_clamp_streamfile_f(new_sf, subfile_offset, subfile_size);

View File

@ -115,9 +115,9 @@ void decode_ea_mt(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, in
* notify the decoder when a new substream begins (even with looping disabled). */ * notify the decoder when a new substream begins (even with looping disabled). */
if (ch_data->loop_sample > 0 && ch_data->samples_done == ch_data->loop_sample) { if (ch_data->loop_sample > 0 && ch_data->samples_done == ch_data->loop_sample) {
ch_data->samples_filled = 0; ch_data->samples_filled = 0;
ch_data->samples_discard = 0; ch_data->samples_discard = 0;
/* offset is usually at loop_offset here, but not always (ex. loop_sample < 432) */ /* offset is usually at loop_offset here, but not always (ex. loop_sample < 432) */
ch_data->offset = ch_data->loop_offset; ch_data->offset = ch_data->loop_offset;
utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */ utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */
utk_reset(ctx); /* decoder init (all fields must be reset, for some edge cases) */ utk_reset(ctx); /* decoder init (all fields must be reset, for some edge cases) */

View File

@ -322,7 +322,7 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
goto fail; goto fail;
if (size == 0 || start + size > get_streamfile_size(sf)) { if (size == 0 || start + size > get_streamfile_size(sf)) {
VGM_LOG("FFMPEG: wrong start+size found: %x + %x > %x \n", (uint32_t)start, (uint32_t)size, get_streamfile_size(sf)); vgm_asserti(size != 0, "FFMPEG: wrong start+size found: %x + %x > %x \n", (uint32_t)start, (uint32_t)size, get_streamfile_size(sf));
size = get_streamfile_size(sf) - start; size = get_streamfile_size(sf) - start;
} }

View File

@ -72,7 +72,7 @@ fail:
} }
void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do) {
int samples_done = 0; int samples_done = 0;
const unsigned int channels = data->info.channelCount; const unsigned int channels = data->info.channelCount;
const unsigned int blockSize = data->info.blockSize; const unsigned int blockSize = data->info.blockSize;

View File

@ -30,25 +30,25 @@ void clHCA_delete(clHCA *);
int clHCA_DecodeHeader(clHCA *, const void *data, unsigned int size); int clHCA_DecodeHeader(clHCA *, const void *data, unsigned int size);
typedef struct clHCA_stInfo { typedef struct clHCA_stInfo {
unsigned int version; unsigned int version;
unsigned int headerSize; unsigned int headerSize;
unsigned int samplingRate; unsigned int samplingRate;
unsigned int channelCount; unsigned int channelCount;
unsigned int blockSize; unsigned int blockSize;
unsigned int blockCount; unsigned int blockCount;
unsigned int encoderDelay; /* samples appended to the beginning */ unsigned int encoderDelay; /* samples appended to the beginning */
unsigned int encoderPadding; /* samples appended to the end */ unsigned int encoderPadding; /* samples appended to the end */
unsigned int loopEnabled; unsigned int loopEnabled;
unsigned int loopStartBlock; unsigned int loopStartBlock;
unsigned int loopEndBlock; unsigned int loopEndBlock;
unsigned int loopStartDelay; /* samples in block before loop starts */ unsigned int loopStartDelay; /* samples in block before loop starts */
unsigned int loopEndPadding; /* samples in block after loop ends */ unsigned int loopEndPadding; /* samples in block after loop ends */
unsigned int samplesPerBlock; /* should be 1024 */ unsigned int samplesPerBlock; /* should be 1024 */
const char *comment; const char *comment;
unsigned int encryptionEnabled; /* requires keycode */ unsigned int encryptionEnabled; /* requires keycode */
/* Derived sample formulas: /* Derived sample formulas:
* - sample count: blockCount*samplesPerBlock - encoderDelay - encoderPadding; * - sample count: blockCount*samplesPerBlock - encoderDelay - encoderPadding;
* - loop start sample = loopStartBlock*samplesPerBlock - encoderDelay + loopStartDelay * - loop start sample = loopStartBlock*samplesPerBlock - encoderDelay + loopStartDelay
* - loop end sample = loopEndBlock*samplesPerBlock - encoderDelay + (samplesPerBlock - info.loopEndPadding) * - loop end sample = loopEndBlock*samplesPerBlock - encoderDelay + (samplesPerBlock - info.loopEndPadding)
*/ */

View File

@ -289,8 +289,8 @@ imuse_codec_data* init_imuse(STREAMFILE* sf, int channels) {
while (counter > 0) { while (counter > 0) {
if (counter & i) if (counter & i)
value += step; value += step;
step >>= 1; step >>= 1;
counter >>= 1; counter >>= 1;
} }
data->adpcm_table[i + j * 64] = value; /* non sequential: all 64 [0]s, [1]s ... [88]s */ data->adpcm_table[i + j * 64] = value; /* non sequential: all 64 [0]s, [1]s ... [88]s */

View File

@ -250,11 +250,11 @@ static void decode_mpeg_standard(VGMSTREAMCHANNEL* stream, mpeg_codec_data* data
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->buffer_size,stream->streamfile); data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->buffer_size,stream->streamfile);
/* end of stream, fill rest with 0s */ /* end of stream, fill rest with 0s */
if (data->bytes_in_buffer <= 0) { if (data->bytes_in_buffer <= 0) {
VGM_ASSERT(samples_to_do < samples_done, "MPEG: end of stream, filling %i\n", (samples_to_do - samples_done)); VGM_ASSERT(samples_to_do < samples_done, "MPEG: end of stream, filling %i\n", (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample)); memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * channels * sizeof(sample));
break; break;
} }
data->buffer_full = 1; data->buffer_full = 1;
data->buffer_used = 0; data->buffer_used = 0;

View File

@ -26,8 +26,8 @@ extern void fft(int n, float* xRe, float* xIm, float* yRe, float* yIm);
#define RELIC_MAX_SIZE RELIC_SIZE_HIGH #define RELIC_MAX_SIZE RELIC_SIZE_HIGH
#define RELIC_MAX_FREQ (RELIC_MAX_SIZE / 2) #define RELIC_MAX_FREQ (RELIC_MAX_SIZE / 2)
#define RELIC_MAX_FFT (RELIC_MAX_SIZE / 4) #define RELIC_MAX_FFT (RELIC_MAX_SIZE / 4)
#define RELIC_MIN_BITRATE 256 #define RELIC_MIN_BITRATE 256
#define RELIC_MAX_BITRATE 2048 #define RELIC_MAX_BITRATE 2048
//#define RELIC_MAX_FRAME_SIZE ((RELIC_MAX_BITRATE / 8) + 0x04) /* extra 0x04 for the bitreader */ //#define RELIC_MAX_FRAME_SIZE ((RELIC_MAX_BITRATE / 8) + 0x04) /* extra 0x04 for the bitreader */

View File

@ -6,24 +6,22 @@
/* Based on Valery V. Anisimovsky's WS-AUD.txt */ /* Based on Valery V. Anisimovsky's WS-AUD.txt */
static char WSTable2bit[4]={-2,-1,0,1}; static char WSTable2bit[4] = { -2,-1,0,1 };
static char WSTable4bit[16]={-9,-8,-6,-5,-4,-3,-2,-1, static char WSTable4bit[16] = { -9,-8,-6,-5,-4,-3,-2,-1, 0, 1, 2, 3, 4, 5 ,6, 8 };
0, 1, 2, 3, 4, 5 ,6, 8};
/* We pass in the VGMSTREAM here, unlike in other codings, because /* We pass in the VGMSTREAM here, unlike in other codings, because
the decoder has to know about the block structure. */ the decoder has to know about the block structure. */
void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample, void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample,
int32_t samples_to_do) { int32_t samples_to_do) {
VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]); VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]);
int16_t hist = stream->adpcm_history1_16; int16_t hist = stream->adpcm_history1_16;
off_t offset = stream->offset; off_t offset = stream->offset;
int samples_left_in_frame = stream->samples_left_in_frame; int samples_left_in_frame = stream->samples_left_in_frame;
off_t header_off = stream->frame_header_offset; off_t header_off = stream->frame_header_offset;
int i; int i;
int32_t sample_count; int32_t sample_count;
if (vgmstream->ws_output_size == vgmstream->current_block_size) { if (vgmstream->ws_output_size == vgmstream->current_block_size) {
/* uncompressed, we just need to convert to 16-bit */ /* uncompressed, we just need to convert to 16-bit */
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing,offset++) { for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing,offset++) {

View File

@ -164,6 +164,7 @@ static const char* extension_list[] = {
"dtk", "dtk",
"dvi", "dvi",
"dxh", "dxh",
"dyx", //txth/reserved [Shrek 4 (iOS)]
"e4x", "e4x",
"eam", "eam",
@ -392,6 +393,7 @@ static const char* extension_list[] = {
"pona", "pona",
"pos", "pos",
"ps2stm", //fake extension for .stm (renamed? to be removed?) "ps2stm", //fake extension for .stm (renamed? to be removed?)
"psb", //txth/reserved [Legend of Mana (Switch), Senxin Aleste (AC)]
"psf", "psf",
"psh", //fake extension for .vsv (to be removed) "psh", //fake extension for .vsv (to be removed)
"psnd", "psnd",
@ -853,7 +855,7 @@ static const coding_info coding_info_list[] = {
{coding_FFmpeg, "FFmpeg"}, {coding_FFmpeg, "FFmpeg"},
#endif #endif
#ifdef VGM_USE_FDKAAC #ifdef VGM_USE_FDKAAC
{coding_MP4_AAC, "MPEG-4 AAC"}, {coding_MP4_AAC, "MPEG-4 AAC"},
#endif #endif
}; };

View File

@ -239,7 +239,7 @@ void blocked_count_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset) {
case coding_PCM8_U_int: block_samples = pcm8_bytes_to_samples(vgmstream->current_block_size, 1); break; case coding_PCM8_U_int: block_samples = pcm8_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_XBOX_IMA: block_samples = xbox_ima_bytes_to_samples(vgmstream->current_block_size, 1); break; case coding_XBOX_IMA: block_samples = xbox_ima_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_NGC_DSP: block_samples = dsp_bytes_to_samples(vgmstream->current_block_size, 1); break; case coding_NGC_DSP: block_samples = dsp_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_PSX: block_samples = ps_bytes_to_samples(vgmstream->current_block_size,1); break; case coding_PSX: block_samples = ps_bytes_to_samples(vgmstream->current_block_size,1); break;
default: default:
VGM_LOG("BLOCKED: missing codec\n"); VGM_LOG("BLOCKED: missing codec\n");
return; return;

View File

@ -5,7 +5,7 @@ VGMSTREAM* init_vgmstream_adp_bos(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
off_t start_offset; off_t start_offset;
int loop_flag = 0; int loop_flag = 0;
int channels; int channels;
/* checks */ /* checks */
if (!check_extensions(sf,"adp")) if (!check_extensions(sf,"adp"))
@ -19,7 +19,7 @@ VGMSTREAM* init_vgmstream_adp_bos(STREAMFILE* sf) {
start_offset = 0x18; start_offset = 0x18;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag); vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;

View File

@ -234,7 +234,7 @@ static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre
} }
} }
/* probably loaded */ /* probably loaded */
load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory); load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory);
close_streamfile(sf_acb); close_streamfile(sf_acb);

View File

@ -1,147 +1,154 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../util.h" #include "../util.h"
static int bink_get_info(STREAMFILE *streamFile, int target_subsong, int * out_total_streams, size_t *out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples); static int bink_get_info(STREAMFILE* sf, int target_subsong, int* p_total_subsongs, size_t* p_stream_size, int* p_channels, int* p_sample_rate, int* p_num_samples);
/* BINK 1/2 - RAD Game Tools movies (audio/video format) */ /* BINK 1/2 - RAD Game Tools movies (audio/video format) */
VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_bik(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0; int channels = 0, loop_flag = 0, sample_rate = 0, num_samples = 0;
int total_subsongs = 0, target_subsong = streamFile->stream_index; int total_subsongs = 0, target_subsong = sf->stream_index;
size_t stream_size; size_t stream_size;
/* checks */ /* checks */
/* .bik/bik2/bk2: standard /* .bik/bik2/bk2: standard
* .bika = fake extension for demuxed audio */ * .bika: fake extension for demuxed audio */
if (!check_extensions(streamFile,"bik,bika,bik2,bk2")) if (!check_extensions(sf,"bik,bik2,bk2,bika"))
goto fail; goto fail;
/* check header "BIK" (bink 1) or "KB2" (bink 2), followed by version-char (audio is the same for both) */ /* check bink1/2 header, followed by version-char (audio is the same) */
if ((read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x42494B00 && if ((read_32bitBE(0x00,sf) & 0xffffff00) != get_id32be("BIK\0") &&
(read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail; (read_32bitBE(0x00,sf) & 0xffffff00) != get_id32be("KB2\0"))
goto fail;
/* find target stream info and samples */
if (!bink_get_info(streamFile, target_subsong, &total_subsongs, &stream_size, &channel_count, &sample_rate, &num_samples)) /* find target stream info and samples */
goto fail; if (!bink_get_info(sf, target_subsong, &total_subsongs, &stream_size, &channels, &sample_rate, &num_samples))
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag); /* build the VGMSTREAM */
if (!vgmstream) goto fail; vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->layout_type = layout_none;
vgmstream->sample_rate = sample_rate; vgmstream->layout_type = layout_none;
vgmstream->num_samples = num_samples; vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_subsongs; vgmstream->num_samples = num_samples;
vgmstream->stream_size = stream_size; vgmstream->num_streams = total_subsongs;
vgmstream->meta_type = meta_BINK; vgmstream->stream_size = stream_size;
vgmstream->meta_type = meta_BINK;
#ifdef VGM_USE_FFMPEG
{ #ifdef VGM_USE_FFMPEG
/* target_subsong should be passed manually */ {
/* target_subsong should be passed manually */
vgmstream->codec_data = init_ffmpeg_header_offset_subsong(streamFile, NULL,0, 0x0,get_streamfile_size(streamFile), target_subsong); vgmstream->codec_data = init_ffmpeg_header_offset_subsong(sf, NULL,0, 0x0,0, target_subsong);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
} }
#else #else
goto fail; goto fail;
#endif #endif
return vgmstream; return vgmstream;
fail: fail:
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }
/** /**
* Gets stream info, and number of samples in a BINK file by reading all frames' headers (as it's VBR), * Gets stream info, and number of samples in a BINK file by reading all frames' headers (as it's VBR),
* as they are not in the main header. The header for BINK1 and 2 is the same. * as they are not in the main header. The header for BINK1 and 2 is the same.
* (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough) * (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough)
*/ */
static int bink_get_info(STREAMFILE *streamFile, int target_subsong, int * out_total_subsongs, size_t * out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples) { static int bink_get_info(STREAMFILE* sf, int target_subsong, int* p_total_subsongs, size_t* p_stream_size, int* p_channels, int* p_sample_rate, int* p_num_samples) {
uint32_t *offsets = NULL; uint32_t* offsets = NULL;
uint32_t num_frames, num_samples_b = 0; uint32_t num_frames, num_samples_b = 0;
off_t cur_offset; off_t cur_offset;
int i, j, sample_rate, channel_count; int i, j, sample_rate, channels;
int total_subsongs; int total_subsongs;
size_t stream_size = 0; size_t stream_size = 0;
size_t filesize = get_streamfile_size(streamFile); size_t filesize = get_streamfile_size(sf);
uint32_t signature = (read_32bitBE(0x00,streamFile) & 0xffffff00); uint32_t signature = (read_32bitBE(0x00,sf) & 0xffffff00);
uint8_t revision = (read_32bitBE(0x00,streamFile) & 0xFF); uint8_t revision = (read_32bitBE(0x00,sf) & 0xFF);
if (read_32bitLE(0x04,streamFile)+ 0x08 != filesize)
goto fail; if (read_32bitLE(0x04,sf) + 0x08 != filesize)
goto fail;
num_frames = (uint32_t)read_32bitLE(0x08,streamFile);
if (num_frames == 0 || num_frames > 0x100000) goto fail; /* something must be off (avoids big allocs below) */ num_frames = (uint32_t)read_32bitLE(0x08,sf);
if (num_frames == 0 || num_frames > 0x100000) goto fail; /* something must be off (avoids big allocs below) */
/* multichannel/multilanguage audio is usually N streams of stereo/mono, no way to know channel layout */
total_subsongs = read_32bitLE(0x28,streamFile); /* multichannel/multilanguage audio is usually N streams of stereo/mono, no way to know channel layout */
if (target_subsong == 0) target_subsong = 1; total_subsongs = read_32bitLE(0x28,sf);
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1 || total_subsongs > 255) goto fail; if (total_subsongs < 1) {
vgm_logi("BIK: no audio in movie (ignore)\n");
/* find stream info and position in offset table */ goto fail;
cur_offset = 0x2c; }
if ((signature == 0x42494B00 && (revision == 0x6b)) || /* k */
(signature == 0x4B423200 && (revision == 0x69 || revision == 0x6a || revision == 0x6b))) /* i,j,k */ if (target_subsong == 0) target_subsong = 1;
cur_offset += 0x04; /* unknown v2 header field */ if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs > 255) goto fail;
cur_offset += 0x04*total_subsongs; /* skip streams max packet bytes */
sample_rate = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x00,streamFile);
channel_count = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x02,streamFile) & 0x2000 ? 2 : 1; /* stereo flag */ /* find stream info and position in offset table */
cur_offset += 0x04*total_subsongs; /* skip streams info */ cur_offset = 0x2c;
cur_offset += 0x04*total_subsongs; /* skip streams ids */ if ((signature == 0x42494B00 && (revision == 0x6b)) || /* k */
(signature == 0x4B423200 && (revision == 0x69 || revision == 0x6a || revision == 0x6b))) /* i,j,k */
cur_offset += 0x04; /* unknown v2 header field */
/* read frame offsets in a buffer, to avoid fseeking to the table back and forth */ cur_offset += 0x04*total_subsongs; /* skip streams max packet bytes */
offsets = malloc(sizeof(uint32_t) * num_frames); sample_rate = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x00,sf);
if (!offsets) goto fail; channels = (uint16_t)read_16bitLE(cur_offset+0x04*(target_subsong-1)+0x02,sf) & 0x2000 ? 2 : 1; /* stereo flag */
cur_offset += 0x04*total_subsongs; /* skip streams info */
for (i=0; i < num_frames; i++) { cur_offset += 0x04*total_subsongs; /* skip streams ids */
offsets[i] = read_32bitLE(cur_offset,streamFile) & 0xFFFFFFFE; /* mask first bit (= keyframe) */
cur_offset += 0x4;
/* read frame offsets in a buffer, to avoid fseeking to the table back and forth */
if (offsets[i] > filesize) goto fail; offsets = malloc(sizeof(uint32_t) * num_frames);
} if (!offsets) goto fail;
/* after the last index is the file size, validate just in case */
if (read_32bitLE(cur_offset,streamFile) != filesize) goto fail; for (i=0; i < num_frames; i++) {
offsets[i] = read_32bitLE(cur_offset,sf) & 0xFFFFFFFE; /* mask first bit (= keyframe) */
/* read each frame header and sum all samples cur_offset += 0x4;
* a frame has N audio packets with a header (one per stream) + video packet */
for (i=0; i < num_frames; i++) { if (offsets[i] > filesize) goto fail;
cur_offset = offsets[i]; }
/* after the last index is the file size, validate just in case */
/* read audio packet headers per stream */ if (read_32bitLE(cur_offset,sf) != filesize) goto fail;
for (j=0; j < total_subsongs; j++) {
uint32_t ap_size = read_32bitLE(cur_offset+0x00,streamFile); /* not counting this int */ /* read each frame header and sum all samples
* a frame has N audio packets with a header (one per stream) + video packet */
if (j == target_subsong-1) { for (i=0; i < num_frames; i++) {
stream_size += 0x04 + ap_size; cur_offset = offsets[i];
if (ap_size > 0)
num_samples_b += read_32bitLE(cur_offset+0x04,streamFile); /* decoded samples in bytes */ /* read audio packet headers per stream */
break; /* next frame */ for (j=0; j < total_subsongs; j++) {
} uint32_t ap_size = read_32bitLE(cur_offset+0x00,sf); /* not counting this int */
else { /* next stream packet or frame */
cur_offset += 4 + ap_size; //todo sometimes ap_size doesn't include itself (+4), others it does? if (j == target_subsong-1) {
} stream_size += 0x04 + ap_size;
} if (ap_size > 0)
} num_samples_b += read_32bitLE(cur_offset+0x04,sf); /* decoded samples in bytes */
break; /* next frame */
free(offsets); }
else { /* next stream packet or frame */
cur_offset += 4 + ap_size; //todo sometimes ap_size doesn't include itself (+4), others it does?
if (out_total_subsongs) *out_total_subsongs = total_subsongs; }
if (out_stream_size) *out_stream_size = stream_size; }
if (out_sample_rate) *out_sample_rate = sample_rate; }
if (out_channel_count) *out_channel_count = channel_count;
//todo returns a few more samples (~48) than binkconv.exe? free(offsets);
if (out_num_samples) *out_num_samples = num_samples_b / (2 * channel_count);
return 1; if (p_total_subsongs) *p_total_subsongs = total_subsongs;
if (p_stream_size) *p_stream_size = stream_size;
fail: if (p_sample_rate) *p_sample_rate = sample_rate;
free(offsets); if (p_channels) *p_channels = channels;
return 0; //todo returns a few more samples (~48) than binkconv.exe?
} if (p_num_samples) *p_num_samples = num_samples_b / (2 * channels);
return 1;
fail:
free(offsets);
return 0;
}

View File

@ -1,5 +1,6 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../util/chunks.h"
/* BKHD - Wwise soundbank container */ /* BKHD - Wwise soundbank container */
@ -19,9 +20,9 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
if (!check_extensions(sf,"bnk")) if (!check_extensions(sf,"bnk"))
goto fail; goto fail;
if (read_u32be(0x00, sf) == 0x414B424B) /* "AKBK" [Shadowrun (X360)] */ if (is_id32be(0x00, sf, "AKBK")) /* [Shadowrun (X360)] */
base_offset = 0x0c; base_offset = 0x0c;
if (read_u32be(base_offset + 0x00, sf) != 0x424B4844) /* "BKHD" */ if (!is_id32be(base_offset + 0x00, sf, "BKHD"))
goto fail; goto fail;
big_endian = guess_endianness32bit(base_offset + 0x04, sf); big_endian = guess_endianness32bit(base_offset + 0x04, sf);
read_u32 = big_endian ? read_u32be : read_u32le; read_u32 = big_endian ? read_u32be : read_u32le;
@ -77,16 +78,41 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
subfile_size = read_u32(offset + 0x14, sf); subfile_size = read_u32(offset + 0x14, sf);
} }
else { else {
off_t didx_offset, data_offset, offset; enum {
size_t didx_size; CHUNK_DIDX = 0x44494458, /* "DIDX" */
if (!find_chunk(sf, 0x44494458, 0x00,0, &didx_offset, &didx_size, big_endian, 0)) /* "DIDX" */ CHUNK_DATA = 0x44415441, /* "DATA" */
goto fail; };
if (!find_chunk(sf, 0x44415441, 0x00,0, &data_offset, NULL, big_endian, 0)) /* "DATA" */ off_t didx_offset = 0, data_offset = 0, didx_size = 0, offset;
chunk_t rc = {0};
rc.be_size = big_endian;
rc.current = 0x00;
while (next_chunk(&rc, sf)) {
switch(rc.type) {
case CHUNK_DIDX:
didx_offset = rc.offset;
didx_size = rc.size;
break;
case CHUNK_DATA:
data_offset = rc.offset;
break;
default:
break;
}
}
if (!didx_offset || !data_offset)
goto fail; goto fail;
total_subsongs = didx_size / 0x0c; total_subsongs = didx_size / 0x0c;
if (total_subsongs < 1) {
vgm_logi("BKHD: bank has no subsongs (ignore)\n");
goto fail;
}
if (target_subsong == 0) target_subsong = 1; if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; if (target_subsong > total_subsongs) goto fail;
offset = didx_offset + (target_subsong - 1) * 0x0c; offset = didx_offset + (target_subsong - 1) * 0x0c;
subfile_id = read_u32(offset + 0x00, sf); subfile_id = read_u32(offset + 0x00, sf);

View File

@ -5,10 +5,10 @@
#define COLUMN_BITMASK_TYPE 0x0f #define COLUMN_BITMASK_TYPE 0x0f
enum columna_flag_t { enum columna_flag_t {
COLUMN_FLAG_NAME = 0x10, COLUMN_FLAG_NAME = 0x10,
COLUMN_FLAG_DEFAULT = 0x20, COLUMN_FLAG_DEFAULT = 0x20,
COLUMN_FLAG_ROW = 0x40, COLUMN_FLAG_ROW = 0x40,
COLUMN_FLAG_UNDEFINED = 0x80 /* shouldn't exist */ COLUMN_FLAG_UNDEFINED = 0x80 /* shouldn't exist */
}; };
enum column_type_t { enum column_type_t {

View File

@ -2,49 +2,47 @@
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../util.h" #include "../util.h"
/* .ADX - from Xenoblade 3D */ /* .ADX - from Xenoblade 3D (3DS) */
/* Xenoblade Chronicles 3D uses an adx extension as with VGMSTREAM* init_vgmstream_dsp_adx(STREAMFILE *sf) {
* the Wii version, but it's actually DSP ADPCM. */ VGMSTREAM* vgmstream = NULL;
VGMSTREAM * init_vgmstream_dsp_adx(STREAMFILE *streamFile) { int loop_flag, channels;
VGMSTREAM * vgmstream = NULL;
int loop_flag, channel_count;
int channel_header_spacing = 0x34; int channel_header_spacing = 0x34;
/* check extension, case insensitive */
if (!check_extensions(streamFile,"adx")) goto fail;
/* check header */ /* checks */
if (read_32bitBE(0,streamFile)!=0x02000000) goto fail; if (!check_extensions(sf,"adx"))
goto fail;
channel_count = read_32bitLE(0, streamFile); if (read_u32be(0x00,sf) != 0x02000000)
loop_flag = read_16bitLE(0x6e, streamFile); goto fail;
if (channel_count > 2 || channel_count < 0) goto fail; channels = read_32bitLE(0, sf);
loop_flag = read_16bitLE(0x6e, sf);
vgmstream = allocate_vgmstream(channel_count,loop_flag); if (channels > 2 || channels < 0) goto fail;
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->coding_type = coding_NGC_DSP; vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_XB3D_ADX; vgmstream->meta_type = meta_XB3D_ADX;
vgmstream->sample_rate = read_32bitLE(0x70,streamFile); vgmstream->sample_rate = read_32bitLE(0x70,sf);
vgmstream->num_samples = read_32bitLE(0x74, streamFile); vgmstream->num_samples = read_32bitLE(0x74, sf);
vgmstream->loop_start_sample = read_32bitLE(0x78, streamFile); vgmstream->loop_start_sample = read_32bitLE(0x78, sf);
vgmstream->loop_end_sample = read_32bitLE(0x7c, streamFile); vgmstream->loop_end_sample = read_32bitLE(0x7c, sf);
dsp_read_coefs_le(vgmstream,streamFile, 0x4, channel_header_spacing);
dsp_read_coefs_le(vgmstream,sf, 0x4, channel_header_spacing);
/* semi-interleave: manually open streams at offset */ /* semi-interleave: manually open streams at offset */
{ {
char filename[PATH_LIMIT]; char filename[PATH_LIMIT];
int i; int i;
streamFile->get_name(streamFile,filename,sizeof(filename)); sf->get_name(sf,filename,sizeof(filename));
for (i = 0; i<channel_count; i++) { for (i = 0; i<channels; i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); vgmstream->ch[i].streamfile = sf->open(sf, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].channel_start_offset =
vgmstream->ch[i].offset = read_32bitLE(0x34+i*channel_header_spacing, streamFile); vgmstream->ch[i].offset = read_32bitLE(0x34+i*channel_header_spacing, sf);
if (!vgmstream->ch[i].streamfile) goto fail; if (!vgmstream->ch[i].streamfile) goto fail;
} }
} }

View File

@ -18,28 +18,29 @@ typedef struct {
int32_t loop_end; int32_t loop_end;
int loop_flag; int loop_flag;
off_t sample_header_offset;
size_t sample_header_size; size_t sample_header_size;
size_t name_table_size; size_t name_table_size;
size_t sample_data_size; size_t sample_data_size;
size_t base_header_size; size_t base_header_size;
off_t extradata_offset; uint32_t extradata_offset;
size_t extradata_size; uint32_t extradata_size;
off_t stream_offset; uint32_t stream_offset;
size_t stream_size; uint32_t stream_size;
off_t name_offset; uint32_t name_offset;
} fsb5_header; } fsb5_header;
/* ********************************************************************************** */ /* ********************************************************************************** */
static layered_layout_data* build_layered_fsb5(STREAMFILE* sf, fsb5_header* fsb5); static layered_layout_data* build_layered_fsb5(STREAMFILE* sf, STREAMFILE* sb, fsb5_header* fsb5);
/* FSB5 - Firelight's FMOD Studio SoundBank format */ /* FSB5 - Firelight's FMOD Studio SoundBank format */
VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
STREAMFILE* sb = NULL;
fsb5_header fsb5 = {0}; fsb5_header fsb5 = {0};
uint32_t offset;
int target_subsong = sf->stream_index; int target_subsong = sf->stream_index;
int i; int i;
@ -53,7 +54,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
if (!is_id32be(0x00,sf, "FSB5")) if (!is_id32be(0x00,sf, "FSB5"))
goto fail; goto fail;
/* 0x00 is rare (seen in Tales from Space Vita) */ /* v0 is rare (seen in Tales from Space Vita) */
fsb5.version = read_u32le(0x04,sf); fsb5.version = read_u32le(0x04,sf);
if (fsb5.version != 0x00 && fsb5.version != 0x01) if (fsb5.version != 0x00 && fsb5.version != 0x01)
goto fail; goto fail;
@ -63,13 +64,20 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
fsb5.name_table_size = read_u32le(0x10,sf); fsb5.name_table_size = read_u32le(0x10,sf);
fsb5.sample_data_size = read_u32le(0x14,sf); fsb5.sample_data_size = read_u32le(0x14,sf);
fsb5.codec = read_u32le(0x18,sf); fsb5.codec = read_u32le(0x18,sf);
/* version 0x01 - 0x1c(4): zero, 0x24(16): hash, 0x34(8): unk /* 0x1c: zero */
* version 0x00 has an extra field (always 0?) at 0x1c */
if (fsb5.version == 0x01) { if (fsb5.version == 0x01) {
/* found by tests and assumed to be flags, no games known */ fsb5.flags = read_u32le(0x20,sf); /* found by tests and assumed to be flags, no games known */
fsb5.flags = read_u32le(0x20,sf); /* 0x24: 128-bit hash */
/* 0x34: unknown (64-bit sub-hash?) */
fsb5.base_header_size = 0x3c;
}
else {
/* 0x20: zero/flags? */
/* 0x24: zero/flags? */
/* 0x28: 128-bit hash */
/* 0x38: unknown (64-bit sub-hash?) */
fsb5.base_header_size = 0x40;
} }
fsb5.base_header_size = (fsb5.version==0x00) ? 0x40 : 0x3C;
if ((fsb5.sample_header_size + fsb5.name_table_size + fsb5.sample_data_size + fsb5.base_header_size) != get_streamfile_size(sf)) { if ((fsb5.sample_header_size + fsb5.name_table_size + fsb5.sample_data_size + fsb5.base_header_size) != get_streamfile_size(sf)) {
vgm_logi("FSB5: wrong size, expected %x + %x + %x + %x vs %x (re-rip)\n", fsb5.sample_header_size, fsb5.name_table_size, fsb5.sample_data_size, fsb5.base_header_size, get_streamfile_size(sf)); vgm_logi("FSB5: wrong size, expected %x + %x + %x + %x vs %x (re-rip)\n", fsb5.sample_header_size, fsb5.name_table_size, fsb5.sample_data_size, fsb5.base_header_size, get_streamfile_size(sf));
@ -79,28 +87,25 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
if (target_subsong == 0) target_subsong = 1; if (target_subsong == 0) target_subsong = 1;
if (target_subsong > fsb5.total_subsongs || fsb5.total_subsongs <= 0) goto fail; if (target_subsong > fsb5.total_subsongs || fsb5.total_subsongs <= 0) goto fail;
fsb5.sample_header_offset = fsb5.base_header_size;
/* find target stream header and data offset, and read all needed values for later use /* find target stream header and data offset, and read all needed values for later use
* (reads one by one as the size of a single stream header is variable) */ * (reads one by one as the size of a single stream header is variable) */
offset = fsb5.base_header_size;
for (i = 0; i < fsb5.total_subsongs; i++) { for (i = 0; i < fsb5.total_subsongs; i++) {
size_t stream_header_size = 0; uint32_t stream_header_size = 0;
off_t data_offset = 0; uint32_t data_offset = 0;
uint32_t sample_mode1, sample_mode2; /* maybe one uint64? */ uint64_t sample_mode;
sample_mode1 = read_u32le(fsb5.sample_header_offset+0x00,sf); sample_mode = read_u64le(offset+0x00,sf);
sample_mode2 = read_u32le(fsb5.sample_header_offset+0x04,sf);
stream_header_size += 0x08; stream_header_size += 0x08;
/* get samples */ /* get samples */
fsb5.num_samples = ((sample_mode2 >> 2) & 0x3FFFFFFF); /* bits2: 31..2 (30) */ fsb5.num_samples = ((sample_mode >> 34) & 0x3FFFFFFF); /* bits: 63..34 (30) */
/* get offset inside data section */ /* get offset inside data section (max 32b offset 0xFFFFFFE0) */
/* up to 0x07FFFFFF * 0x20 = full 32b offset 0xFFFFFFE0 */ data_offset = ((sample_mode >> 7) & 0x07FFFFFF) << 5; /* bits: 33..8 (25) */
data_offset = (((sample_mode2 & 0x03) << 25) | ((sample_mode1 >> 7) & 0x1FFFFFF)) << 5; /* bits2: 1..0 (2) | bits1: 31..8 (25) */
/* get channels */ /* get channels */
switch ((sample_mode1 >> 5) & 0x03) { /* bits1: 7..6 (2) */ switch ((sample_mode >> 5) & 0x03) { /* bits: 7..6 (2) */
case 0: fsb5.channels = 1; break; case 0: fsb5.channels = 1; break;
case 1: fsb5.channels = 2; break; case 1: fsb5.channels = 2; break;
case 2: fsb5.channels = 6; break; /* some Dark Souls 2 MPEG; some IMA ADPCM */ case 2: fsb5.channels = 6; break; /* some Dark Souls 2 MPEG; some IMA ADPCM */
@ -111,7 +116,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
} }
/* get sample rate */ /* get sample rate */
switch ((sample_mode1 >> 1) & 0x0f) { /* bits1: 5..1 (4) */ switch ((sample_mode >> 1) & 0x0f) { /* bits: 5..1 (4) */
case 0: fsb5.sample_rate = 4000; break; case 0: fsb5.sample_rate = 4000; break;
case 1: fsb5.sample_rate = 8000; break; case 1: fsb5.sample_rate = 8000; break;
case 2: fsb5.sample_rate = 11000; break; case 2: fsb5.sample_rate = 11000; break;
@ -129,8 +134,8 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
} }
/* get extra flags */ /* get extra flags */
if (sample_mode1 & 0x01) { /* bits1: 0 (1) */ if (sample_mode & 0x01) { /* bits: 0 (1) */
off_t extraflag_offset = fsb5.sample_header_offset+0x08; uint32_t extraflag_offset = offset + 0x08;
uint32_t extraflag, extraflag_type, extraflag_size, extraflag_end; uint32_t extraflag, extraflag_type, extraflag_size, extraflag_end;
do { do {
@ -217,7 +222,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
fsb5.channels = fsb5.channels * fsb5.layers; fsb5.channels = fsb5.channels * fsb5.layers;
break; break;
default: default:
vgm_logi("FSB5: stream %i unknown flag 0x%x at %x + 0x04 + 0x%x (report)\n", i, extraflag_type, (uint32_t)extraflag_offset, extraflag_size); vgm_logi("FSB5: stream %i unknown flag 0x%x at %x + 0x04 + 0x%x (report)\n", i, extraflag_type, extraflag_offset, extraflag_size);
break; break;
} }
} }
@ -242,11 +247,10 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
fsb5.stream_size = fsb5.sample_data_size - data_offset; fsb5.stream_size = fsb5.sample_data_size - data_offset;
} }
else { else {
off_t next_data_offset; uint32_t next_data_offset;
uint32_t next_sample_mode1, next_sample_mode2; uint64_t next_sample_mode;
next_sample_mode1 = read_u32le(fsb5.sample_header_offset+stream_header_size+0x00,sf); next_sample_mode = read_u64le(offset+stream_header_size+0x00,sf);
next_sample_mode2 = read_u32le(fsb5.sample_header_offset+stream_header_size+0x04,sf); next_data_offset = ((next_sample_mode >> 7) & 0x07FFFFFF) << 5;
next_data_offset = (((next_sample_mode2 & 0x03) << 25) | ((next_sample_mode1 >> 7) & 0x1FFFFFF)) << 5;
fsb5.stream_size = next_data_offset - data_offset; fsb5.stream_size = next_data_offset - data_offset;
} }
@ -255,9 +259,9 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
} }
/* continue searching target */ /* continue searching target */
fsb5.sample_header_offset += stream_header_size; offset += stream_header_size;
} }
/* target stream not found*/
if (!fsb5.stream_offset || !fsb5.stream_size) if (!fsb5.stream_offset || !fsb5.stream_size)
goto fail; goto fail;
@ -268,6 +272,17 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
} }
/* FSB5 can hit +2GB offsets, but since decoders aren't ready to handle that use a subfile to hide big offsets
* (some FSB5 CLI versions make buggy offsets = bad output but this was fixed later) */
if (fsb5.stream_offset > 0x7FFFFFFF) {
sb = setup_subfile_streamfile(sf, fsb5.stream_offset, fsb5.stream_size, NULL);
fsb5.stream_offset = 0x00;
}
else {
sb = sf;
}
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(fsb5.channels, fsb5.loop_flag); vgmstream = allocate_vgmstream(fsb5.channels, fsb5.loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
@ -282,7 +297,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
vgmstream->stream_size = fsb5.stream_size; vgmstream->stream_size = fsb5.stream_size;
vgmstream->meta_type = meta_FSB5; vgmstream->meta_type = meta_FSB5;
if (fsb5.name_offset) if (fsb5.name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, fsb5.name_offset,sf); read_string(vgmstream->stream_name,STREAM_NAME_SIZE, fsb5.name_offset, sf);
switch (fsb5.codec) { switch (fsb5.codec) {
case 0x00: /* FMOD_SOUND_FORMAT_NONE */ case 0x00: /* FMOD_SOUND_FORMAT_NONE */
@ -325,7 +340,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x02; vgmstream->interleave_block_size = 0x02;
} }
dsp_read_coefs_be(vgmstream,sf,fsb5.extradata_offset,0x2E); dsp_read_coefs_be(vgmstream, sf, fsb5.extradata_offset, 0x2E);
break; break;
case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM [Skylanders] */ case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM [Skylanders] */
@ -359,12 +374,12 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
block_count = fsb5.stream_size / block_size + (fsb5.stream_size % block_size ? 1 : 0); block_count = fsb5.stream_size / block_size + (fsb5.stream_size % block_size ? 1 : 0);
bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, fsb5.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, fsb5.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, fsb5.stream_offset,fsb5.stream_size); vgmstream->codec_data = init_ffmpeg_header_offset(sb, buf,bytes, fsb5.stream_offset, fsb5.stream_size);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, sf, fsb5.stream_offset,fsb5.stream_size, 0, 0,0); /* samples look ok */ xma_fix_raw_samples(vgmstream, sb, fsb5.stream_offset, fsb5.stream_size, 0, 0,0); /* samples look ok */
break; break;
} }
#endif #endif
@ -375,7 +390,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : 4); /* observed default */ cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : 4); /* observed default */
vgmstream->codec_data = init_mpeg_custom(sf, fsb5.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg); vgmstream->codec_data = init_mpeg_custom(sb, fsb5.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
break; break;
@ -387,7 +402,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
fsb5.layers = (fsb5.channels <= 2) ? 1 : (fsb5.channels+1) / 2; fsb5.layers = (fsb5.channels <= 2) ? 1 : (fsb5.channels+1) / 2;
if (fsb5.layers > 1) { if (fsb5.layers > 1) {
vgmstream->layout_data = build_layered_fsb5(sf, &fsb5); vgmstream->layout_data = build_layered_fsb5(sf, sb, &fsb5);
if (!vgmstream->layout_data) goto fail; if (!vgmstream->layout_data) goto fail;
vgmstream->coding_type = coding_CELT_FSB; vgmstream->coding_type = coding_CELT_FSB;
vgmstream->layout_type = layout_layered; vgmstream->layout_type = layout_layered;
@ -415,7 +430,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
if (fsb5.layers > 1) { if (fsb5.layers > 1) {
/* multichannel made of various layers [Little Big Planet (Vita)] */ /* multichannel made of various layers [Little Big Planet (Vita)] */
vgmstream->layout_data = build_layered_fsb5(sf, &fsb5); vgmstream->layout_data = build_layered_fsb5(sf, sb, &fsb5);
if (!vgmstream->layout_data) goto fail; if (!vgmstream->layout_data) goto fail;
vgmstream->coding_type = coding_ATRAC9; vgmstream->coding_type = coding_ATRAC9;
vgmstream->layout_type = layout_layered; vgmstream->layout_type = layout_layered;
@ -449,7 +464,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
/* XWMA encoder only does up to 6ch (doesn't use FSB multistreams for more) */ /* XWMA encoder only does up to 6ch (doesn't use FSB multistreams for more) */
bytes = ffmpeg_make_riff_xwma(buf,0x100, format, fsb5.stream_size, vgmstream->channels, vgmstream->sample_rate, average_bps, block_align); bytes = ffmpeg_make_riff_xwma(buf,0x100, format, fsb5.stream_size, vgmstream->channels, vgmstream->sample_rate, average_bps, block_align);
vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, fsb5.stream_offset,fsb5.stream_size); vgmstream->codec_data = init_ffmpeg_header_offset(sb, buf,bytes, fsb5.stream_offset, fsb5.stream_size);
if ( !vgmstream->codec_data ) goto fail; if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
@ -465,7 +480,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
cfg.sample_rate = fsb5.sample_rate; cfg.sample_rate = fsb5.sample_rate;
cfg.setup_id = read_u32le(fsb5.extradata_offset,sf); cfg.setup_id = read_u32le(fsb5.extradata_offset,sf);
vgmstream->codec_data = init_vorbis_custom(sf, fsb5.stream_offset, VORBIS_FSB, &cfg); vgmstream->codec_data = init_vorbis_custom(sb, fsb5.stream_offset, VORBIS_FSB, &cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_VORBIS_custom; vgmstream->coding_type = coding_VORBIS_custom;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
@ -479,13 +494,13 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
vgmstream->interleave_block_size = 0x8c; vgmstream->interleave_block_size = 0x8c;
break; break;
#if 0 //disabled until some game is found, can be created in the GUI tool #if 0 //disabled until some game is found, can be created in the GUI tool
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
case 0x11: { /* FMOD_SOUND_FORMAT_OPUS */ case 0x11: { /* FMOD_SOUND_FORMAT_OPUS */
int skip = 312; //fsb_opus_get_encoder_delay(fsb5.stream_offset, sf); /* returns 120 but this seems correct */ int skip = 312; //fsb_opus_get_encoder_delay(fsb5.stream_offset, sb); /* returns 120 but this seems correct */
//vgmstream->num_samples -= skip; //vgmstream->num_samples -= skip;
vgmstream->codec_data = init_ffmpeg_fsb_opus(sf, fsb5.stream_offset, fsb5.stream_size, vgmstream->channels, skip, vgmstream->sample_rate); vgmstream->codec_data = init_ffmpeg_fsb_opus(sb, fsb5.stream_offset, fsb5.stream_size, vgmstream->channels, skip, vgmstream->sample_rate);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
@ -498,18 +513,20 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) {
goto fail; goto fail;
} }
if (!vgmstream_open_stream(vgmstream,sf,fsb5.stream_offset)) if (!vgmstream_open_stream(vgmstream, sb, fsb5.stream_offset))
goto fail; goto fail;
if (sb != sf) close_streamfile(sb);
return vgmstream; return vgmstream;
fail: fail:
if (sb != sf) close_streamfile(sb);
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }
static layered_layout_data* build_layered_fsb5(STREAMFILE* sf, fsb5_header* fsb5) { static layered_layout_data* build_layered_fsb5(STREAMFILE* sf, STREAMFILE* sb, fsb5_header* fsb5) {
layered_layout_data* data = NULL; layered_layout_data* data = NULL;
STREAMFILE* temp_sf = NULL; STREAMFILE* temp_sf = NULL;
size_t interleave, config = 0; size_t interleave, config = 0;
@ -526,9 +543,9 @@ static layered_layout_data* build_layered_fsb5(STREAMFILE* sf, fsb5_header* fsb5
/* 2ch+2ch..+1ch or 2ch+2ch..+2ch = check last layer */ /* 2ch+2ch..+1ch or 2ch+2ch..+2ch = check last layer */
layer_channels = (i+1 == fsb5->layers && fsb5->channels % 2 == 1) ? 1 : 2; layer_channels = (i+1 == fsb5->layers && fsb5->channels % 2 == 1) ? 1 : 2;
if (read_u32be(fsb5->stream_offset+0x00,sf) != 0x17C30DF3) /* FSB CELT frame ID */ if (read_u32be(fsb5->stream_offset+0x00,sb) != 0x17C30DF3) /* FSB CELT frame ID */
goto fail; goto fail;
interleave = 0x04+0x04+read_u32le(fsb5->stream_offset+0x04,sf); /* frame size */ interleave = 0x04+0x04+read_u32le(fsb5->stream_offset+0x04,sb); /* frame size */
//todo unknown interleave for max quality odd channel streams (found in test files) //todo unknown interleave for max quality odd channel streams (found in test files)
/* FSB5 odd channels use 2ch+2ch...+1ch streams, and the last only goes up to 0x17a, and other /* FSB5 odd channels use 2ch+2ch...+1ch streams, and the last only goes up to 0x17a, and other
@ -609,7 +626,7 @@ static layered_layout_data* build_layered_fsb5(STREAMFILE* sf, fsb5_header* fsb5
} }
temp_sf = setup_fsb5_streamfile(sf, fsb5->stream_offset, fsb5->stream_size, fsb5->layers, i, interleave); temp_sf = setup_fsb5_streamfile(sb, fsb5->stream_offset, fsb5->stream_size, fsb5->layers, i, interleave);
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
if (!vgmstream_open_stream(data->layers[i], temp_sf, 0x00)) if (!vgmstream_open_stream(data->layers[i], temp_sf, 0x00))

View File

@ -128,7 +128,7 @@ VGMSTREAM* init_vgmstream_fsb5_fev_bank(STREAMFILE* sf) {
} }
} }
//;VGM_LOG("FSB5 FEV: offset=%lx, size=%x\n", subfile_offset,subfile_size); //;VGM_LOG("FSB5 FEV: offset=%lx, size=%x\n", subfile_offset,subfile_size);
temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, "fsb"); temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, "fsb");
if (!temp_sf) goto fail; if (!temp_sf) goto fail;

View File

@ -23,8 +23,8 @@ typedef struct {
uint32_t channel_layout; uint32_t channel_layout;
int is_external; int is_external;
off_t stream_offsets[MAX_CHANNELS]; uint32_t stream_offsets[MAX_CHANNELS];
size_t stream_sizes[MAX_CHANNELS]; uint32_t stream_sizes[MAX_CHANNELS];
off_t sound_name_offset; off_t sound_name_offset;
off_t config_name_offset; off_t config_name_offset;

View File

@ -1,187 +1,192 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../util.h" #include "../util.h"
static int smacker_get_info(STREAMFILE *streamFile, int target_subsong, int * out_total_streams, size_t *out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples); static int smacker_get_info(STREAMFILE* sf, int target_subsong, int* p_total_subsongs, size_t* p_stream_size, int* p_channels, int* p_sample_rate, int* p_num_samples);
/* SMK - RAD Game Tools Smacker movies (audio/video format) */ /* SMK - RAD Game Tools older Smacker movies (audio/video format) */
VGMSTREAM * init_vgmstream_smk(STREAMFILE *streamFile) { VGMSTREAM* init_vgmstream_smk(STREAMFILE *sf) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM* vgmstream = NULL;
int channel_count = 0, loop_flag = 0, sample_rate = 0, num_samples = 0; int channels = 0, loop_flag = 0, sample_rate = 0, num_samples = 0;
int total_subsongs = 0, target_subsong = streamFile->stream_index; int total_subsongs = 0, target_subsong = sf->stream_index;
size_t stream_size; size_t stream_size;
/* checks */ /* checks */
if (!check_extensions(streamFile,"smk")) if (!check_extensions(sf,"smk"))
goto fail; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x534D4B32 && /* "SMK2" */ if (!is_id32be(0x00,sf, "SMK2") &&
read_32bitBE(0x00,streamFile) != 0x534D4B34) /* "SMK4" */ !is_id32be(0x00,sf, "SMK4"))
goto fail; goto fail;
/* find target stream info */ /* find target stream info */
if (!smacker_get_info(streamFile, target_subsong, &total_subsongs, &stream_size, &channel_count, &sample_rate, &num_samples)) if (!smacker_get_info(sf, target_subsong, &total_subsongs, &stream_size, &channels, &sample_rate, &num_samples))
goto fail; goto fail;
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag); vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_SMACKER;
vgmstream->sample_rate = sample_rate; vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples; vgmstream->num_samples = num_samples;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size; vgmstream->num_streams = total_subsongs;
vgmstream->meta_type = meta_SMACKER; vgmstream->stream_size = stream_size;
{ {
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
/* target_subsong should be passed manually */ /* target_subsong should be passed manually */
vgmstream->codec_data = init_ffmpeg_header_offset_subsong(streamFile, NULL,0, 0x0,get_streamfile_size(streamFile), target_subsong); vgmstream->codec_data = init_ffmpeg_header_offset_subsong(sf, NULL,0, 0x00, 0, target_subsong);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
#else vgmstream->layout_type = layout_none;
goto fail; #else
#endif goto fail;
} #endif
}
return vgmstream;
return vgmstream;
fail:
close_vgmstream(vgmstream); fail:
return NULL; close_vgmstream(vgmstream);
} return NULL;
}
typedef enum {
SMK_AUDIO_PACKED = (1<<7), typedef enum {
SMK_AUDIO_PRESENT = (1<<6), SMK_AUDIO_PACKED = (1<<7),
SMK_AUDIO_16BITS = (1<<5), SMK_AUDIO_PRESENT = (1<<6),
SMK_AUDIO_STEREO = (1<<4), SMK_AUDIO_16BITS = (1<<5),
SMK_AUDIO_BINK_RDFT = (1<<3), SMK_AUDIO_STEREO = (1<<4),
SMK_AUDIO_BINK_DCT = (1<<2), SMK_AUDIO_BINK_RDFT = (1<<3),
//SMK_AUD_UNUSED1 = (1<<1), SMK_AUDIO_BINK_DCT = (1<<2),
//SMK_AUD_UNUSED0 = (1<<0), //SMK_AUD_UNUSED1 = (1<<1),
} smk_audio_flag; //SMK_AUD_UNUSED0 = (1<<0),
} smk_audio_flag;
//todo test multilang streams and codecs other than SMACKAUD
/* Gets stream info, and number of samples in a file by reading frames //todo test multilang streams and codecs other than SMACKAUD
* info: https://wiki.multimedia.cx/index.php/Smacker */ /* Gets stream info, and number of samples in a file by reading frames
static int smacker_get_info(STREAMFILE *sf, int target_subsong, int * out_total_subsongs, size_t * out_stream_size, int * out_channel_count, int * out_sample_rate, int * out_num_samples) { * info: https://wiki.multimedia.cx/index.php/Smacker */
STREAMFILE *sf_index = NULL; static int smacker_get_info(STREAMFILE* sf, int target_subsong, int* out_total_subsongs, size_t* p_stream_size, int* p_channels, int* p_sample_rate, int* p_num_samples) {
uint32_t flags, total_frames, trees_sizes; STREAMFILE* sf_index = NULL;
off_t size_offset, type_offset, data_offset; uint32_t flags, total_frames, trees_sizes;
int i, j, sample_rate = 0, channel_count = 0, num_samples = 0; off_t size_offset, type_offset, data_offset;
int total_subsongs, target_stream = 0; int i, j, sample_rate = 0, channels = 0, num_samples = 0;
size_t stream_size = 0; int total_subsongs, target_stream = 0;
uint8_t stream_flags = 0; size_t stream_size = 0;
uint8_t stream_flags = 0;
/* rough format:
* - header (id, frames, video info/config, audio info) /* rough format:
* - frame sizes table * - header (id, frames, video info/config, audio info)
* - frame info table * - frame sizes table
* - huffman trees * - frame info table
* - frame data * - huffman trees
*/ * - frame data
*/
/* read header */
total_frames = read_u32le(0x0c,sf); /* read header */
if (total_frames <= 0 || total_frames > 0x100000) goto fail; /* something must be off */ total_frames = read_u32le(0x0c,sf);
if (total_frames <= 0 || total_frames > 0x100000) goto fail; /* something must be off */
flags = read_u32le(0x14,sf);
if (flags & 1) /* extra "ring frame" */ flags = read_u32le(0x14,sf);
total_frames += 1; if (flags & 1) /* extra "ring frame" */
total_frames += 1;
trees_sizes = read_u32le(0x34,sf);
trees_sizes = read_u32le(0x34,sf);
if (target_subsong == 0) target_subsong = 1;
total_subsongs = 0; if (target_subsong == 0) target_subsong = 1;
for (i = 0; i < 7; i++) { /* up to 7 audio (multilang?) streams */ total_subsongs = 0;
uint32_t audio_info = read_u32le(0x48 + 0x04*i,sf); for (i = 0; i < 7; i++) { /* up to 7 audio (multilang?) streams */
uint8_t audio_flags = (audio_info >> 24) & 0xFF; uint32_t audio_info = read_u32le(0x48 + 0x04*i,sf);
int audio_rate = audio_info & 0x00FFFFFF; uint8_t audio_flags = (audio_info >> 24) & 0xFF;
int audio_rate = audio_info & 0x00FFFFFF;
if (audio_flags & SMK_AUDIO_PRESENT) {
total_subsongs++; if (audio_flags & SMK_AUDIO_PRESENT) {
total_subsongs++;
if (target_subsong == total_subsongs) {
target_stream = i; if (target_subsong == total_subsongs) {
sample_rate = audio_rate & 0x00FFFFFF; target_stream = i;
channel_count = (audio_flags & SMK_AUDIO_STEREO) ? 2 : 1; sample_rate = audio_rate & 0x00FFFFFF;
stream_flags = audio_flags; channels = (audio_flags & SMK_AUDIO_STEREO) ? 2 : 1;
} stream_flags = audio_flags;
} }
} }
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; }
if (sample_rate == 0 || channel_count == 0) goto fail; if (total_subsongs < 1) {
vgm_logi("SMK: no audio in movie (ignore)\n");
/* read size and type tables into buffer */ goto fail;
sf_index = reopen_streamfile(sf, total_frames*0x04 + total_frames*0x01); }
if (!sf_index) goto fail; if (target_subsong < 0 || target_subsong > total_subsongs) goto fail;
if (sample_rate == 0 || channels == 0) goto fail;
/* read frames and sum all samples, since some codecs are VBR */
size_offset = 0x68; /* read size and type tables into buffer */
type_offset = size_offset + total_frames*0x04; sf_index = reopen_streamfile(sf, total_frames*0x04 + total_frames*0x01);
data_offset = type_offset + total_frames*0x01 + trees_sizes; if (!sf_index) goto fail;
for (i=0; i < total_frames; i++) {
uint32_t frame_size = read_u32le(size_offset,sf_index) & 0xFFFFFFFC; /* last 2 bits are keyframe flags */ /* read frames and sum all samples, since some codecs are VBR */
uint8_t frame_type = read_u8 (type_offset,sf_index); /* 0: has palette, 1..7: has stream N) */ size_offset = 0x68;
off_t offset = data_offset; type_offset = size_offset + total_frames*0x04;
data_offset = type_offset + total_frames*0x01 + trees_sizes;
/* skip palette */ for (i = 0; i < total_frames; i++) {
if (frame_type & (1<<0)) { uint32_t frame_size = read_u32le(size_offset,sf_index) & 0xFFFFFFFC; /* last 2 bits are keyframe flags */
uint8_t palette_size = read_u8(offset,sf); uint8_t frame_type = read_u8 (type_offset,sf_index); /* 0: has palette, 1..7: has stream N) */
offset += palette_size * 4; off_t offset = data_offset;
}
/* skip palette */
/* read target audio packet and ignore rest (though probably all streams are the same) */ if (frame_type & (1<<0)) {
for (j = 0; j < 7; j++) { uint8_t palette_size = read_u8(offset,sf);
uint32_t audio_size; offset += palette_size * 4;
}
/* check if stream N exists in this frame (supposedly streams can be go in separate frames) */
if ( !(frame_type & (1<<(j+1))) ) /* read target audio packet and ignore rest (though probably all streams are the same) */
continue; for (j = 0; j < 7; j++) {
uint32_t audio_size;
audio_size = read_u32le(offset,sf);
/* check if stream N exists in this frame (supposedly streams can be go in separate frames) */
if (j == target_stream) { if ( !(frame_type & (1<<(j+1))) )
continue;
/* resulting PCM bytes to samples */
if (stream_flags & SMK_AUDIO_PACKED) { /* Smacker and maybe Bink codecs */ audio_size = read_u32le(offset,sf);
uint32_t unpacked_size = read_u32le(offset+0x04,sf);
num_samples += unpacked_size / (0x02 * channel_count); if (j == target_stream) {
}
else if (stream_flags & SMK_AUDIO_16BITS) { /* PCM16 */ /* resulting PCM bytes to samples */
num_samples += (audio_size - 0x04) / (0x02 * channel_count); if (stream_flags & SMK_AUDIO_PACKED) { /* Smacker and maybe Bink codecs */
} uint32_t unpacked_size = read_u32le(offset+0x04,sf);
else { /* PCM8 */ num_samples += unpacked_size / (0x02 * channels);
num_samples += (audio_size - 0x04) / (0x01 * channel_count); }
} else if (stream_flags & SMK_AUDIO_16BITS) { /* PCM16 */
} num_samples += (audio_size - 0x04) / (0x02 * channels);
}
stream_size += audio_size; else { /* PCM8 */
offset += audio_size; num_samples += (audio_size - 0x04) / (0x01 * channels);
} }
}
/* rest is video packet (size = offset - data_offset) */
stream_size += audio_size;
size_offset += 0x04; offset += audio_size;
type_offset += 0x01; }
data_offset += frame_size;
} /* rest is video packet (size = offset - data_offset) */
if (out_total_subsongs) *out_total_subsongs = total_subsongs; size_offset += 0x04;
if (out_stream_size) *out_stream_size = stream_size; type_offset += 0x01;
if (out_sample_rate) *out_sample_rate = sample_rate; data_offset += frame_size;
if (out_channel_count) *out_channel_count = channel_count; }
if (out_num_samples) *out_num_samples = num_samples;
if (out_total_subsongs) *out_total_subsongs = total_subsongs;
close_streamfile(sf_index); if (p_stream_size) *p_stream_size = stream_size;
return 1; if (p_sample_rate) *p_sample_rate = sample_rate;
if (p_channels) *p_channels = channels;
fail: if (p_num_samples) *p_num_samples = num_samples;
close_streamfile(sf_index);
return 0; close_streamfile(sf_index);
} return 1;
fail:
close_streamfile(sf_index);
return 0;
}

View File

@ -8,7 +8,7 @@ VGMSTREAM* init_vgmstream_wii_bns(STREAMFILE* sf) {
off_t bns_offset; off_t bns_offset;
uint32_t info_offset = 0, data_offset = 0; uint32_t info_offset = 0, data_offset = 0;
uint32_t channel_info_offset_list_offset; uint32_t channel_info_offset_list_offset;
int channels, loop_flag, sample_rate; int channels, loop_flag, sample_rate;
uint32_t sample_count, loop_start; uint32_t sample_count, loop_start;
/* checks */ /* checks */
@ -106,7 +106,7 @@ VGMSTREAM* init_vgmstream_wii_bns(STREAMFILE* sf) {
channel_info_offset_list_offset = info_offset + read_u32be(info_offset+0x10,sf); channel_info_offset_list_offset = info_offset + read_u32be(info_offset+0x10,sf);
} }
/* build the VGMSTREAM */ /* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag); vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;

View File

@ -510,7 +510,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
if (ww.bits_per_sample != 16) goto fail; if (ww.bits_per_sample != 16) goto fail;
/* original data_size doesn't look related to samples or anything */ /* original data_size doesn't look related to samples or anything */
ww.data_size = ww.file_size - ww.data_offset; ww.data_size = ww.file_size - ww.data_offset;
vgmstream->codec_data = init_ffmpeg_offset(sf, ww.data_offset, ww.data_size); vgmstream->codec_data = init_ffmpeg_offset(sf, ww.data_offset, ww.data_size);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
@ -682,7 +682,7 @@ static int is_dsp_full_interleave(STREAMFILE* sf, wwise_header* ww, off_t coef_o
* - .bnk would be memory banks = full * - .bnk would be memory banks = full
* - otherwise small-ish sizes, stereo, with initial predictors for the * - otherwise small-ish sizes, stereo, with initial predictors for the
* second channel matching half size = full * second channel matching half size = full
* some files aren't detectable like this though, when predictors are 0 * some files aren't detectable like this though, when predictors are 0
* (but since memory wem aren't that used this shouldn't be too common) */ * (but since memory wem aren't that used this shouldn't be too common) */
if (ww->truncated) if (ww->truncated)
@ -960,7 +960,7 @@ fail:
0x14 (4): LoopInfo.dwLoopEndPacketOffset? 0x14 (4): LoopInfo.dwLoopEndPacketOffset?
0x18 (4): dwSeekTableSize (0 = no seek table) 0x18 (4): dwSeekTableSize (0 = no seek table)
0x1c (4): dwVorbisDataOffset (offset within data) 0x1c (4): dwVorbisDataOffset (offset within data)
0x20 (2): uMaxPacketSize (not including header) 0x20 (2): uMaxPacketSize (not including header)
0x22 (2): uLastGranuleExtra (0..~0x100) 0x22 (2): uLastGranuleExtra (0..~0x100)
0x24 (4): dwDecodeAllocSize (0~0x5000) 0x24 (4): dwDecodeAllocSize (0~0x5000)
0x28 (4): dwDecodeX64AllocSize (mid, 0~0x5000) 0x28 (4): dwDecodeX64AllocSize (mid, 0~0x5000)
@ -976,7 +976,7 @@ fail:
0x0c (2): ? (small, 0..~0x400) [(4) when size is 0x2C] 0x0c (2): ? (small, 0..~0x400) [(4) when size is 0x2C]
0x10 (4): dwSeekTableSize (0 = no seek table) 0x10 (4): dwSeekTableSize (0 = no seek table)
0x14 (4): dwVorbisDataOffset (offset within data) 0x14 (4): dwVorbisDataOffset (offset within data)
0x18 (2): uMaxPacketSize (not including header) 0x18 (2): uMaxPacketSize (not including header)
0x1a (2): uLastGranuleExtra (0..~0x100) [(4) when size is 0x2C] 0x1a (2): uLastGranuleExtra (0..~0x100) [(4) when size is 0x2C]
0x1c (4): dwDecodeAllocSize (0~0x5000) 0x1c (4): dwDecodeAllocSize (0~0x5000)
0x20 (4): dwDecodeX64AllocSize (0~0x5000) 0x20 (4): dwDecodeX64AllocSize (0~0x5000)
@ -993,7 +993,7 @@ fail:
0x26 (2): LoopInfo.uLoopEndExtra (extra samples after seek?) 0x26 (2): LoopInfo.uLoopEndExtra (extra samples after seek?)
0x28 (4): dwSeekTableSize (0 = no seek table) 0x28 (4): dwSeekTableSize (0 = no seek table)
0x2c (4): dwVorbisDataOffset (offset within data) 0x2c (4): dwVorbisDataOffset (offset within data)
0x30 (2): uMaxPacketSize (not including header) 0x30 (2): uMaxPacketSize (not including header)
0x32 (2): uLastGranuleExtra (small, 0..~0x100) 0x32 (2): uLastGranuleExtra (small, 0..~0x100)
0x34 (4): dwDecodeAllocSize (mid, 0~0x5000) 0x34 (4): dwDecodeAllocSize (mid, 0~0x5000)
0x38 (4): dwDecodeX64AllocSize (mid, 0~0x5000) 0x38 (4): dwDecodeX64AllocSize (mid, 0~0x5000)

View File

@ -190,7 +190,7 @@ static int lz4mg_decompress(lz4mg_stream_t* strm) {
default: default:
goto fail; goto fail;
} }
} }
buffer_end: buffer_end:
strm->next_out += dst_pos; strm->next_out += dst_pos;
@ -208,16 +208,16 @@ fail:
#if 0 #if 0
/* non-streamed form for reference, assumes buffers are big enough */ /* non-streamed form for reference, assumes buffers are big enough */
static void decompress_lz4mg(uint8_t* dst, size_t dst_size, const uint8_t* src, size_t src_size) { static void decompress_lz4mg(uint8_t* dst, size_t dst_size, const uint8_t* src, size_t src_size) {
size_t src_pos = 0; size_t src_pos = 0;
size_t dst_pos = 0; size_t dst_pos = 0;
uint8_t token; uint8_t token;
int literal_len, match_len, next_len; int literal_len, match_len, next_len;
int match_pos, match_offset; int match_pos, match_offset;
int i; int i;
while (src_pos < src_size && dst_pos < dst_size) { while (src_pos < src_size && dst_pos < dst_size) {
token = src[src_pos++]; token = src[src_pos++];
if (src_pos > src_size) if (src_pos > src_size)
break; break;
@ -259,7 +259,7 @@ static void decompress_lz4mg(uint8_t* dst, size_t dst_size, const uint8_t* src,
for(i = 0; i < match_len; i++) { for(i = 0; i < match_len; i++) {
dst[dst_pos++] = dst[match_pos++]; /* note RLE with short offsets */ dst[dst_pos++] = dst[match_pos++]; /* note RLE with short offsets */
} }
} }
} }
#endif #endif

View File

@ -26,8 +26,8 @@ typedef struct {
off_t stream_offset; off_t stream_offset;
} xvag_header; } xvag_header;
static int init_xvag_atrac9(STREAMFILE* sf, VGMSTREAM* vgmstream, xvag_header * xvag, off_t chunk_offset); static int init_xvag_atrac9(STREAMFILE* sf, VGMSTREAM* vgmstream, xvag_header* xvag, off_t chunk_offset);
static layered_layout_data* build_layered_xvag(STREAMFILE* sf, xvag_header * xvag, off_t chunk_offset, off_t start_offset); static layered_layout_data* build_layered_xvag(STREAMFILE* sf, 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) */ /* 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* sf) { VGMSTREAM* init_vgmstream_xvag(STREAMFILE* sf) {
@ -42,7 +42,7 @@ VGMSTREAM* init_vgmstream_xvag(STREAMFILE* sf) {
/* checks */ /* checks */
/* .xvag: standard /* .xvag: standard
* (extensionless): The Last Of Us (PS3) speech files */ * (extensionless): The Last of Us (PS3) speech files */
if (!check_extensions(sf,"xvag,")) if (!check_extensions(sf,"xvag,"))
goto fail; goto fail;
if (!is_id32be(0x00,sf, "XVAG")) if (!is_id32be(0x00,sf, "XVAG"))
@ -254,7 +254,7 @@ fail:
} }
#ifdef VGM_USE_ATRAC9 #ifdef VGM_USE_ATRAC9
static int init_xvag_atrac9(STREAMFILE* sf, VGMSTREAM* vgmstream, xvag_header * xvag, off_t chunk_offset) { static int init_xvag_atrac9(STREAMFILE* sf, VGMSTREAM* vgmstream, xvag_header* xvag, off_t chunk_offset) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = xvag->big_endian ? read_32bitBE : read_32bitLE; int32_t (*read_32bit)(off_t,STREAMFILE*) = xvag->big_endian ? read_32bitBE : read_32bitLE;
atrac9_config cfg = {0}; atrac9_config cfg = {0};
@ -265,7 +265,7 @@ static int init_xvag_atrac9(STREAMFILE* sf, VGMSTREAM* vgmstream, xvag_header *
/* 0x08: data size (layer only) */ /* 0x08: data size (layer only) */
/* 0x10: decoder delay? */ /* 0x10: decoder delay? */
cfg.encoder_delay = read_32bit(chunk_offset+0x14,sf); cfg.encoder_delay = read_32bit(chunk_offset+0x14,sf);
/* sometimes ATRAC9 data starts with a fake RIFF, that has total channels rather than layer channels */ /* sometimes ATRAC9 data starts with a fake RIFF, that has total channels rather than layer channels */
vgmstream->codec_data = init_atrac9(&cfg); vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
@ -278,7 +278,7 @@ fail:
} }
#endif #endif
static layered_layout_data* build_layered_xvag(STREAMFILE* sf, xvag_header * xvag, off_t chunk_offset, off_t start_offset) { static layered_layout_data* build_layered_xvag(STREAMFILE* sf, xvag_header* xvag, off_t chunk_offset, off_t start_offset) {
layered_layout_data* data = NULL; layered_layout_data* data = NULL;
STREAMFILE* temp_sf = NULL; STREAMFILE* temp_sf = NULL;
int32_t (*read_32bit)(off_t,STREAMFILE*) = xvag->big_endian ? read_32bitBE : read_32bitLE; int32_t (*read_32bit)(off_t,STREAMFILE*) = xvag->big_endian ? read_32bitBE : read_32bitLE;
@ -309,7 +309,7 @@ static layered_layout_data* build_layered_xvag(STREAMFILE* sf, xvag_header * xva
if (!init_xvag_atrac9(sf, data->layers[i], xvag, chunk_offset)) if (!init_xvag_atrac9(sf, data->layers[i], xvag, chunk_offset))
goto fail; goto fail;
/* interleaves N layers for custom multichannel, may rarely use subsongs [Days Gone (PS4) multilayer test] /* interleaves N layers for custom multichannel, may rarely use subsongs [Days Gone (PS4) multilayer test]
* ex. 2 layers, 1 subsong : [L1][L2][L1][L2] * ex. 2 layers, 1 subsong : [L1][L2][L1][L2]
* ex. 2 layers, 2 subsongs: [L1S1][L2S1][L1S2][L2S2] (assumed, could be [L1S1][L1S2][L2S1][L2S2]) */ * ex. 2 layers, 2 subsongs: [L1S1][L2S1][L1S2][L2S2] (assumed, could be [L1S1][L1S2][L2S1][L2S2]) */
chunk = i + xvag->subsongs * (xvag->target_subsong - 1); /* [L1S1][L2S1][L1S2][L2S2] */ chunk = i + xvag->subsongs * (xvag->target_subsong - 1); /* [L1S1][L2S1][L1S2][L2S2] */

View File

@ -518,7 +518,7 @@ static void seek_force_loop(VGMSTREAM* vgmstream, int loop_count) {
return; return;
/* pretend decoder reached loop end so state is set to loop start */ /* pretend decoder reached loop end so state is set to loop start */
vgmstream->loop_count = loop_count - 1; /* seeking to first loop musy become ++ > 0 */ vgmstream->loop_count = loop_count - 1; /* seeking to first loop must become ++ > 0 */
vgmstream->current_sample = vgmstream->loop_end_sample; vgmstream->current_sample = vgmstream->loop_end_sample;
vgmstream_do_loop(vgmstream); vgmstream_do_loop(vgmstream);
} }

File diff suppressed because it is too large Load Diff

View File

@ -19,40 +19,34 @@
/* MSVC fixes (though mingw uses MSVCRT but not MSC_VER, maybe use AND?) */ /* MSVC fixes (though mingw uses MSVCRT but not MSC_VER, maybe use AND?) */
#if defined(__MSVCRT__) || defined(_MSC_VER) #if defined(__MSVCRT__) || defined(_MSC_VER)
#include <io.h> #include <io.h>
#ifndef fseeko #ifndef off64_t
#define fseeko fseek #define off_t __int64
#endif #endif
#ifndef ftello
#define ftello ftell
#endif
#define dup _dup
#ifdef fileno
#undef fileno
#endif
#define fileno _fileno
#define fdopen _fdopen
// #ifndef off64_t
// #define off_t __int64
// #endif
#endif
#if defined(XBMC)
#define fseeko fseek
#endif #endif
#ifndef DIR_SEPARATOR #ifndef DIR_SEPARATOR
#if defined (_WIN32) || defined (WIN32) #if defined (_WIN32) || defined (WIN32)
#define DIR_SEPARATOR '\\' #define DIR_SEPARATOR '\\'
#else #else
#define DIR_SEPARATOR '/' #define DIR_SEPARATOR '/'
#endif #endif
#endif #endif
/* 64-bit offset is needed for banks that hit +2.5GB (like .fsb or .ktsl2stbin).
* Leave as typedef to toggle since it's theoretically slower when compiled as 32-bit.
* ATM it's only used in choice places until more performance tests are done.
* uint32_t could be an option but needs to test when/how neg offsets are used.
*
* On POSIX 32-bit off_t can become off64_t by passing -D_FILE_OFFSET_BITS=64,
* but not on MSVC as it doesn't have proper POSIX support, so a custom type is needed.
* fseeks/tells also need to be adjusted for 64-bit support.
*/
typedef int64_t offv_t; //off64_t
//typedef int64_t sizev_t; // size_t int64_t off64_t
/* Streamfiles normally use an internal buffer to increase performance, configurable /* Streamfiles normally use an internal buffer to increase performance, configurable
* but usually of this size. Lower increases the number of freads/system calls (slower). * but usually of this size. Lower increases the number of freads/system calls (slower).
* However some formats need to jump around causing more buffer trashing than usual, * However some formats need to jump around causing more buffer trashing than usual,
@ -65,17 +59,26 @@
* to do file operations, as plugins may need to provide their own callbacks. * to do file operations, as plugins may need to provide their own callbacks.
* Reads from arbitrary offsets, meaning internally may need fseek equivalents during reads. */ * Reads from arbitrary offsets, meaning internally may need fseek equivalents during reads. */
typedef struct _STREAMFILE { typedef struct _STREAMFILE {
size_t (*read)(struct _STREAMFILE*, uint8_t* dst, off_t offset, size_t length); /* read 'length' data at 'offset' to 'dst' */
size_t (*get_size)(struct _STREAMFILE*); size_t (*read)(struct _STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length);
off_t (*get_offset)(struct _STREAMFILE*); //todo: DO NOT USE, NOT RESET PROPERLY (remove?)
/* for dual-file support */ /* get max offset */
void (*get_name)(struct _STREAMFILE*, char* name, size_t length); size_t (*get_size)(struct _STREAMFILE* sf);
struct _STREAMFILE* (*open)(struct _STREAMFILE*, const char* const filename, size_t buffersize);
//todo: DO NOT USE, NOT RESET PROPERLY (remove?)
offv_t (*get_offset)(struct _STREAMFILE*);
/* copy current filename to name buf */
void (*get_name)(struct _STREAMFILE* sf, char* name, size_t name_size);
/* open another streamfile from filename */
struct _STREAMFILE* (*open)(struct _STREAMFILE* sf, const char* const filename, size_t buffer_size);
/* free current STREAMFILE */
void (*close)(struct _STREAMFILE*); void (*close)(struct _STREAMFILE*);
/* Substream selection for formats with subsongs.
/* Substream selection for files with subsongs. Manually used in metas if supported. * Not ideal here, but it was the simplest way to pass to all init_vgmstream_x functions. */
* Not ideal here, but it's the simplest way to pass to all init_vgmstream_x functions. */
int stream_index; /* 0=default/auto (first), 1=first, N=Nth */ int stream_index; /* 0=default/auto (first), 1=first, N=Nth */
} STREAMFILE; } STREAMFILE;
@ -105,8 +108,8 @@ STREAMFILE* open_wrap_streamfile_f(STREAMFILE* sf);
/* Opens a STREAMFILE that clamps reads to a section of a larger streamfile. /* Opens a STREAMFILE that clamps reads to a section of a larger streamfile.
* Can be used with subfiles inside a bigger file (to fool metas, or to simplify custom IO). */ * Can be used with subfiles inside a bigger file (to fool metas, or to simplify custom IO). */
STREAMFILE* open_clamp_streamfile(STREAMFILE* sf, off_t start, size_t size); STREAMFILE* open_clamp_streamfile(STREAMFILE* sf, offv_t start, size_t size);
STREAMFILE* open_clamp_streamfile_f(STREAMFILE* sf, off_t start, size_t size); STREAMFILE* open_clamp_streamfile_f(STREAMFILE* sf, offv_t start, size_t size);
/* Opens a STREAMFILE that uses custom IO for streamfile reads. /* Opens a STREAMFILE that uses custom IO for streamfile reads.
* Can be used to modify data on the fly (ex. decryption), or even transform it from a format to another. * Can be used to modify data on the fly (ex. decryption), or even transform it from a format to another.
@ -156,8 +159,8 @@ static inline void close_streamfile(STREAMFILE* sf) {
} }
/* read from a file, returns number of bytes read */ /* read from a file, returns number of bytes read */
static inline size_t read_streamfile(uint8_t *dst, off_t offset, size_t length, STREAMFILE* sf) { static inline size_t read_streamfile(uint8_t* dst, offv_t offset, size_t length, STREAMFILE* sf) {
return sf->read(sf, dst, offset,length); return sf->read(sf, dst, offset, length);
} }
/* return file size */ /* return file size */

View File

@ -11,7 +11,7 @@
/* ************************************* */ /* ************************************* */
/* opens a utf16 (unicode) path */ /* opens a utf16 (unicode) path */
static FILE* wa_fopen(const in_char *wpath) { static FILE* wa_fopen(const in_char* wpath) {
#ifdef UNICODE_INPUT_PLUGIN #ifdef UNICODE_INPUT_PLUGIN
return _wfopen(wpath,L"rb"); return _wfopen(wpath,L"rb");
#else #else
@ -34,23 +34,23 @@ static FILE* wa_fdopen(int fd) {
/* a STREAMFILE that operates via STDIOSTREAMFILE but handles Winamp's unicode (in_char) paths */ /* a STREAMFILE that operates via STDIOSTREAMFILE but handles Winamp's unicode (in_char) paths */
typedef struct { typedef struct {
STREAMFILE sf; STREAMFILE vt;
STREAMFILE *stdiosf; STREAMFILE* stdiosf;
FILE *infile_ref; /* pointer to the infile in stdiosf (partially handled by stdiosf) */ FILE* infile_ref; /* pointer to the infile in stdiosf (partially handled by stdiosf) */
} WINAMP_STREAMFILE; } WINAMP_STREAMFILE;
static STREAMFILE *open_winamp_streamfile_by_file(FILE *infile, const char * path); static STREAMFILE* open_winamp_streamfile_by_file(FILE* infile, const char* path);
//static STREAMFILE *open_winamp_streamfile_by_ipath(const in_char *wpath); //static STREAMFILE* open_winamp_streamfile_by_ipath(const in_char* wpath);
static size_t wasf_read(WINAMP_STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length) { static size_t wasf_read(WINAMP_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) {
return sf->stdiosf->read(sf->stdiosf, dest, offset, length); return sf->stdiosf->read(sf->stdiosf, dst, offset, length);
} }
static off_t wasf_get_size(WINAMP_STREAMFILE* sf) { static size_t wasf_get_size(WINAMP_STREAMFILE* sf) {
return sf->stdiosf->get_size(sf->stdiosf); return sf->stdiosf->get_size(sf->stdiosf);
} }
static off_t wasf_get_offset(WINAMP_STREAMFILE* sf) { static offv_t wasf_get_offset(WINAMP_STREAMFILE* sf) {
return sf->stdiosf->get_offset(sf->stdiosf); return sf->stdiosf->get_offset(sf->stdiosf);
} }
@ -58,7 +58,7 @@ static void wasf_get_name(WINAMP_STREAMFILE* sf, char* buffer, size_t length) {
sf->stdiosf->get_name(sf->stdiosf, buffer, length); sf->stdiosf->get_name(sf->stdiosf, buffer, length);
} }
static STREAMFILE *wasf_open(WINAMP_STREAMFILE* sf, const char* const filename, size_t buffersize) { static STREAMFILE* wasf_open(WINAMP_STREAMFILE* sf, const char* const filename, size_t buffersize) {
in_char wpath[PATH_LIMIT]; in_char wpath[PATH_LIMIT];
if (!filename) if (!filename)
@ -77,7 +77,7 @@ static STREAMFILE *wasf_open(WINAMP_STREAMFILE* sf, const char* const filename,
FILE *new_file; FILE *new_file;
if (((new_fd = dup(fileno(sf->infile_ref))) >= 0) && (new_file = wa_fdopen(new_fd))) { if (((new_fd = dup(fileno(sf->infile_ref))) >= 0) && (new_file = wa_fdopen(new_fd))) {
STREAMFILE *new_sf = open_winamp_streamfile_by_file(new_file, filename); STREAMFILE* new_sf = open_winamp_streamfile_by_file(new_file, filename);
if (new_sf) if (new_sf)
return new_sf; return new_sf;
fclose(new_file); fclose(new_file);
@ -101,7 +101,7 @@ static void wasf_close(WINAMP_STREAMFILE* sf) {
free(sf); /* and the current struct */ free(sf); /* and the current struct */
} }
static STREAMFILE *open_winamp_streamfile_by_file(FILE* file, const char* path) { static STREAMFILE* open_winamp_streamfile_by_file(FILE* file, const char* path) {
WINAMP_STREAMFILE* this_sf = NULL; WINAMP_STREAMFILE* this_sf = NULL;
STREAMFILE* stdiosf = NULL; STREAMFILE* stdiosf = NULL;
@ -111,17 +111,17 @@ static STREAMFILE *open_winamp_streamfile_by_file(FILE* file, const char* path)
stdiosf = open_stdio_streamfile_by_file(file, path); stdiosf = open_stdio_streamfile_by_file(file, path);
if (!stdiosf) goto fail; if (!stdiosf) goto fail;
this_sf->sf.read = (void*)wasf_read; this_sf->vt.read = (void*)wasf_read;
this_sf->sf.get_size = (void*)wasf_get_size; this_sf->vt.get_size = (void*)wasf_get_size;
this_sf->sf.get_offset = (void*)wasf_get_offset; this_sf->vt.get_offset = (void*)wasf_get_offset;
this_sf->sf.get_name = (void*)wasf_get_name; this_sf->vt.get_name = (void*)wasf_get_name;
this_sf->sf.open = (void*)wasf_open; this_sf->vt.open = (void*)wasf_open;
this_sf->sf.close = (void*)wasf_close; this_sf->vt.close = (void*)wasf_close;
this_sf->stdiosf = stdiosf; this_sf->stdiosf = stdiosf;
this_sf->infile_ref = file; this_sf->infile_ref = file;
return &this_sf->sf; /* pointer to STREAMFILE start = rest of the custom data follows */ return &this_sf->vt;
fail: fail:
close_streamfile(stdiosf); close_streamfile(stdiosf);