This commit is contained in:
bnnm 2024-07-18 23:50:22 +02:00
parent 33f01354e6
commit ea630eeead
15 changed files with 599 additions and 525 deletions

View File

@ -2,13 +2,15 @@
# main vgmstream code
#
# automatically get all possible .o by finding all .c
OBJECTS =
#SRCS = $(wildcard **/*.c) #GNUMake 3.81?
SRCS = $(wildcard *.c) $(wildcard */*.c) $(wildcard */*/*.c)
# get all possible .o by finding all .c
OBJECTS = $(patsubst %.c,%.o,$(SRCS))
# in case of renamed files
OBJECTS_CLEAN = $(wildcard *.o) $(wildcard */*.o) $(wildcard */*/*.o)
libvgmstream.a: $(OBJECTS)
$(AR) crs libvgmstream.a $(OBJECTS)
@ -20,6 +22,6 @@ libvgmstream.so: $(OBJECTS)
# $(CC) $(CFLAGS) -M -o vgmstream-deps
clean:
$(RMF) $(OBJECTS) libvgmstream.a
$(RMF) $(OBJECTS_CLEAN) libvgmstream.a
.PHONY: clean

View File

@ -33,14 +33,14 @@ TARGET_EXT_LIBS += $(LIBS_TARGET_EXT_LIBS)
export CFLAGS LDFLAGS
#SRC_SRCS = $(wildcard *.c)
SRC_SRCS = in_vgmstream.c in_streamfile.c in_config.c
SRCS = $(wildcard *.c)
OBJECTS = $(wildcard *.o)
###############################################################################
### targets
in_vgmstream: libvgmstream.a $(TARGET_EXT_LIBS) resource.o
$(CC) -shared -static-libgcc $(CFLAGS) $(SRC_SRCS) resource.o $(LDFLAGS) -o $(OUTPUT_WINAMP)
$(CC) -shared -static-libgcc $(CFLAGS) $(SRCS) resource.o $(LDFLAGS) -o $(OUTPUT_WINAMP)
$(STRIP) $(OUTPUT_WINAMP)
resource.o: resource.rc resource.h
@ -53,6 +53,6 @@ $(TARGET_EXT_LIBS):
$(MAKE) -C ../ext_libs $@
clean:
$(RMF) $(OUTPUT_WINAMP) resource.o
$(RMF) $(OUTPUT_WINAMP) $(OBJECTS) resource.o
.PHONY: clean in_vgmstream libvgmstream.a $(TARGET_EXT_LIBS)

View File

@ -9,8 +9,8 @@
/* ************************************* */
/* config */
#define CONFIG_APP_NAME TEXT("vgmstream plugin")
#define CONFIG_INI_NAME TEXT("plugin.ini")
#define CONFIG_APP_NAME TEXT("vgmstream plugin")
#define CONFIG_INI_NAME TEXT("plugin.ini")
#define INI_FADE_TIME TEXT("fade_seconds")
#define INI_FADE_DELAY TEXT("fade_delay")
@ -28,28 +28,28 @@
#define INI_CLIP_TYPE TEXT("clip_type")
TCHAR *dlg_priority_strings[7] = {
TEXT("Idle"),
TEXT("Lowest"),
TEXT("Below Normal"),
TEXT("Normal"),
TEXT("Above Normal"),
TEXT("Highest (not recommended)"),
TEXT("Time Critical (not recommended)")
TEXT("Idle"),
TEXT("Lowest"),
TEXT("Below Normal"),
TEXT("Normal"),
TEXT("Above Normal"),
TEXT("Highest (not recommended)"),
TEXT("Time Critical (not recommended)")
};
TCHAR *dlg_replaygain_strings[] = {
TEXT("None"),
TEXT("Album"),
TEXT("Peak")
TEXT("None"),
TEXT("Album"),
TEXT("Peak")
};
int priority_values[7] = {
THREAD_PRIORITY_IDLE,
THREAD_PRIORITY_LOWEST,
THREAD_PRIORITY_BELOW_NORMAL,
THREAD_PRIORITY_NORMAL,
THREAD_PRIORITY_ABOVE_NORMAL,
THREAD_PRIORITY_HIGHEST,
THREAD_PRIORITY_TIME_CRITICAL
const int priority_values[7] = {
THREAD_PRIORITY_IDLE,
THREAD_PRIORITY_LOWEST,
THREAD_PRIORITY_BELOW_NORMAL,
THREAD_PRIORITY_NORMAL,
THREAD_PRIORITY_ABOVE_NORMAL,
THREAD_PRIORITY_HIGHEST,
THREAD_PRIORITY_TIME_CRITICAL
};

120
winamp/in_utils.c Normal file
View File

@ -0,0 +1,120 @@
#include "in_vgmstream.h"
/* Adds ext to Winamp's extension list */
static int add_extension(char* dst, int dst_len, const char* ext) {
int ext_len;
ext_len = strlen(ext);
if (dst_len <= ext_len + 1)
return 0;
strcpy(dst, ext); /* seems winamp uppercases this if needed */
dst[ext_len] = ';';
return ext_len + 1;
}
/* Creates Winamp's extension list, a single string that ends with \0\0.
* Each extension must be in this format: "extensions\0Description\0"
*
* The list is used to accept extensions by default when IsOurFile returns 0, to register file
* types, and in the open dialog's type combo. Format actually can be:
* - "ext1;ext2;...\0EXTS Audio Files (*.ext1; *.ext2; *...\0", //single line with all
* - "ext1\0EXT1 Audio File (*.ext1)\0ext2\0EXT2 Audio File (*.ext2)\0...", //multiple lines
* Open dialog's text (including all plugin's "Description") seems limited to old MAX_PATH 260
* (max size for "extensions" checks seems ~0x40000 though). Given vgmstream's huge number
* of exts, use single line to (probably) work properly with dialogs (used to be multi line).
*/
void build_extension_list(char* winamp_list, int winamp_list_size) {
const char** ext_list;
size_t ext_list_len;
int i, written;
int description_size = 0x100; /* reserved max at the end */
winamp_list[0] = '\0';
winamp_list[1] = '\0';
ext_list = vgmstream_get_formats(&ext_list_len);
for (i = 0; i < ext_list_len; i++) {
int used = add_extension(winamp_list, winamp_list_size - description_size, ext_list[i]);
if (used <= 0) {
vgm_logi("build_extension_list: not enough buf for all exts\n");
break;
}
winamp_list += used;
winamp_list_size -= used;
}
if (i > 0) {
winamp_list[-1] = '\0'; /* last "ext;" to "ext\0" */
}
/* generic description for the info dialog since we can't really show everything */
written = snprintf(winamp_list, winamp_list_size - 2, "vgmstream Audio Files%c", '\0');
/* should end with double \0 */
if (written < 0) {
winamp_list[0] = '\0';
winamp_list[1] = '\0';
}
else {
winamp_list[written + 0] = '\0';
winamp_list[written + 1] = '\0';
}
}
/* makes a modified filename, suitable to pass parameters around */
static void make_fn_subsong(in_char* dst, int dst_size, const in_char* filename, int subsong_index) {
/* Follows "(file)(config)(ext)". Winamp needs to "see" (ext) to validate, and file goes first so relative
* files work in M3Us (path is added). Protocols a la "vgmstream://(config)(file)" work but don't get full paths. */
wa_snprintf(dst,dst_size, wa_L("%s|$s=%i|.vgmstream"), filename, subsong_index);
}
/* unpacks the subsongs by adding entries to the playlist */
bool split_subsongs(const in_char* filename, int subsong_index, VGMSTREAM* vgmstream) {
int i, playlist_index;
HWND hPlaylistWindow;
if (settings.disable_subsongs || vgmstream->num_streams <= 1)
return 0; /* don't split if no subsongs */
if (subsong_index > 0 || vgmstream->stream_index > 0)
return 0; /* no split if already playing subsong */
hPlaylistWindow = (HWND)SendMessage(input_module.hMainWindow, WM_WA_IPC, IPC_GETWND_PE, IPC_GETWND);
playlist_index = SendMessage(input_module.hMainWindow,WM_WA_IPC,0,IPC_GETLISTPOS);
/* The only way to pass info around in Winamp is encoding it into the filename, so a fake name
* is created with the index. Then, winamp_Play (and related) intercepts and reads the index. */
for (i = 0; i < vgmstream->num_streams; i++) {
in_char stream_fn[PATH_LIMIT];
make_fn_subsong(stream_fn,PATH_LIMIT, filename, (i+1)); /* encode index in filename */
/* insert at index */
{
COPYDATASTRUCT cds = {0};
wa_fileinfo f;
wa_strncpy(f.file, stream_fn,MAX_PATH-1);
f.file[MAX_PATH-1] = '\0';
f.index = playlist_index + (i+1);
cds.dwData = wa_IPC_PE_INSERTFILENAME;
cds.lpData = (void*)&f;
cds.cbData = sizeof(wa_fileinfo);
SendMessage(hPlaylistWindow,WM_COPYDATA,0,(LPARAM)&cds);
}
/* IPC_ENQUEUEFILE can pre-set the title without needing the Playlist handle, but can't insert at index */
}
/* remove current file from the playlist */
SendMessage(hPlaylistWindow, WM_WA_IPC, IPC_PE_DELETEINDEX, playlist_index);
/* autoplay doesn't always advance to the first unpacked track, but manually fails somehow */
//SendMessage(input_module.hMainWindow,WM_WA_IPC,playlist_index,IPC_SETPLAYLISTPOS);
//SendMessage(input_module.hMainWindow,WM_WA_IPC,0,IPC_STARTPLAY);
return 1;
}

View File

@ -27,7 +27,7 @@ DWORD WINAPI __stdcall decode(void *arg);
* (older versions of XMPlay also will crash with so many exts but can't autodetect version here) */
#define EXTENSION_LIST_SIZE (0x2000 * 6)
/* fixed list to simplify but could also malloc/free on init/close */
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
char extension_list[EXTENSION_LIST_SIZE] = {0};
/* current play state */
@ -127,61 +127,6 @@ static VGMSTREAM* init_vgmstream_winamp_fileinfo(const in_char* fn) {
}
/* makes a modified filename, suitable to pass parameters around */
static void make_fn_subsong(in_char* dst, int dst_size, const in_char* filename, int stream_index) {
/* Follows "(file)(config)(ext)". Winamp needs to "see" (ext) to validate, and file goes first so relative
* files work in M3Us (path is added). Protocols a la "vgmstream://(config)(file)" work but don't get full paths. */
wa_snprintf(dst,dst_size, wa_L("%s|$s=%i|.vgmstream"), filename, stream_index);
}
/* unpacks the subsongs by adding entries to the playlist */
static int split_subsongs(const in_char* filename, int stream_index, VGMSTREAM *vgmstream) {
int i, playlist_index;
HWND hPlaylistWindow;
if (settings.disable_subsongs || vgmstream->num_streams <= 1)
return 0; /* don't split if no subsongs */
if (stream_index > 0 || vgmstream->stream_index > 0)
return 0; /* no split if already playing subsong */
hPlaylistWindow = (HWND)SendMessage(input_module.hMainWindow, WM_WA_IPC, IPC_GETWND_PE, IPC_GETWND);
playlist_index = SendMessage(input_module.hMainWindow,WM_WA_IPC,0,IPC_GETLISTPOS);
/* The only way to pass info around in Winamp is encoding it into the filename, so a fake name
* is created with the index. Then, winamp_Play (and related) intercepts and reads the index. */
for (i = 0; i < vgmstream->num_streams; i++) {
in_char stream_fn[PATH_LIMIT];
make_fn_subsong(stream_fn,PATH_LIMIT, filename, (i+1)); /* encode index in filename */
/* insert at index */
{
COPYDATASTRUCT cds = {0};
wa_fileinfo f;
wa_strncpy(f.file, stream_fn,MAX_PATH-1);
f.file[MAX_PATH-1] = '\0';
f.index = playlist_index + (i+1);
cds.dwData = wa_IPC_PE_INSERTFILENAME;
cds.lpData = (void*)&f;
cds.cbData = sizeof(wa_fileinfo);
SendMessage(hPlaylistWindow,WM_COPYDATA,0,(LPARAM)&cds);
}
/* IPC_ENQUEUEFILE can pre-set the title without needing the Playlist handle, but can't insert at index */
}
/* remove current file from the playlist */
SendMessage(hPlaylistWindow, WM_WA_IPC, IPC_PE_DELETEINDEX, playlist_index);
/* autoplay doesn't always advance to the first unpacked track, but manually fails somehow */
//SendMessage(input_module.hMainWindow,WM_WA_IPC,playlist_index,IPC_SETPLAYLISTPOS);
//SendMessage(input_module.hMainWindow,WM_WA_IPC,0,IPC_STARTPLAY);
return 1;
}
/* try to detect XMPlay, which can't interact with the playlist = no splitting */
static int is_xmplay() {
if (GetModuleHandle( TEXT("xmplay.exe") ))
@ -194,70 +139,6 @@ static int is_xmplay() {
return 0;
}
/* Adds ext to Winamp's extension list */
static int add_extension(char* dst, int dst_len, const char* ext) {
int ext_len;
ext_len = strlen(ext);
if (dst_len <= ext_len + 1)
return 0;
strcpy(dst, ext); /* seems winamp uppercases this if needed */
dst[ext_len] = ';';
return ext_len + 1;
}
/* Creates Winamp's extension list, a single string that ends with \0\0.
* Each extension must be in this format: "extensions\0Description\0"
*
* The list is used to accept extensions by default when IsOurFile returns 0, to register file
* types, and in the open dialog's type combo. Format actually can be:
* - "ext1;ext2;...\0EXTS Audio Files (*.ext1; *.ext2; *...\0", //single line with all
* - "ext1\0EXT1 Audio File (*.ext1)\0ext2\0EXT2 Audio File (*.ext2)\0...", //multiple lines
* Open dialog's text (including all plugin's "Description") seems limited to old MAX_PATH 260
* (max size for "extensions" checks seems ~0x40000 though). Given vgmstream's huge number
* of exts, use single line to (probably) work properly with dialogs (used to be multi line).
*/
static void build_extension_list(char* winamp_list, int winamp_list_size) {
const char** ext_list;
size_t ext_list_len;
int i, written;
int description_size = 0x100; /* reserved max at the end */
winamp_list[0] = '\0';
winamp_list[1] = '\0';
ext_list = vgmstream_get_formats(&ext_list_len);
for (i = 0; i < ext_list_len; i++) {
int used = add_extension(winamp_list, winamp_list_size - description_size, ext_list[i]);
if (used <= 0) {
vgm_logi("build_extension_list: not enough buf for all exts\n");
break;
}
winamp_list += used;
winamp_list_size -= used;
}
if (i > 0) {
winamp_list[-1] = '\0'; /* last "ext;" to "ext\0" */
}
/* generic description for the info dialog since we can't really show everything */
written = snprintf(winamp_list, winamp_list_size - 2, "vgmstream Audio Files%c", '\0');
/* should end with double \0 */
if (written < 0) {
winamp_list[0] = '\0';
winamp_list[1] = '\0';
}
else {
winamp_list[written + 0] = '\0';
winamp_list[written + 1] = '\0';
}
}
/* unicode utils */
static void get_title(in_char* dst, int dst_size, const in_char* fn, VGMSTREAM* infostream) {
in_char filename[PATH_LIMIT];
@ -388,7 +269,7 @@ void winamp_Init() {
}
/* dynamically make a list of supported extensions */
build_extension_list(working_extension_list, sizeof(working_extension_list));
build_extension_list(extension_list, sizeof(extension_list));
}
/* called at program quit */
@ -872,7 +753,7 @@ In_Module input_module = {
PLUGIN_NAME,
0, /* hMainWindow (filled in by Winamp) */
0, /* hDllInstance (filled in by Winamp) */
working_extension_list,
extension_list,
1, /* is_seekable flag */
9, /* UsesOutputPlug flag */
winamp_Config,

View File

@ -34,7 +34,7 @@
/* ************************************* */
extern In_Module input_module;
extern int priority_values[7];
extern const int priority_values[7];
typedef enum {
REPLAYGAIN_NONE,
@ -87,8 +87,8 @@ extern winamp_log_t* walog;
/* ************************************* */
/* IN_UNICODE */
/* ************************************* */
//todo safe ops
//todo there must be a better way to handle unicode...
//TODO safe ops
//TODO there must be a better way to handle unicode...
#ifdef UNICODE_INPUT_PLUGIN
#define wa_strcmp wcscmp
#define wa_strncmp wcsncmp
@ -190,11 +190,11 @@ static inline void cfg_char_to_wchar(TCHAR *wdst, size_t wdstsize, const char *s
}
/* ************************************* */
/* IN_STREAMFILE */
/* ************************************* */
/* in_streamfile.c */
STREAMFILE* open_winamp_streamfile_by_ipath(const in_char* wpath);
#endif /*_IN_VGMSTREAM_*/
void build_extension_list(char* extension_list, int list_size);
bool split_subsongs(const in_char* filename, int subsong_index, VGMSTREAM* vgmstream);
#endif

View File

@ -67,10 +67,15 @@
<ItemGroup>
<ClInclude Include="in_vgmstream.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="sdk\in2.h" />
<ClInclude Include="sdk\ipc_pe.h" />
<ClInclude Include="sdk\out.h" />
<ClInclude Include="sdk\wa_ipc.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="in_config.c" />
<ClCompile Include="in_streamfile.c" />
<ClCompile Include="in_utils.c" />
<ClCompile Include="in_vgmstream.c" />
</ItemGroup>
<ItemGroup>

View File

@ -21,6 +21,18 @@
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="sdk\in2.h">
<Filter>sdk\Header Files</Filter>
</ClInclude>
<ClInclude Include="sdk\ipc_pe.h">
<Filter>sdk\Header Files</Filter>
</ClInclude>
<ClInclude Include="sdk\out.h">
<Filter>sdk\Header Files</Filter>
</ClInclude>
<ClInclude Include="sdk\wa_ipc.h">
<Filter>sdk\Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="resource.rc">
@ -34,6 +46,9 @@
<ClCompile Include="in_streamfile.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="in_utils.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="in_vgmstream.c">
<Filter>Source Files</Filter>
</ClCompile>

View File

@ -31,12 +31,14 @@ TARGET_EXT_LIBS += $(LIBS_TARGET_EXT_LIBS)
export CFLAGS LDFLAGS
SRCS = $(wildcard *.c)
OBJECTS = $(wildcard *.o)
###############################################################################
### targets
xmp_vgmstream: libvgmstream.a $(TARGET_EXT_LIBS)
$(CC) -shared -static-libgcc $(CFLAGS) xmp_vgmstream.c $(LDFLAGS) -o $(OUTPUT_XMPLAY) xmpin.def
$(CC) -shared -static-libgcc $(CFLAGS) $(SRCS) $(LDFLAGS) -o $(OUTPUT_XMPLAY) xmpin.def
$(STRIP) $(OUTPUT_XMPLAY)
libvgmstream.a:
@ -46,6 +48,6 @@ $(TARGET_EXT_LIBS):
$(MAKE) -C ../ext_libs $@
clean:
$(RMF) $(OUTPUT_XMPLAY)
$(RMF) $(OBJECTS) $(OUTPUT_XMPLAY)
.PHONY: clean xmp_vgmstream libvgmstream.a $(TARGET_EXT_LIBS)

View File

@ -69,8 +69,11 @@
<ItemGroup>
<ClInclude Include="xmpfunc.h" />
<ClInclude Include="xmpin.h" />
<ClInclude Include="xmp_vgmstream.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="xmp_streamfile.c" />
<ClCompile Include="xmp_utils.c" />
<ClCompile Include="xmp_vgmstream.c" />
</ItemGroup>
<ItemGroup>

View File

@ -1,11 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="xmp_vgmstream.c" />
<ClCompile Include="xmp_streamfile.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="xmp_utils.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="xmp_vgmstream.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="xmpfunc.h" />
<ClInclude Include="xmpin.h" />
<ClInclude Include="xmpfunc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="xmpin.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="xmp_vgmstream.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="xmpin.def" />

86
xmplay/xmp_streamfile.c Normal file
View File

@ -0,0 +1,86 @@
#include "xmp_vgmstream.h"
/* a STREAMFILE that operates via XMPlay's XMPFUNC_FILE+XMPFILE */
typedef struct _XMPLAY_STREAMFILE {
STREAMFILE sf; /* callbacks */
XMPFILE infile; /* actual FILE */
XMPFUNC_FILE* xmpf_file; /* helper */
char name[0x8000]; /* path limit */
off_t offset; /* current offset */
int internal_xmpfile; /* infile was not supplied externally and can be closed */
} XMPLAY_STREAMFILE;
static size_t xmpsf_read(XMPLAY_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) {
size_t read;
if (sf->offset != offset) {
if (sf->xmpf_file->Seek(sf->infile, offset))
sf->offset = offset;
else
sf->offset = sf->xmpf_file->Tell(sf->infile);
}
read = sf->xmpf_file->Read(sf->infile, dst, length);
if (read > 0)
sf->offset += read;
return read;
}
static off_t xmpsf_get_size(XMPLAY_STREAMFILE* sf) {
return sf->xmpf_file->GetSize(sf->infile);
}
static off_t xmpsf_get_offset(XMPLAY_STREAMFILE* sf) {
return sf->xmpf_file->Tell(sf->infile);
}
static void xmpsf_get_name(XMPLAY_STREAMFILE* sf, char* buffer, size_t length) {
snprintf(buffer, length, "%s", sf->name);
buffer[length - 1] = '\0';
}
static STREAMFILE* xmpsf_open(XMPLAY_STREAMFILE* sf, const char* const filename, size_t buffersize) {
XMPFILE newfile;
if (!filename)
return NULL;
newfile = sf->xmpf_file->Open(filename);
if (!newfile) return NULL;
return open_xmplay_streamfile_by_xmpfile(newfile, sf->xmpf_file, filename, true); /* internal XMPFILE */
}
static void xmpsf_close(XMPLAY_STREAMFILE* sf) {
/* Close XMPFILE, but only if we opened it (ex. for subfiles inside metas).
* Otherwise must be left open as other parts of XMPlay need it and would crash. */
if (sf->internal_xmpfile) {
sf->xmpf_file->Close(sf->infile);
}
free(sf);
}
STREAMFILE* open_xmplay_streamfile_by_xmpfile(XMPFILE infile, XMPFUNC_FILE* xmpf_file, const char* path, bool internal) {
XMPLAY_STREAMFILE* this_sf = calloc(1, sizeof(XMPLAY_STREAMFILE));
if (!this_sf) return NULL;
this_sf->sf.read = (void*)xmpsf_read;
this_sf->sf.get_size = (void*)xmpsf_get_size;
this_sf->sf.get_offset = (void*)xmpsf_get_offset;
this_sf->sf.get_name = (void*)xmpsf_get_name;
this_sf->sf.open = (void*)xmpsf_open;
this_sf->sf.close = (void*)xmpsf_close;
this_sf->infile = infile;
this_sf->offset = 0;
snprintf(this_sf->name, sizeof(this_sf->name), "%s", path);
this_sf->name[sizeof(this_sf->name) - 1] = '\0';
this_sf->internal_xmpfile = internal;
this_sf->xmpf_file = xmpf_file;
return &this_sf->sf; /* pointer to STREAMFILE start = rest of the custom data follows */
}

262
xmplay/xmp_utils.c Normal file
View File

@ -0,0 +1,262 @@
#include "xmp_vgmstream.h"
/* with <v3.8.5.62, any more than ~1000 will crash XMplay's file list screen. */
#define EXTENSION_LIST_SIZE_OLD 1000
#define EXTENSION_LIST_SIZE_OLD_VERSION 0x0308053d /* less than v3.8.5.62 */
/* Adds ext to XMPlay's extension list. */
static int add_extension(int length, char * dst, const char * ext) {
int ext_len;
int i;
if (length <= 1)
return 0;
ext_len = strlen(ext);
/* check if end reached or not enough room to add */
if (ext_len+2 > length-2) {
dst[0]='\0';
return 0;
}
/* copy new extension + null terminate */
for (i=0; i < ext_len; i++)
dst[i] = ext[i];
dst[i]='/';
dst[i+1]='\0';
return i+1;
}
/* Creates XMPlay's extension list, a single string with 2 nulls.
* Extensions must be in this format: "Description\0extension1/.../extensionN" */
void build_extension_list(char* extension_list, int list_size, DWORD version) {
const char ** ext_list;
size_t ext_list_len;
int i, written;
int limit_old = EXTENSION_LIST_SIZE_OLD;
int limit = list_size;
if (limit > limit_old && version <= EXTENSION_LIST_SIZE_OLD_VERSION)
limit = limit_old;
written = sprintf(extension_list, "%s%c", "vgmstream files",'\0');
ext_list = vgmstream_get_formats(&ext_list_len);
for (i=0; i < ext_list_len; i++) {
written += add_extension(limit-written, extension_list + written, ext_list[i]);
}
extension_list[written-1] = '\0'; /* remove last "/" */
}
/* Get tags as an array of "key\0value\0", NULL-terminated.
* Processes tags from the path alone (expects folders to be named in a particular way),
* so of limited usefulness. */
char* get_tags_from_filepath_info(VGMSTREAM* infostream, XMPFUNC_MISC* xmpf_misc, const char* filepath) {
char* tags;
int pos = 0;
tags = xmpf_misc->Alloc(1024);
memset(tags, 0x00, 1024);
if (strlen(infostream->stream_name) > 0) {
memcpy(tags + pos, "title", 5);
pos += 6;
memcpy(tags + pos, infostream->stream_name, strlen(infostream->stream_name));
pos += strlen(infostream->stream_name) + 1;
}
const char* end;
const char* start = NULL;
int j = 2;
for (const char* i = filepath + strlen(filepath); i > filepath; i--)
{
if ((*i == '\\') && (j == 1))
{
start = i + 1;
j--;
break;
}
if ((*i == '\\') && (j == 2))
{
end = i;
j--;
}
}
//run some sanity checks
int brace_curly = 0, brace_square = 0;
char check_ok = 0;
for (const char* i = filepath; *i != 0; i++)
{
if (*i == '(')
brace_curly++;
if (*i == ')')
brace_curly--;
if (*i == '[')
brace_square++;
if (*i == ']')
brace_square--;
if (brace_curly > 1 || brace_curly < -1 || brace_square > 1 || brace_square < -1)
break;
}
if (brace_square == 0 && brace_curly == 0)
check_ok = 1;
if (start != NULL && strstr(filepath, "\\VGMPP\\") != NULL && check_ok == 1 && strchr(start, '(') != NULL)
{
char tagline[1024];
memset(tagline, 0x00, sizeof(tagline));
strncpy(tagline, start, end - start);
char* alttitle_st;
char* alttitle_ed;
char* album_st;
char* album_ed;
char* company_st;
char* company_ed;
char* company2_st;
char* company2_ed;
char* date_st;
char* date_ed;
char* platform_st;
char* platform_ed;
if (strchr(tagline, '[') != NULL) //either alternative title or platform usually
{
alttitle_st = strchr(tagline, '[') + 1;
alttitle_ed = strchr(alttitle_st, ']');
if (strchr(alttitle_st, '[') != NULL && strchr(alttitle_st, '[') > strchr(alttitle_st, '(')) //both might be present actually
{
platform_st = strchr(alttitle_st, '[') + 1;
platform_ed = strchr(alttitle_ed + 1, ']');
}
else
{
platform_st = NULL;
platform_ed = NULL;
}
}
else
{
platform_st = NULL;
platform_ed = NULL;
alttitle_st = NULL;
alttitle_ed = NULL;
}
album_st = tagline;
if (strchr(tagline, '(') < alttitle_st && alttitle_st != NULL) //square braces after curly braces -- platform
{
platform_st = alttitle_st;
platform_ed = alttitle_ed;
alttitle_st = NULL;
alttitle_ed = NULL;
album_ed = strchr(tagline, '('); //get normal title for now
}
else if (alttitle_st != NULL)
album_ed = strchr(tagline, '[');
else
album_ed = strchr(tagline, '(');
date_st = strchr(album_ed, '(') + 1; //first string in curly braces is usualy release date, I have one package with platform name there
if (date_st == NULL)
date_ed = NULL;
if (date_st[0] >= 0x30 && date_st[0] <= 0x39 && date_st[1] >= 0x30 && date_st[1] <= 0x39) //check if it contains 2 digits
{
date_ed = strchr(date_st, ')');
}
else //platform?
{
platform_st = date_st;
platform_ed = strchr(date_st, ')');
date_st = strchr(platform_ed, '(') + 1;
date_ed = strchr(date_st, ')');
}
company_st = strchr(date_ed, '(') + 1; //company name follows date
if (company_st != NULL)
{
company_ed = strchr(company_st, ')');
if (strchr(company_ed, '(') != NULL)
{
company2_st = strchr(company_ed, '(') + 1;
company2_ed = strchr(company2_st, ')');
}
else
{
company2_st = NULL;
company2_ed = NULL;
}
}
else
{
company_st = NULL;
company_ed = NULL;
company2_st = NULL;
company2_ed = NULL;
}
if (alttitle_st != NULL) //prefer alternative title, which is usually japanese
{
memcpy(tags + pos, "album", 5);
pos += 6;
memcpy(tags + pos, alttitle_st, alttitle_ed - alttitle_st);
pos += alttitle_ed - alttitle_st + 1;
}
else
{
memcpy(tags + pos, "album", 5);
pos += 6;
memcpy(tags + pos, album_st, album_ed - album_st);
pos += album_ed - album_st + 1;
}
if (date_st != NULL)
{
memcpy(tags + pos, "date", 4);
pos += 5;
memcpy(tags + pos, date_st, date_ed - date_st);
pos += date_ed - date_st + 1;
}
if (date_st != NULL)
{
memcpy(tags + pos, "genre", 5);
pos += 6;
memcpy(tags + pos, platform_st, platform_ed - platform_st);
pos += platform_ed - platform_st + 1;
}
if (company_st != NULL)
{
char combuf[256];
memset(combuf, 0x00, sizeof(combuf));
char tmp[128];
memset(tmp, 0x00, sizeof(tmp));
memcpy(tmp, company_st, company_ed - company_st);
if (company2_st != NULL)
{
char tmp2[128];
memset(tmp2, 0x00, sizeof(tmp2));
memcpy(tmp2, company2_st, company2_ed - company2_st);
sprintf(combuf, "\r\n\r\nDeveloper\t%s\r\nPublisher\t%s", tmp, tmp2);
}
else
sprintf(combuf, "\r\n\r\nDeveloper\t%s", tmp);
memcpy(tags + pos, "comment", 7);
pos += 8;
memcpy(tags + pos, combuf, strlen(combuf));
pos += strlen(combuf) + 1;
}
}
return tags; /* assuming XMPlay free()s this, since it Alloc()s it */
}

View File

@ -11,10 +11,7 @@
#include <string.h>
#include <ctype.h>
#include "xmpin.h"
#include "../src/vgmstream.h"
#include "../src/api.h"
#include "xmp_vgmstream.h"
#include "../version.h"
#ifndef VGMSTREAM_VERSION
@ -28,20 +25,19 @@
#define SAMPLE_BUFFER_SIZE 1024
/* XMPlay extension list, only needed to associate extensions in Windows */
/* with <v3.8.5.62, any more than ~1000 will crash XMplay's file list screen. */
#define EXTENSION_LIST_SIZE (0x2000 * 6)
#define EXTENSION_LIST_SIZE_OLD 1000
#define EXTENSION_LIST_SIZE_OLD_VERSION 0x0308053d /* less than v3.8.5.62 */
#define XMPLAY_MAX_PATH 32768
/* XMPlay function library */
static XMPFUNC_IN *xmpfin;
static XMPFUNC_MISC *xmpfmisc;
static XMPFUNC_FILE *xmpffile;
static XMPFUNC_IN* xmpf_in;
static XMPFUNC_MISC* xmpf_misc;
static XMPFUNC_FILE* xmpf_file;
/* XMPlay extension list, only needed to associate extensions in Windows */
#define EXTENSION_LIST_SIZE (0x2000 * 6)
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
char filepath[MAX_PATH];
char last_filepath[MAX_PATH] = {0};
/* plugin config */
double fade_seconds = 10.0;
@ -66,96 +62,11 @@ static int shownerror = 0; /* init error */
/* ************************************* */
/* a STREAMFILE that operates via XMPlay's XMPFUNC_FILE+XMPFILE */
typedef struct _XMPLAY_STREAMFILE {
STREAMFILE sf; /* callbacks */
XMPFILE infile; /* actual FILE */
char name[PATH_LIMIT];
off_t offset; /* current offset */
int internal_xmpfile; /* infile was not supplied externally and can be closed */
} XMPLAY_STREAMFILE;
static STREAMFILE* open_xmplay_streamfile_by_xmpfile(XMPFILE file, const char* path, int internal);
static size_t xmpsf_read(XMPLAY_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) {
size_t read;
if (sf->offset != offset) {
if (xmpffile->Seek(sf->infile, offset))
sf->offset = offset;
else
sf->offset = xmpffile->Tell(sf->infile);
}
read = xmpffile->Read(sf->infile, dst, length);
if (read > 0)
sf->offset += read;
return read;
}
static off_t xmpsf_get_size(XMPLAY_STREAMFILE* sf) {
return xmpffile->GetSize(sf->infile);
}
static off_t xmpsf_get_offset(XMPLAY_STREAMFILE* sf) {
return xmpffile->Tell(sf->infile);
}
static void xmpsf_get_name(XMPLAY_STREAMFILE* sf, char* buffer, size_t length) {
strncpy(buffer, sf->name, length);
buffer[length-1] = '\0';
}
static STREAMFILE* xmpsf_open(XMPLAY_STREAMFILE* sf, const char* const filename, size_t buffersize) {
XMPFILE newfile;
if (!filename)
return NULL;
newfile = xmpffile->Open(filename);
if (!newfile) return NULL;
strncpy(filepath, filename, MAX_PATH);
filepath[MAX_PATH - 1] = 0x00;
return open_xmplay_streamfile_by_xmpfile(newfile, filename, 1); /* internal XMPFILE */
}
static void xmpsf_close(XMPLAY_STREAMFILE* sf) {
/* Close XMPFILE, but only if we opened it (ex. for subfiles inside metas).
* Otherwise must be left open as other parts of XMPlay need it and would crash. */
if (sf->internal_xmpfile) {
xmpffile->Close(sf->infile);
}
free(sf);
}
static STREAMFILE* open_xmplay_streamfile_by_xmpfile(XMPFILE infile, const char* path, int internal) {
XMPLAY_STREAMFILE* this_sf = calloc(1, sizeof(XMPLAY_STREAMFILE));
if (!this_sf) return NULL;
this_sf->sf.read = (void*)xmpsf_read;
this_sf->sf.get_size = (void*)xmpsf_get_size;
this_sf->sf.get_offset = (void*)xmpsf_get_offset;
this_sf->sf.get_name = (void*)xmpsf_get_name;
this_sf->sf.open = (void*)xmpsf_open;
this_sf->sf.close = (void*)xmpsf_close;
this_sf->infile = infile;
this_sf->offset = 0;
strncpy(this_sf->name, path, sizeof(this_sf->name));
this_sf->internal_xmpfile = internal;
return &this_sf->sf; /* pointer to STREAMFILE start = rest of the custom data follows */
}
VGMSTREAM* init_vgmstream_xmplay(XMPFILE infile, const char* path, int subsong) {
STREAMFILE* sf = NULL;
VGMSTREAM* vgmstream = NULL;
sf = open_xmplay_streamfile_by_xmpfile(infile, path, 0); /* external XMPFILE */
sf = open_xmplay_streamfile_by_xmpfile(infile, xmpf_file, path, false); /* external XMPFILE */
if (!sf) return NULL;
sf->stream_index = subsong;
@ -184,259 +95,6 @@ static void apply_config(VGMSTREAM* vgmstream) {
vgmstream_apply_config(vgmstream, &vcfg);
}
/* get the tags as an array of "key\0value\0", NULL-terminated */
static char *get_tags(VGMSTREAM * infostream) {
char* tags;
int pos = 0;
tags = xmpfmisc->Alloc(1024);
memset(tags, 0x00, 1024);
if (infostream->stream_name != NULL && strlen(infostream->stream_name) > 0)
{
memcpy(tags + pos, "title", 5);
pos += 6;
memcpy(tags + pos, infostream->stream_name, strlen(infostream->stream_name));
pos += strlen(infostream->stream_name) + 1;
}
char* end;
char* start = NULL;
int j = 2;
for (char* i = filepath+strlen(filepath); i > filepath; i--)
{
if ((*i == '\\') && (j == 1))
{
start = i + 1;
j--;
break;
}
if ((*i == '\\') && (j == 2))
{
end = i;
j--;
}
}
//run some sanity checks
int brace_curly = 0, brace_square = 0;
char check_ok = 0;
for (char* i = filepath; *i != 0; i++)
{
if (*i == '(')
brace_curly++;
if (*i == ')')
brace_curly--;
if (*i == '[')
brace_square++;
if (*i == ']')
brace_square--;
if (brace_curly > 1 || brace_curly < -1 || brace_square > 1 || brace_square < -1)
break;
}
if (brace_square == 0 && brace_curly == 0)
check_ok = 1;
if (start != NULL && strstr(filepath, "\\VGMPP\\") != NULL && check_ok == 1 && strchr(start, '(') != NULL)
{
char tagline[1024];
memset(tagline, 0x00, sizeof(tagline));
strncpy(tagline, start, end - start);
char* alttitle_st;
char* alttitle_ed;
char* album_st;
char* album_ed;
char* company_st;
char* company_ed;
char* company2_st;
char* company2_ed;
char* date_st;
char* date_ed;
char* platform_st;
char* platform_ed;
if (strchr(tagline, '[') != NULL) //either alternative title or platform usually
{
alttitle_st = strchr(tagline, '[') + 1;
alttitle_ed = strchr(alttitle_st, ']');
if (strchr(alttitle_st, '[') != NULL && strchr(alttitle_st, '[') > strchr(alttitle_st, '(')) //both might be present actually
{
platform_st = strchr(alttitle_st, '[') + 1;
platform_ed = strchr(alttitle_ed + 1, ']');
}
else
{
platform_st = NULL;
platform_ed = NULL;
}
}
else
{
platform_st = NULL;
platform_ed = NULL;
alttitle_st = NULL;
alttitle_ed = NULL;
}
album_st = tagline;
if (strchr(tagline, '(') < alttitle_st && alttitle_st != NULL) //square braces after curly braces -- platform
{
platform_st = alttitle_st;
platform_ed = alttitle_ed;
alttitle_st = NULL;
alttitle_ed = NULL;
album_ed = strchr(tagline, '('); //get normal title for now
}
else if (alttitle_st != NULL)
album_ed = strchr(tagline, '[');
else
album_ed = strchr(tagline, '(');
date_st = strchr(album_ed, '(') + 1; //first string in curly braces is usualy release date, I have one package with platform name there
if (date_st == NULL)
date_ed = NULL;
if (date_st[0] >= 0x30 && date_st[0] <= 0x39 && date_st[1] >= 0x30 && date_st[1] <= 0x39) //check if it contains 2 digits
{
date_ed = strchr(date_st, ')');
}
else //platform?
{
platform_st = date_st;
platform_ed = strchr(date_st, ')');
date_st = strchr(platform_ed, '(') + 1;
date_ed = strchr(date_st, ')');
}
company_st = strchr(date_ed, '(') + 1; //company name follows date
if (company_st != NULL)
{
company_ed = strchr(company_st, ')');
if (strchr(company_ed, '(') != NULL)
{
company2_st = strchr(company_ed, '(') + 1;
company2_ed = strchr(company2_st, ')');
}
else
{
company2_st = NULL;
company2_ed = NULL;
}
}
else
{
company_st = NULL;
company_ed = NULL;
company2_st = NULL;
company2_ed = NULL;
}
if (alttitle_st != NULL) //prefer alternative title, which is usually japanese
{
memcpy(tags + pos, "album", 5);
pos += 6;
memcpy(tags + pos, alttitle_st, alttitle_ed - alttitle_st);
pos += alttitle_ed - alttitle_st + 1;
}
else
{
memcpy(tags + pos, "album", 5);
pos += 6;
memcpy(tags + pos, album_st, album_ed - album_st);
pos += album_ed - album_st + 1;
}
if (date_st != NULL)
{
memcpy(tags + pos, "date", 4);
pos += 5;
memcpy(tags + pos, date_st, date_ed - date_st);
pos += date_ed - date_st + 1;
}
if (date_st != NULL)
{
memcpy(tags + pos, "genre", 5);
pos += 6;
memcpy(tags + pos, platform_st, platform_ed - platform_st);
pos += platform_ed - platform_st + 1;
}
if (company_st != NULL)
{
char combuf[256];
memset(combuf, 0x00, sizeof(combuf));
char tmp[128];
memset(tmp, 0x00, sizeof(tmp));
memcpy(tmp, company_st, company_ed - company_st);
if (company2_st != NULL)
{
char tmp2[128];
memset(tmp2, 0x00, sizeof(tmp2));
memcpy(tmp2, company2_st, company2_ed - company2_st);
sprintf(combuf, "\r\n\r\nDeveloper\t%s\r\nPublisher\t%s", tmp, tmp2);
}
else
sprintf(combuf, "\r\n\r\nDeveloper\t%s", tmp);
memcpy(tags + pos, "comment", 7);
pos += 8;
memcpy(tags + pos, combuf, strlen(combuf));
pos += strlen(combuf) + 1;
}
}
return tags; /* assuming XMPlay free()s this, since it Alloc()s it */
}
/* Adds ext to XMPlay's extension list. */
static int add_extension(int length, char * dst, const char * ext) {
int ext_len;
int i;
if (length <= 1)
return 0;
ext_len = strlen(ext);
/* check if end reached or not enough room to add */
if (ext_len+2 > length-2) {
dst[0]='\0';
return 0;
}
/* copy new extension + null terminate */
for (i=0; i < ext_len; i++)
dst[i] = ext[i];
dst[i]='/';
dst[i+1]='\0';
return i+1;
}
/* Creates XMPlay's extension list, a single string with 2 nulls.
* Extensions must be in this format: "Description\0extension1/.../extensionN" */
static void build_extension_list() {
const char ** ext_list;
size_t ext_list_len;
int i, written;
int limit = EXTENSION_LIST_SIZE;
if (xmpfmisc->GetVersion() <= EXTENSION_LIST_SIZE_OLD_VERSION)
limit = EXTENSION_LIST_SIZE_OLD;
written = sprintf(working_extension_list, "%s%c", "vgmstream files",'\0');
ext_list = vgmstream_get_formats(&ext_list_len);
for (i=0; i < ext_list_len; i++) {
written += add_extension(limit-written, working_extension_list + written, ext_list[i]);
}
working_extension_list[written-1] = '\0'; /* remove last "/" */
}
/* ************************************* */
/* info for the "about" button in plugin options */
@ -487,6 +145,11 @@ DWORD WINAPI xmplay_GetFileInfo(const char *filename, XMPFILE file, float **leng
if (!infostream)
return 0;
char temp_filepath[MAX_PATH];
snprintf(temp_filepath, sizeof(temp_filepath), "%s", filename);
temp_filepath[sizeof(temp_filepath) - 1] = '\0';
apply_config(infostream);
//vgmstream_mixing_autodownmix(infostream, downmix_channels);
@ -494,7 +157,7 @@ DWORD WINAPI xmplay_GetFileInfo(const char *filename, XMPFILE file, float **leng
if (length && infostream->sample_rate) {
int length_samples = vgmstream_get_samples(infostream);
float *lens = (float*)xmpfmisc->Alloc(sizeof(float));
float *lens = (float*)xmpf_misc->Alloc(sizeof(float));
lens[0] = (float)length_samples / (float)infostream->sample_rate;
*length = lens;
}
@ -503,7 +166,7 @@ DWORD WINAPI xmplay_GetFileInfo(const char *filename, XMPFILE file, float **leng
if (disable_subsongs || subsong_count == 0)
subsong_count = 1;
*tags = get_tags(infostream);
*tags = get_tags_from_filepath_info(infostream, xmpf_misc, temp_filepath);
close_vgmstream(infostream);
return subsong_count;
@ -518,6 +181,9 @@ DWORD WINAPI xmplay_Open(const char *filename, XMPFILE file) {
if (!vgmstream)
return 0;
snprintf(last_filepath, sizeof(last_filepath), "%s", filename);
last_filepath[sizeof(last_filepath) - 1] = '\0';
apply_config(vgmstream);
//vgmstream_mixing_autodownmix(vgmstream, downmix_channels);
@ -533,7 +199,7 @@ DWORD WINAPI xmplay_Open(const char *filename, XMPFILE file) {
if (length_samples) {
float length = (float)length_samples / (float)vgmstream->sample_rate;
xmpfin->SetLength(length, TRUE);
xmpf_in->SetLength(length, TRUE);
}
return 1;
@ -553,8 +219,8 @@ void WINAPI xmplay_SetFormat(XMPFORMAT *form) {
}
/* get tags, return NULL to delay title update (OPTIONAL) */
char * WINAPI xmplay_GetTags() {
return get_tags(vgmstream);
char* WINAPI xmplay_GetTags() {
return get_tags_from_filepath_info(vgmstream, xmpf_misc, last_filepath);
}
/* main panel info text (short file info) */
@ -677,7 +343,7 @@ double WINAPI xmplay_SetPosition(DWORD pos) {
/* decode some sample data */
DWORD WINAPI xmplay_Process(float* buf, DWORD bufsize) {
int32_t i, done, samples_to_do;
BOOL do_loop = xmpfin->GetLooping();
BOOL do_loop = xmpf_in->GetLooping();
float *sbuf = buf;
@ -794,11 +460,12 @@ __declspec(dllexport) XMPIN* WINAPI XMPIN_GetInterface(UINT32 face, InterfacePr
return NULL;
}
xmpfin = (XMPFUNC_IN*)faceproc(XMPFUNC_IN_FACE);
xmpfmisc = (XMPFUNC_MISC*)faceproc(XMPFUNC_MISC_FACE);
xmpffile = (XMPFUNC_FILE*)faceproc(XMPFUNC_FILE_FACE);
/* retrieval of xmp helpers */
xmpf_in = (XMPFUNC_IN*)faceproc(XMPFUNC_IN_FACE);
xmpf_misc = (XMPFUNC_MISC*)faceproc(XMPFUNC_MISC_FACE);
xmpf_file = (XMPFUNC_FILE*)faceproc(XMPFUNC_FILE_FACE);
build_extension_list();
build_extension_list(working_extension_list, sizeof(working_extension_list), xmpf_misc->GetVersion());
return &vgmstream_xmpin;
}

16
xmplay/xmp_vgmstream.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef _XMP_VGMSTREAM_
#define _XMP_VGMSTREAM_
#include "xmpin.h"
#include "xmpfunc.h"
#include "../src/vgmstream.h"
#include "../src/api.h"
STREAMFILE* open_xmplay_streamfile_by_xmpfile(XMPFILE infile, XMPFUNC_FILE* xmpf_file, const char* path, bool internal);
char* get_tags_from_filepath_info(VGMSTREAM* infostream, XMPFUNC_MISC* xmpf_misc, const char* filepath);
void build_extension_list(char* extension_list, int list_size, DWORD version);
#endif