mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-01 01:27:20 +01:00
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:
commit
dfa3779ad4
25
Makefile
25
Makefile
@ -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
|
||||||
|
13
README.md
13
README.md
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
13
make-build-cmake.sh
Executable 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
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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*/
|
||||||
|
@ -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);
|
||||||
|
@ -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) */
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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)
|
||||||
*/
|
*/
|
||||||
|
@ -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 */
|
||||||
|
@ -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;
|
||||||
|
@ -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 */
|
||||||
|
|
||||||
|
|
||||||
|
@ -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++) {
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
301
src/meta/bik.c
301
src/meta/bik.c
@ -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;
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
123
src/meta/fsb5.c
123
src/meta/fsb5.c
@ -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))
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
379
src/meta/smk.c
379
src/meta/smk.c
@ -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;
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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] */
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
852
src/streamfile.c
852
src/streamfile.c
File diff suppressed because it is too large
Load Diff
@ -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 */
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user