mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 01:30:49 +01:00
cleanup
This commit is contained in:
parent
33f01354e6
commit
ea630eeead
10
src/Makefile
10
src/Makefile
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
120
winamp/in_utils.c
Normal 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;
|
||||
}
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
|
@ -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
86
xmplay/xmp_streamfile.c
Normal 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
262
xmplay/xmp_utils.c
Normal 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 */
|
||||
}
|
@ -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
16
xmplay/xmp_vgmstream.h
Normal 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
|
Loading…
Reference in New Issue
Block a user