mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-17 23:36:41 +01:00
commit
15362e0b59
40
README.md
40
README.md
@ -18,6 +18,8 @@ Help and newest builds can be found here: https://www.hcs64.com/
|
|||||||
|
|
||||||
Latest development is usually here: https://github.com/losnoco/vgmstream/
|
Latest development is usually here: https://github.com/losnoco/vgmstream/
|
||||||
|
|
||||||
|
You can find further info about other details in https://github.com/losnoco/vgmstream/tree/master/doc
|
||||||
|
|
||||||
## Needed extra files (for Windows)
|
## Needed extra files (for Windows)
|
||||||
Support for some codecs (Ogg Vorbis, MPEG audio, etc) is done with external
|
Support for some codecs (Ogg Vorbis, MPEG audio, etc) is done with external
|
||||||
libraries, so you will need to have certain DLL files.
|
libraries, so you will need to have certain DLL files.
|
||||||
@ -307,6 +309,44 @@ named and that filenames inside match the song filename. For Winamp you need
|
|||||||
to make sure options > titles > advanced title formatting checkbox is set and
|
to make sure options > titles > advanced title formatting checkbox is set and
|
||||||
the format defined.
|
the format defined.
|
||||||
|
|
||||||
|
## Virtual TXTP files
|
||||||
|
Some of vgmstream's plugins allow you to use virtual .txtp files, that combined
|
||||||
|
with playlists let you make quick song configs.
|
||||||
|
|
||||||
|
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
|
||||||
|
filename.
|
||||||
|
|
||||||
|
Instead of manually creating .txtp files you can put non-existing virtual .txtp
|
||||||
|
in a `.m3u` playlist:
|
||||||
|
```
|
||||||
|
# playlist that opens subsongs directly without having to create .txtp
|
||||||
|
# notice the full filename, then #(config), then ".txtp" (spaces are optional)
|
||||||
|
bank_bgm_full.nub #s1 .txtp
|
||||||
|
bank_bgm_full.nub #s10 .txtp
|
||||||
|
```
|
||||||
|
|
||||||
|
Combine with tagging (see above) for extra fun OST-like config.
|
||||||
|
```
|
||||||
|
# @ALBUM GOD HAND
|
||||||
|
|
||||||
|
# play 1 loop, delay and do a longer fade
|
||||||
|
# %TITLE Too Hot !!
|
||||||
|
circus_a_mix_ver2.adx #l 1.0 #d 5.0 #f 15.0 .txtp
|
||||||
|
|
||||||
|
# play 1 loop instead of the default 2 then fade with the song's internal fading
|
||||||
|
# %TITLE Yet... Oh see mind
|
||||||
|
boss2_3ningumi_ver6.adx #l 1.0 #F .txtp
|
||||||
|
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use it in CLI for quick access to some txtp-exclusive functions:
|
||||||
|
```
|
||||||
|
# force change sample rate to 22050
|
||||||
|
test.exe btl_koopa1_44k_lp.brstm #h22050.txtp -o btl_koopa1_44k_lp.wav
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Supported codec types
|
## Supported codec types
|
||||||
Quick list of codecs vgmstream supports, including many obscure ones that
|
Quick list of codecs vgmstream supports, including many obscure ones that
|
||||||
|
@ -20,6 +20,7 @@ extern "C" {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
STREAMFILE sf; /* callbacks */
|
STREAMFILE sf; /* callbacks */
|
||||||
|
|
||||||
|
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 */
|
||||||
@ -32,12 +33,12 @@ typedef struct {
|
|||||||
} 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 buffersize, abort_callback * p_abort, t_filestats * stats);
|
||||||
static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_file,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 buffersize, abort_callback * p_abort);
|
||||||
|
|
||||||
static size_t read_foo(FOO_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) {
|
static size_t read_foo(FOO_STREAMFILE *streamfile, uint8_t * dest, off_t offset, size_t length) {
|
||||||
size_t length_read_total = 0;
|
size_t length_read_total = 0;
|
||||||
|
|
||||||
if (!streamfile || !dest || length <= 0 || offset < 0)
|
if (!streamfile || !streamfile->m_file_opened || !dest || 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? */
|
||||||
@ -124,7 +125,8 @@ static void get_name_foo(FOO_STREAMFILE *streamfile,char *buffer,size_t length)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void close_foo(FOO_STREAMFILE * streamfile) {
|
static void close_foo(FOO_STREAMFILE * streamfile) {
|
||||||
streamfile->m_file.release();
|
if (streamfile->m_file_opened)
|
||||||
|
streamfile->m_file.release();
|
||||||
free(streamfile->name);
|
free(streamfile->name);
|
||||||
free(streamfile->buffer);
|
free(streamfile->buffer);
|
||||||
free(streamfile);
|
free(streamfile);
|
||||||
@ -139,22 +141,22 @@ static STREAMFILE *open_foo(FOO_STREAMFILE *streamFile,const char * const filena
|
|||||||
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 (!strcmp(streamFile->name,filename)) {
|
if (streamFile->m_file_opened && !strcmp(streamFile->name,filename)) {
|
||||||
m_file = streamFile->m_file;
|
m_file = streamFile->m_file; //copy?
|
||||||
{
|
{
|
||||||
newstreamFile = open_foo_streamfile_buffer_by_file(m_file,filename,buffersize,streamFile->p_abort);
|
newstreamFile = open_foo_streamfile_buffer_by_file(m_file, streamFile->m_file_opened, filename, buffersize, streamFile->p_abort);
|
||||||
if (newstreamFile) {
|
if (newstreamFile) {
|
||||||
return newstreamFile;
|
return newstreamFile;
|
||||||
}
|
}
|
||||||
// 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,buffersize,streamFile->p_abort,NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_file,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 buffersize, abort_callback * p_abort) {
|
||||||
uint8_t * buffer;
|
uint8_t * buffer;
|
||||||
FOO_STREAMFILE * streamfile;
|
FOO_STREAMFILE * streamfile;
|
||||||
|
|
||||||
@ -171,6 +173,7 @@ static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_fil
|
|||||||
streamfile->sf.open = (_STREAMFILE *(__cdecl *)(_STREAMFILE *,const char *const ,size_t)) open_foo;
|
streamfile->sf.open = (_STREAMFILE *(__cdecl *)(_STREAMFILE *,const char *const ,size_t)) open_foo;
|
||||||
streamfile->sf.close = (void (__cdecl *)(_STREAMFILE *)) close_foo;
|
streamfile->sf.close = (void (__cdecl *)(_STREAMFILE *)) close_foo;
|
||||||
|
|
||||||
|
streamfile->m_file_opened = m_file_opened;
|
||||||
streamfile->m_file = m_file;
|
streamfile->m_file = m_file;
|
||||||
streamfile->p_abort = p_abort;
|
streamfile->p_abort = p_abort;
|
||||||
streamfile->buffersize = buffersize;
|
streamfile->buffersize = buffersize;
|
||||||
@ -180,7 +183,10 @@ static STREAMFILE * open_foo_streamfile_buffer_by_file(service_ptr_t<file> m_fil
|
|||||||
if (!streamfile->name) goto fail;
|
if (!streamfile->name) goto fail;
|
||||||
|
|
||||||
/* cache filesize */
|
/* cache filesize */
|
||||||
streamfile->filesize = streamfile->m_file->get_size(*streamfile->p_abort);
|
if (streamfile->m_file_opened)
|
||||||
|
streamfile->filesize = streamfile->m_file->get_size(*streamfile->p_abort);
|
||||||
|
else
|
||||||
|
streamfile->filesize = 0;
|
||||||
|
|
||||||
return &streamfile->sf;
|
return &streamfile->sf;
|
||||||
|
|
||||||
@ -193,16 +199,23 @@ fail:
|
|||||||
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 buffersize, abort_callback * p_abort, t_filestats * stats) {
|
||||||
STREAMFILE *streamFile;
|
STREAMFILE *streamFile;
|
||||||
service_ptr_t<file> infile;
|
service_ptr_t<file> infile;
|
||||||
|
bool infile_exists;
|
||||||
|
|
||||||
if(!(filesystem::g_exists(filename, *p_abort)))
|
infile_exists = filesystem::g_exists(filename, *p_abort);
|
||||||
return NULL;
|
if(!infile_exists) {
|
||||||
|
/* allow non-existing files in some cases */
|
||||||
|
if (!vgmstream_is_virtual_filename(filename))
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
filesystem::g_open_read(infile,filename,*p_abort);
|
if (infile_exists) {
|
||||||
if(stats) *stats = infile->get_stats(*p_abort);
|
filesystem::g_open_read(infile,filename,*p_abort);
|
||||||
|
if(stats) *stats = infile->get_stats(*p_abort);
|
||||||
|
}
|
||||||
|
|
||||||
streamFile = open_foo_streamfile_buffer_by_file(infile,filename,buffersize,p_abort);
|
streamFile = open_foo_streamfile_buffer_by_file(infile, infile_exists, filename, buffersize, p_abort);
|
||||||
if (!streamFile) {
|
if (!streamFile) {
|
||||||
// fclose(infile);
|
//m_file.release(); //todo not needed after g_open_read?
|
||||||
}
|
}
|
||||||
|
|
||||||
return streamFile;
|
return streamFile;
|
||||||
|
@ -73,20 +73,28 @@ input_vgmstream::~input_vgmstream() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// called first when a new file is opened
|
// called first when a new file is opened
|
||||||
void input_vgmstream::open(service_ptr_t<file> p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) {
|
void input_vgmstream::open(service_ptr_t<file> p_filehint, const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) {
|
||||||
|
|
||||||
if (!p_path) { // shouldn't be possible
|
if (!p_path) { // shouldn't be possible
|
||||||
throw exception_io_data();
|
throw exception_io_data();
|
||||||
return;
|
return; //???
|
||||||
}
|
}
|
||||||
|
|
||||||
filename = p_path;
|
filename = p_path;
|
||||||
|
|
||||||
|
// allow non-existing files in some cases
|
||||||
|
bool infile_virtual = !filesystem::g_exists(p_path, p_abort)
|
||||||
|
&& vgmstream_is_virtual_filename(filename) == 1;
|
||||||
|
|
||||||
// keep file stats around (timestamp, filesize)
|
// don't try to open virtual files as it'll fail
|
||||||
if ( p_filehint.is_empty() )
|
// (doesn't seem to have any adverse effect, except maybe no stats)
|
||||||
input_open_file_helper( p_filehint, filename, p_reason, p_abort );
|
// setup_vgmstream also makes further checks before file is finally opened
|
||||||
stats = p_filehint->get_stats( p_abort );
|
if (!infile_virtual) {
|
||||||
|
// keep file stats around (timestamp, filesize)
|
||||||
|
if ( p_filehint.is_empty() )
|
||||||
|
input_open_file_helper( p_filehint, filename, p_reason, p_abort );
|
||||||
|
stats = p_filehint->get_stats( p_abort );
|
||||||
|
}
|
||||||
|
|
||||||
switch(p_reason) {
|
switch(p_reason) {
|
||||||
case input_open_decode: // prepare to retrieve info and decode
|
case input_open_decode: // prepare to retrieve info and decode
|
||||||
@ -169,7 +177,7 @@ void input_vgmstream::get_info(t_uint32 p_subsong, file_info & p_info, abort_cal
|
|||||||
strcpy(tagfile_path,tagfile_name);
|
strcpy(tagfile_path,tagfile_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
STREAMFILE *tagFile = open_foo_streamfile(tagfile_path, &p_abort, &stats);
|
STREAMFILE *tagFile = open_foo_streamfile(tagfile_path, &p_abort, NULL);
|
||||||
if (tagFile != NULL) {
|
if (tagFile != NULL) {
|
||||||
VGMSTREAM_TAGS *tags;
|
VGMSTREAM_TAGS *tags;
|
||||||
const char *tag_key, *tag_val;
|
const char *tag_key, *tag_val;
|
||||||
|
@ -26,7 +26,7 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char
|
|||||||
static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offset, size_t length) {
|
static size_t read_stdio(STDIOSTREAMFILE *streamfile,uint8_t * dest, off_t offset, size_t length) {
|
||||||
size_t length_read_total = 0;
|
size_t length_read_total = 0;
|
||||||
|
|
||||||
if (!streamfile || !dest || length <= 0 || offset < 0)
|
if (!streamfile || !streamfile->infile || !dest || 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? */
|
||||||
@ -110,7 +110,8 @@ static void get_name_stdio(STDIOSTREAMFILE *streamfile,char *buffer,size_t lengt
|
|||||||
buffer[length-1]='\0';
|
buffer[length-1]='\0';
|
||||||
}
|
}
|
||||||
static void close_stdio(STDIOSTREAMFILE * streamfile) {
|
static void close_stdio(STDIOSTREAMFILE * streamfile) {
|
||||||
fclose(streamfile->infile);
|
if (streamfile->infile)
|
||||||
|
fclose(streamfile->infile);
|
||||||
free(streamfile->buffer);
|
free(streamfile->buffer);
|
||||||
free(streamfile);
|
free(streamfile);
|
||||||
}
|
}
|
||||||
@ -124,7 +125,7 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil
|
|||||||
return NULL;
|
return NULL;
|
||||||
#if !defined (__ANDROID__)
|
#if !defined (__ANDROID__)
|
||||||
// if same name, duplicate the file pointer we already have open
|
// if same name, duplicate the file pointer we already have open
|
||||||
if (!strcmp(streamFile->name,filename)) {
|
if (streamFile->infile && !strcmp(streamFile->name,filename)) {
|
||||||
if (((newfd = dup(fileno(streamFile->infile))) >= 0) &&
|
if (((newfd = dup(fileno(streamFile->infile))) >= 0) &&
|
||||||
(newfile = fdopen( newfd, "rb" )))
|
(newfile = fdopen( newfd, "rb" )))
|
||||||
{
|
{
|
||||||
@ -141,7 +142,7 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil
|
|||||||
return open_stdio_streamfile_buffer(filename,buffersize);
|
return open_stdio_streamfile_buffer(filename,buffersize);
|
||||||
}
|
}
|
||||||
|
|
||||||
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize) {
|
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile, const char * const filename, size_t buffersize) {
|
||||||
uint8_t * buffer = NULL;
|
uint8_t * buffer = NULL;
|
||||||
STDIOSTREAMFILE * streamfile = NULL;
|
STDIOSTREAMFILE * streamfile = NULL;
|
||||||
|
|
||||||
@ -166,8 +167,13 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char
|
|||||||
streamfile->name[sizeof(streamfile->name)-1] = '\0';
|
streamfile->name[sizeof(streamfile->name)-1] = '\0';
|
||||||
|
|
||||||
/* cache filesize */
|
/* cache filesize */
|
||||||
fseeko(streamfile->infile,0,SEEK_END);
|
if (infile) {
|
||||||
streamfile->filesize = ftello(streamfile->infile);
|
fseeko(streamfile->infile,0,SEEK_END);
|
||||||
|
streamfile->filesize = ftello(streamfile->infile);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
streamfile->filesize = 0; /* allow virtual, non-existing files */
|
||||||
|
}
|
||||||
|
|
||||||
/* Typically fseek(o)/ftell(o) may only handle up to ~2.14GB, signed 32b = 0x7FFFFFFF
|
/* Typically fseek(o)/ftell(o) may only handle up to ~2.14GB, signed 32b = 0x7FFFFFFF
|
||||||
* (happens in banks like FSB, though rarely). Can be remedied with the
|
* (happens in banks like FSB, though rarely). Can be remedied with the
|
||||||
@ -190,11 +196,15 @@ static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, si
|
|||||||
STREAMFILE *streamFile;
|
STREAMFILE *streamFile;
|
||||||
|
|
||||||
infile = fopen(filename,"rb");
|
infile = fopen(filename,"rb");
|
||||||
if (!infile) return NULL;
|
if (!infile) {
|
||||||
|
/* allow non-existing files in some cases */
|
||||||
|
if (!vgmstream_is_virtual_filename(filename))
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
streamFile = open_stdio_streamfile_buffer_by_file(infile,filename,buffersize);
|
streamFile = open_stdio_streamfile_buffer_by_file(infile,filename,buffersize);
|
||||||
if (!streamFile) {
|
if (!streamFile) {
|
||||||
fclose(infile);
|
if (infile) fclose(infile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return streamFile;
|
return streamFile;
|
||||||
|
@ -2882,3 +2882,20 @@ fail:
|
|||||||
/* open streams will be closed in close_vgmstream(), hopefully called by the meta */
|
/* open streams will be closed in close_vgmstream(), hopefully called by the meta */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int vgmstream_is_virtual_filename(const char* filename) {
|
||||||
|
int len = strlen(filename);
|
||||||
|
if (len < 6)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* vgmstream can play .txtp files that have size 0 but point to another file with config
|
||||||
|
* based only in the filename (ex. "file.fsb #2.txtp" plays 2nd subsong of file.fsb).
|
||||||
|
*
|
||||||
|
* Also, .m3u playlist can include files that don't exist, and players often allow filenames
|
||||||
|
* pointing to nothing (since could be some protocol/url).
|
||||||
|
*
|
||||||
|
* Plugins can use both quirks to allow "virtual files" (.txtp) in .m3u that don't need
|
||||||
|
* to exist but allow config. Plugins with this function if the filename is virtual,
|
||||||
|
* and their STREAMFILEs should be modified as to ignore null FILEs and report size 0. */
|
||||||
|
return strcmp(&filename[len-5], ".txtp") == 0;
|
||||||
|
}
|
||||||
|
@ -1336,6 +1336,9 @@ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sa
|
|||||||
/* Set number of max loops to do, then play up to stream end (for songs with proper endings) */
|
/* Set number of max loops to do, then play up to stream end (for songs with proper endings) */
|
||||||
void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target);
|
void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target);
|
||||||
|
|
||||||
|
/* Return 1 if vgmstream detects from the filename that said file can be used even if doesn't physically exist */
|
||||||
|
int vgmstream_is_virtual_filename(const char* filename);
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------*/
|
/* -------------------------------------------------------------------------*/
|
||||||
/* vgmstream "private" API */
|
/* vgmstream "private" API */
|
||||||
/* -------------------------------------------------------------------------*/
|
/* -------------------------------------------------------------------------*/
|
||||||
|
@ -210,7 +210,7 @@ static FILE* wa_fdopen(int fd) {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
STREAMFILE sf;
|
STREAMFILE sf;
|
||||||
STREAMFILE *stdiosf;
|
STREAMFILE *stdiosf;
|
||||||
FILE *infile_ref; /* pointer to the infile in 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);
|
||||||
@ -244,7 +244,7 @@ static STREAMFILE *wasf_open(WINAMP_STREAMFILE *streamFile, const char *const fi
|
|||||||
|
|
||||||
/* if same name, duplicate the file pointer we already have open */ //unsure if all this is needed
|
/* if same name, duplicate the file pointer we already have open */ //unsure if all this is needed
|
||||||
streamFile->stdiosf->get_name(streamFile->stdiosf, name, PATH_LIMIT);
|
streamFile->stdiosf->get_name(streamFile->stdiosf, name, PATH_LIMIT);
|
||||||
if (!strcmp(name,filename)) {
|
if (streamFile->infile_ref && !strcmp(name,filename)) {
|
||||||
if (((newfd = dup(fileno(streamFile->infile_ref))) >= 0) &&
|
if (((newfd = dup(fileno(streamFile->infile_ref))) >= 0) &&
|
||||||
(newfile = wa_fdopen(newfd)))
|
(newfile = wa_fdopen(newfd)))
|
||||||
{
|
{
|
||||||
@ -275,7 +275,7 @@ static STREAMFILE *open_winamp_streamfile_by_file(FILE *infile, const char * pat
|
|||||||
this_sf = calloc(1,sizeof(WINAMP_STREAMFILE));
|
this_sf = calloc(1,sizeof(WINAMP_STREAMFILE));
|
||||||
if (!this_sf) goto fail;
|
if (!this_sf) goto fail;
|
||||||
|
|
||||||
stdiosf = open_stdio_streamfile_by_file(infile,path);
|
stdiosf = open_stdio_streamfile_by_file(infile, path);
|
||||||
if (!stdiosf) goto fail;
|
if (!stdiosf) goto fail;
|
||||||
|
|
||||||
this_sf->sf.read = (void*)wasf_read;
|
this_sf->sf.read = (void*)wasf_read;
|
||||||
@ -302,16 +302,21 @@ static STREAMFILE *open_winamp_streamfile_by_ipath(const in_char *wpath) {
|
|||||||
STREAMFILE *streamFile;
|
STREAMFILE *streamFile;
|
||||||
char path[PATH_LIMIT];
|
char path[PATH_LIMIT];
|
||||||
|
|
||||||
/* open a FILE from a Winamp (possibly UTF-16) path */
|
|
||||||
infile = wa_fopen(wpath);
|
|
||||||
if (!infile) return NULL;
|
|
||||||
|
|
||||||
/* convert to UTF-8 if needed for internal use */
|
/* convert to UTF-8 if needed for internal use */
|
||||||
wa_ichar_to_char(path,PATH_LIMIT, wpath);
|
wa_ichar_to_char(path,PATH_LIMIT, wpath);
|
||||||
|
|
||||||
|
/* open a FILE from a Winamp (possibly UTF-16) path */
|
||||||
|
infile = wa_fopen(wpath);
|
||||||
|
if (!infile) {
|
||||||
|
/* allow non-existing files in some cases */
|
||||||
|
if (!vgmstream_is_virtual_filename(path))
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
streamFile = open_winamp_streamfile_by_file(infile,path);
|
streamFile = open_winamp_streamfile_by_file(infile,path);
|
||||||
if (!streamFile) {
|
if (!streamFile) {
|
||||||
fclose(infile);
|
if (infile) fclose(infile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return streamFile;
|
return streamFile;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user