mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-30 03:47:30 +01:00
commit
869ca7127a
@ -21,6 +21,7 @@ extern "C" {
|
||||
#ifndef VERSION
|
||||
#include "../version.h"
|
||||
#endif
|
||||
|
||||
#ifndef VERSION
|
||||
#define PLUGIN_VERSION __DATE__
|
||||
#else
|
||||
@ -145,6 +146,8 @@ void input_vgmstream::get_info(t_uint32 p_subsong, file_info & p_info, abort_cal
|
||||
p_info.meta_set("TITLE",temp);
|
||||
}
|
||||
|
||||
p_info.info_set("vgmstream version",PLUGIN_VERSION);
|
||||
|
||||
p_info.info_set_int("samplerate", samplerate);
|
||||
p_info.info_set_int("channels", channels);
|
||||
p_info.info_set_int("bitspersample",16);
|
||||
|
@ -1,13 +1,16 @@
|
||||
#include "vgmstream.h"
|
||||
|
||||
//#define VGM_REGISTER_TYPE(extension) ...
|
||||
//#define VGM_REGISTER_TYPE_COMMON(extension) ... /* for common extensions like aiff */
|
||||
|
||||
/* defines the list of accepted extensions. vgmstream doesn't use it internally so it's here
|
||||
* to inform plugins that need it. Common extensions are commented out to avoid stealing them. */
|
||||
|
||||
/* some extensions could be #ifdef but no really needed */
|
||||
/* some extensions require external libraries and could be #ifdef, no really needed */
|
||||
/* some formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension is not parsed */
|
||||
|
||||
|
||||
static const char* extension_list[] = {
|
||||
//"", /* vgmstream can plays extensionless files too, but plugins must accept them manually */
|
||||
|
||||
"04sw",
|
||||
"2dx9",
|
||||
"2pfs",
|
||||
@ -38,6 +41,7 @@ static const char* extension_list[] = {
|
||||
"al2",
|
||||
"amts", //fake extension/header id for .stm (to be removed)
|
||||
"ao", //txth/reserved [Cloudphobia (PC)]
|
||||
"apc", //txth/reserved [MegaRace 3 (PC)]
|
||||
"as4",
|
||||
"asd",
|
||||
"asf",
|
||||
@ -367,6 +371,7 @@ static const char* extension_list[] = {
|
||||
"v0",
|
||||
//"v1", //dual channel with v0
|
||||
"vag",
|
||||
"vai", //txth/reserved [Ratatouille (GC)]
|
||||
"vas",
|
||||
"vawx",
|
||||
"vb",
|
||||
@ -435,7 +440,7 @@ static const char* extension_list[] = {
|
||||
"zsd",
|
||||
"zwdsp",
|
||||
|
||||
"vgmstream"
|
||||
"vgmstream" /* fake extension, catch-all for FFmpeg/txth/etc */
|
||||
|
||||
//, NULL //end mark
|
||||
};
|
||||
@ -907,7 +912,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_X360_TRA, "Terminal Reality .TRA raw header"},
|
||||
{meta_PS2_VGS, "Princess Soft VGS header"},
|
||||
{meta_PS2_IAB, "Runtime .IAB header"},
|
||||
{meta_PS2_STRLR, "STR L/R header"},
|
||||
{meta_PS2_STRLR, "The Bouncer STR header"},
|
||||
{meta_LSF_N1NJ4N, ".lsf !n1nj4n header"},
|
||||
{meta_VAWX, "feelplus VAWX header"},
|
||||
{meta_PC_SNDS, "assumed Heavy Iron IMA by .snds extension"},
|
||||
@ -1023,6 +1028,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_OGG_GWM, "Ogg Vorbis (GWM header)"},
|
||||
{meta_DSP_SADF, "Procyon Studio SADF header"},
|
||||
{meta_H4M, "Hudson HVQM4 header"},
|
||||
{meta_OGG_MUS, "Ogg Vorbis (MUS header)"},
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{meta_FFmpeg, "FFmpeg supported file format"},
|
||||
|
@ -1,19 +1,17 @@
|
||||
#include "layout.h"
|
||||
#include "../vgmstream.h"
|
||||
|
||||
/* set up for the block at the given offset */
|
||||
/* The Bouncer STRx blocks, one block per channel when stereo */
|
||||
void block_update_ps2_strlr(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
|
||||
int i;
|
||||
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
vgmstream->current_block_size = read_32bitLE(
|
||||
vgmstream->current_block_offset+0x4,
|
||||
vgmstream->ch[0].streamfile)*2;
|
||||
vgmstream->next_block_offset = vgmstream->current_block_offset+vgmstream->current_block_size+0x40;
|
||||
//vgmstream->current_block_size/=vgmstream->channels;
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
vgmstream->current_block_size = read_32bitLE(block_offset+0x04,streamFile); /* can be smaller than 0x800 */
|
||||
vgmstream->next_block_offset = block_offset + 0x800*vgmstream->channels;
|
||||
/* 0x08: number of remaning blocks, 0x10: some id/size? (shared in all blocks) */
|
||||
|
||||
for (i=0;i<vgmstream->channels;i++) {
|
||||
vgmstream->ch[i].offset = vgmstream->current_block_offset+0x20+(0x800*i);
|
||||
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
vgmstream->ch[i].offset = block_offset + 0x20 + 0x800*i;
|
||||
}
|
||||
}
|
||||
|
@ -238,7 +238,13 @@ static const hcakey_info hcakey_list[] = {
|
||||
{3201512}, // 000000000030D9E8
|
||||
|
||||
// PriPara: All Idol Perfect Stage (Takara Tomy) [Switch]
|
||||
{217735759}, // 000000000CFA624F
|
||||
{217735759}, // 000000000CFA624F
|
||||
|
||||
// Space Invaders Extreme (Taito Corporation, Backbone Entertainment) [PC]
|
||||
{91380310056}, // 0000001546B0E028
|
||||
|
||||
// CR Another God Hades Advent (Universal Entertainment Corporation) [iOS/Android]
|
||||
{64813795}, // 0000000003DCFAE3
|
||||
|
||||
};
|
||||
|
||||
|
@ -226,6 +226,28 @@ static void gwm_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, vo
|
||||
}
|
||||
}
|
||||
|
||||
static void mus_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
|
||||
static const uint8_t key[16] = {
|
||||
0x21,0x4D,0x6F,0x01,0x20,0x4C,0x6E,0x02,0x1F,0x4B,0x6D,0x03,0x20,0x4C,0x6E,0x02
|
||||
};
|
||||
|
||||
size_t bytes_read = size*nmemb;
|
||||
ogg_vorbis_streamfile * const ov_streamfile = datasource;
|
||||
int i;
|
||||
char *header_id = "OggS";
|
||||
|
||||
/* bytes are xor'd with key, first "OggS" is changed */
|
||||
for (i = 0; i < bytes_read; i++) {
|
||||
if (ov_streamfile->offset+i < 0x04) { /* if decrypted gives "Mus " */
|
||||
((uint8_t*)ptr)[i] = (uint8_t)header_id[(ov_streamfile->offset + i) % 4];
|
||||
}
|
||||
else {
|
||||
((uint8_t*)ptr)[i] ^= key[(ov_streamfile->offset + i) % sizeof(key)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */
|
||||
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
@ -239,6 +261,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
||||
int is_rpgmvo = 0;
|
||||
int is_eno = 0;
|
||||
int is_gwm = 0;
|
||||
int is_mus = 0;
|
||||
|
||||
|
||||
/* check extension */
|
||||
@ -262,6 +285,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
||||
is_eno = 1;
|
||||
} else if (check_extensions(streamFile,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */
|
||||
is_gwm = 1;
|
||||
} else if (check_extensions(streamFile,"mus")) { /* .mus: Redux - Dark Matters (PC) */
|
||||
is_mus = 1;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
@ -355,7 +380,6 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
||||
start_offset = 0x01;
|
||||
}
|
||||
|
||||
|
||||
/* check GWM [Adagio: Cloudburst (PC)], encrypted */
|
||||
if (is_gwm) {
|
||||
ovmi.xor_value = 0x5D;
|
||||
@ -363,6 +387,12 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
||||
ovmi.meta_type = meta_OGG_GWM;
|
||||
}
|
||||
|
||||
/* check .mus [Redux - Dark Matters (PC)], encrypted */
|
||||
if (is_mus) {
|
||||
ovmi.decryption_callback = mus_ogg_decryption_callback;
|
||||
ovmi.meta_type = meta_OGG_MUS;
|
||||
}
|
||||
|
||||
|
||||
return init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi);
|
||||
|
||||
|
@ -5,17 +5,14 @@
|
||||
VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int channel_count, loop_flag, sample_rate, num_samples;
|
||||
size_t file_size, data_size, unknown1, unknown2, interleave;
|
||||
int loop_flag;
|
||||
int channel_count;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "joe"))
|
||||
goto fail;
|
||||
|
||||
loop_flag = 1;
|
||||
channel_count = 2;
|
||||
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
data_size = read_32bitLE(0x04,streamFile);
|
||||
unknown1 = read_32bitLE(0x08,streamFile);
|
||||
@ -48,14 +45,20 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) {
|
||||
|
||||
start_offset = file_size - data_size;
|
||||
|
||||
channel_count = 2;
|
||||
sample_rate = read_32bitLE(0x00,streamFile);
|
||||
num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||
|
||||
/* most songs simply repeat except a few jingles (PS-ADPCM flags are always set) */
|
||||
loop_flag = (num_samples > 20*sample_rate); /* in seconds */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = read_32bitLE(0x00,streamFile);
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
//todo improve, not working 100% with early .joe
|
||||
{
|
||||
@ -103,6 +106,7 @@ VGMSTREAM * init_vgmstream_ps2_joe(STREAMFILE *streamFile) {
|
||||
}
|
||||
}
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
vgmstream->meta_type = meta_PS2_JOE;
|
||||
|
@ -1,80 +1,57 @@
|
||||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* STR: The Bouncer (PS2) */
|
||||
/* STR - The Bouncer (PS2) */
|
||||
VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
int i;
|
||||
off_t start_offset;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("str",filename_extension(filename))) goto fail;
|
||||
int channel_count, loop_flag;
|
||||
off_t start_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .vs: real extension (from .nam container) , .str: partial header id */
|
||||
if (!check_extensions(streamFile, "vs,str"))
|
||||
goto fail;
|
||||
|
||||
#if 0
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x5354524C) /* "STRL" */
|
||||
if (!(read_32bitBE(0x000,streamFile) == 0x5354524C && /* "STRL" */
|
||||
read_32bitBE(0x800,streamFile) == 0x53545252) && /* "STRR" */
|
||||
read_32bitBE(0x00,streamFile) != 0x5354524D) /* "STRM" */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x53545252) /* "STRR" */
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
/* don't hijack Sonic & Sega All Stars Racing X360 (xma) */
|
||||
if (read_32bitBE(0x00,streamFile) == 0x52494646)
|
||||
goto fail; /* "RIFF"*/
|
||||
/* don't hijack Mad Dash Racing (Xbox) */
|
||||
if (read_32bitLE(0x0c,streamFile) == 1
|
||||
&& read_32bitLE(0x010,streamFile) == 0
|
||||
&& read_32bitLE(0x400,streamFile) == 0
|
||||
&& read_32bitLE(0x7f0,streamFile) == 0)
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = 2;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
channel_count = (read_32bitBE(0x00,streamFile) == 0x5354524D) ? 1 : 2; /* "STRM"=mono (voices) */
|
||||
start_offset = 0x00;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = 0x0;
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = 48000;
|
||||
vgmstream->sample_rate = 44100;
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
|
||||
vgmstream->layout_type = layout_blocked_ps2_strlr;
|
||||
//vgmstream->interleave_block_size = read_32bitLE(0xC, streamFile);
|
||||
vgmstream->meta_type = meta_PS2_STRLR;
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
|
||||
/* calc num_samples */
|
||||
{
|
||||
for (i=0;i<channel_count;i++)
|
||||
{
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, 0x8000);
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
vgmstream->next_block_offset = start_offset;
|
||||
do {
|
||||
block_update_ps2_strlr(vgmstream->next_block_offset,vgmstream);
|
||||
vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1);
|
||||
}
|
||||
while (vgmstream->next_block_offset < get_streamfile_size(streamFile));
|
||||
}
|
||||
|
||||
/* Calc num_samples */
|
||||
block_update_ps2_strlr(start_offset, vgmstream);
|
||||
vgmstream->num_samples=0;
|
||||
|
||||
do
|
||||
{
|
||||
vgmstream->num_samples += vgmstream->current_block_size * 14 / 16;
|
||||
block_update_ps2_strlr(vgmstream->next_block_offset, vgmstream);
|
||||
} while (vgmstream->next_block_offset < get_streamfile_size(streamFile));
|
||||
|
||||
block_update_ps2_strlr(start_offset, vgmstream);
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -313,6 +313,7 @@ static int parse_sb_header(ubi_sb_header * sb, STREAMFILE *streamFile) {
|
||||
/* ignore non-audio entry (other types seem to have config data) */
|
||||
if (read_32bit(offset + 0x04, streamFile) != 0x01)
|
||||
continue;
|
||||
//;VGM_LOG("SB at %lx\n", offset);
|
||||
|
||||
/* weird case when there is no internal substream ID and just seem to rotate every time type changes, joy */
|
||||
if (sb->has_rotating_ids) { /* assumes certain configs can't happen in this case */
|
||||
@ -779,6 +780,23 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile)
|
||||
|
||||
return 1;
|
||||
}
|
||||
#if 0
|
||||
/* Far cry: Instincts - Evolution (2006)(Xbox) */
|
||||
if (sb->version == 0x00170000 && is_sb2) {
|
||||
sb->section1_entry_size = 0x48;
|
||||
sb->section2_entry_size = 0x6c;
|
||||
|
||||
sb->external_flag_offset = 0;
|
||||
sb->num_samples_offset = 0x28;
|
||||
sb->stream_id_offset = 0;
|
||||
sb->sample_rate_offset = 0x3c;
|
||||
sb->channels_offset = 0x44;
|
||||
sb->stream_type_offset = 0x48;
|
||||
sb->extra_name_offset = 0x58;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Prince of Persia: Rival Swords (2007)(PSP) */
|
||||
if (sb->version == 0x00180005 && is_sb5) {
|
||||
|
28
src/util.c
28
src/util.c
@ -2,16 +2,28 @@
|
||||
#include "util.h"
|
||||
#include "streamtypes.h"
|
||||
|
||||
const char * filename_extension(const char * filename) {
|
||||
const char * ext;
|
||||
const char * filename_extension(const char * pathname) {
|
||||
const char * filename;
|
||||
const char * extension;
|
||||
|
||||
/* You know what would be nice? strrchrnul().
|
||||
* Instead I have to do it myself. */
|
||||
ext = strrchr(filename,'.');
|
||||
if (ext==NULL) ext=filename+strlen(filename); /* point to null, i.e. an empty string for the extension */
|
||||
else ext=ext+1; /* skip the dot */
|
||||
/* get basename + extension */
|
||||
filename = pathname;
|
||||
#if 0
|
||||
//must detect empty extensions in folders with . in the name; not too important and DIR_SEPARATOR could improved
|
||||
filename = strrchr(pathname, DIR_SEPARATOR);
|
||||
if (filename == NULL)
|
||||
filename = pathname; /* pathname has no separators (single filename) */
|
||||
else
|
||||
filename++; /* skip the separator */
|
||||
#endif
|
||||
|
||||
return ext;
|
||||
extension = strrchr(filename,'.');
|
||||
if (extension==NULL)
|
||||
extension = filename+strlen(filename); /* point to null, i.e. an empty string for the extension */
|
||||
else
|
||||
extension++; /* skip the dot */
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
/* unused */
|
||||
|
@ -679,6 +679,7 @@ typedef enum {
|
||||
meta_TA_AAC_VITA, /* tri-Ace AAC (Judas Code) */
|
||||
meta_OGG_GWM, /* Ogg Vorbis with encryption [Metronomicon (PC)] */
|
||||
meta_H4M, /* Hudson HVQM4 video [Resident Evil 0 (GC), Tales of Symphonia (GC)] */
|
||||
meta_OGG_MUS, /* Ogg Vorbis with encryption [Redux - Dark Matters (PC)] */
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
meta_FFmpeg,
|
||||
|
@ -43,7 +43,7 @@
|
||||
In_Module input_module;
|
||||
DWORD WINAPI __stdcall decode(void *arg);
|
||||
|
||||
/* Winamp Play extension list, needed to accept/play and associate extensions in Windows */
|
||||
/* Winamp Play extension list, to accept and associate extensions in Windows */
|
||||
#define EXTENSION_LIST_SIZE (0x2000 * 6)
|
||||
#define EXT_BUFFER_SIZE 200
|
||||
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
|
||||
@ -88,6 +88,7 @@ in_char lastfn[PATH_LIMIT] = {0}; /* name of the currently playing file */
|
||||
#define wa_strchr wcschr
|
||||
#define wa_sscanf swscanf
|
||||
#define wa_snprintf _snwprintf
|
||||
#define wa_strrchr wcsrchr
|
||||
#define wa_fileinfo fileinfoW
|
||||
#define wa_IPC_PE_INSERTFILENAME IPC_PE_INSERTFILENAMEW
|
||||
#define wa_L(x) L ##x
|
||||
@ -99,6 +100,7 @@ in_char lastfn[PATH_LIMIT] = {0}; /* name of the currently playing file */
|
||||
#define wa_strchr strchr
|
||||
#define wa_sscanf sscanf
|
||||
#define wa_snprintf snprintf
|
||||
#define wa_strrchr strrchr
|
||||
#define wa_fileinfo fileinfo
|
||||
#define wa_IPC_PE_INSERTFILENAME IPC_PE_INSERTFILENAME
|
||||
#define wa_L(x) x
|
||||
@ -777,7 +779,9 @@ static void add_extension(int length, char * dst, const char * ext) {
|
||||
}
|
||||
|
||||
/* Creates Winamp's extension list, a single string that ends with \0\0.
|
||||
* Each extension must be in this format: "extension\0Description\0" */
|
||||
* Each extension must be in this format: "extension\0Description\0"
|
||||
* The list is used to accept extensions by default when IsOurFile returns 0, and to register file types.
|
||||
* It could be ignored/empty and just detect in IsOurFile instead. */
|
||||
static void build_extension_list() {
|
||||
const char ** ext_list;
|
||||
size_t ext_list_len;
|
||||
@ -871,7 +875,29 @@ void winamp_Quit() {
|
||||
|
||||
/* called before extension checks, to allow detection of mms://, etc */
|
||||
int winamp_IsOurFile(const in_char *fn) {
|
||||
return 0; /* we don't recognize protocols */
|
||||
const in_char *filename;
|
||||
const in_char *extension;
|
||||
|
||||
/* get basename + extension */
|
||||
filename = fn;
|
||||
#if 0
|
||||
//must detect empty extensions in folders with . in the name; doesn't work ok?
|
||||
filename = wa_strrchr(fn, wa_L('\\'));
|
||||
if (filename == NULL)
|
||||
filename = fn;
|
||||
else
|
||||
filename++;
|
||||
#endif
|
||||
extension = wa_strrchr(filename, wa_L('.'));
|
||||
if (extension == NULL)
|
||||
return 1; /* extensionless, try to play it */
|
||||
else
|
||||
extension++;
|
||||
|
||||
/* returning 0 here means it only accepts the extensions in working_extension_list */
|
||||
/* it's possible to ignore the list and manually accept extensions, like foobar's g_is_our_path */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* request to start playing a file */
|
||||
|
Loading…
x
Reference in New Issue
Block a user