Basic support for GB cart loader command.

Definition of GB carts loaded inside transferpak is done through the
mupen64plus.cfg file as follow :

[TransferPak]
GB-rom-1 = "path/to/gb_rom.gb"
GB-ram-1 = "path/to/gb_rom.sav"
GB-rom-2 ...
GB-ram-2 ...

Setting an empty GB ROM acts as if GB cart was removed form TransferPak.
Setting an empty GB RAM let the Core generate a blank save file.

These parameters are modifiable during emulator execution. You just need
to trigger the GB cart change button, and these parameters will be
reloaded.

You can also specify these at program startup using command-line
arguments --gb-{rom,ram}-{1,2,3,4}
This commit is contained in:
Bobby Smiles 2017-10-04 00:40:11 +02:00 committed by bsmiles32
parent 36d7cd8eb3
commit 5e17accad0
5 changed files with 227 additions and 3 deletions

2
README
View file

@ -42,6 +42,8 @@ Parameters:
--emumode (mode) : set emu mode to: 0=Pure Interpreter 1=Interpreter 2=DynaRec
--testshots (list) : take screenshots at frames given in comma-separated (list), then quit
--set (param-spec) : set a configuration variable, format: ParamSection[ParamName]=Value
--gb-rom-{1,2,3,4} : define GB cart rom to load inside transferpak {1,2,3,4}"
--gb-ram-{1,2,3,4} : define GB cart ram to load inside transferpak {1,2,3,4}"
--core-compare-send : use the Core Comparison debugging feature, in data sending mode
--core-compare-recv : use the Core Comparison debugging feature, in data receiving mode
--nosaveoptions : do not save the given command-line options in configuration file

View file

@ -73,6 +73,10 @@ ptr_ConfigGetParamFloat ConfigGetParamFloat = NULL;
ptr_ConfigGetParamBool ConfigGetParamBool = NULL;
ptr_ConfigGetParamString ConfigGetParamString = NULL;
ptr_ConfigExternalOpen ConfigExternalOpen = NULL;
ptr_ConfigExternalClose ConfigExternalClose = NULL;
ptr_ConfigExternalGetParameter ConfigExternalGetParameter = NULL;
ptr_ConfigGetSharedDataFilepath ConfigGetSharedDataFilepath = NULL;
ptr_ConfigGetUserConfigPath ConfigGetUserConfigPath = NULL;
ptr_ConfigGetUserDataPath ConfigGetUserDataPath = NULL;
@ -239,6 +243,10 @@ m64p_error AttachCoreLib(const char *CoreLibFilepath)
ConfigGetParamBool = (ptr_ConfigGetParamBool) osal_dynlib_getproc(CoreHandle, "ConfigGetParamBool");
ConfigGetParamString = (ptr_ConfigGetParamString) osal_dynlib_getproc(CoreHandle, "ConfigGetParamString");
ConfigExternalOpen = (ptr_ConfigExternalOpen) osal_dynlib_getproc(CoreHandle, "ConfigExternalOpen");
ConfigExternalClose = (ptr_ConfigExternalClose) osal_dynlib_getproc(CoreHandle, "ConfigExternalClose");
ConfigExternalGetParameter = (ptr_ConfigExternalGetParameter) osal_dynlib_getproc(CoreHandle, "ConfigExternalGetParameter");
ConfigGetSharedDataFilepath = (ptr_ConfigGetSharedDataFilepath) osal_dynlib_getproc(CoreHandle, "ConfigGetSharedDataFilepath");
ConfigGetUserConfigPath = (ptr_ConfigGetUserConfigPath) osal_dynlib_getproc(CoreHandle, "ConfigGetUserConfigPath");
ConfigGetUserDataPath = (ptr_ConfigGetUserDataPath) osal_dynlib_getproc(CoreHandle, "ConfigGetUserDataPath");
@ -304,6 +312,10 @@ m64p_error DetachCoreLib(void)
ConfigGetParamBool = NULL;
ConfigGetParamString = NULL;
ConfigExternalOpen = NULL;
ConfigExternalClose = NULL;
ConfigExternalGetParameter = NULL;
ConfigGetSharedDataFilepath = NULL;
ConfigGetUserDataPath = NULL;
ConfigGetUserCachePath = NULL;

View file

@ -70,6 +70,10 @@ extern ptr_ConfigGetParamFloat ConfigGetParamFloat;
extern ptr_ConfigGetParamBool ConfigGetParamBool;
extern ptr_ConfigGetParamString ConfigGetParamString;
extern ptr_ConfigExternalOpen ConfigExternalOpen;
extern ptr_ConfigExternalClose ConfigExternalClose;
extern ptr_ConfigExternalGetParameter ConfigExternalGetParameter;
extern ptr_ConfigGetSharedDataFilepath ConfigGetSharedDataFilepath;
extern ptr_ConfigGetUserConfigPath ConfigGetUserConfigPath;
extern ptr_ConfigGetUserDataPath ConfigGetUserDataPath;

View file

@ -22,9 +22,9 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* This is the main application entry point for the console-only front-end
* for Mupen64Plus v2.0.
* for Mupen64Plus v2.0.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@ -37,6 +37,7 @@
#include "m64p_types.h"
#include "main.h"
#include "osal_preproc.h"
#include "osal_files.h"
#include "plugin.h"
#include "version.h"
@ -56,6 +57,7 @@ int g_Verbose = 0;
static m64p_handle l_ConfigCore = NULL;
static m64p_handle l_ConfigVideo = NULL;
static m64p_handle l_ConfigUI = NULL;
static m64p_handle l_ConfigTransferPak = NULL;
static const char *l_CoreLibPath = NULL;
static const char *l_ConfigDirPath = NULL;
@ -151,6 +153,69 @@ static void FrameCallback(unsigned int FrameIndex)
}
}
static char *formatstr(const char *fmt, ...)
{
int size = 128, ret;
char *str = (char *)malloc(size), *newstr;
va_list args;
/* There are two implementations of vsnprintf we have to deal with:
* C99 version: Returns the number of characters which would have been written
* if the buffer had been large enough, and -1 on failure.
* Windows version: Returns the number of characters actually written,
* and -1 on failure or truncation.
* NOTE: An implementation equivalent to the Windows one appears in glibc <2.1.
*/
while (str != NULL)
{
va_start(args, fmt);
ret = vsnprintf(str, size, fmt, args);
va_end(args);
// Successful result?
if (ret >= 0 && ret < size)
return str;
// Increment the capacity of the buffer
if (ret >= size)
size = ret + 1; // C99 version: We got the needed buffer size
else
size *= 2; // Windows version: Keep guessing
newstr = (char *)realloc(str, size);
if (newstr == NULL)
free(str);
str = newstr;
}
return NULL;
}
static int is_path_separator(char c)
{
return strchr(OSAL_DIR_SEPARATORS, c) != NULL;
}
char* combinepath(const char* first, const char *second)
{
size_t len_first, off_second = 0;
if (first == NULL || second == NULL)
return NULL;
len_first = strlen(first);
while (is_path_separator(first[len_first-1]))
len_first--;
while (is_path_separator(second[off_second]))
off_second++;
return formatstr("%.*s%c%s", (int) len_first, first, OSAL_DIR_SEPARATORS[0], second + off_second);
}
/*********************************************************************************************************
* Configuration handling
*/
@ -160,6 +225,7 @@ static m64p_error OpenConfigurationHandles(void)
float fConfigParamsVersion;
int bSaveConfig = 0;
m64p_error rval;
unsigned int i;
/* Open Configuration sections for core library and console User Interface */
rval = (*ConfigOpenSection)("Core", &l_ConfigCore);
@ -176,6 +242,13 @@ static m64p_error OpenConfigurationHandles(void)
return rval;
}
rval = (*ConfigOpenSection)("Transferpak", &l_ConfigTransferPak);
if (rval != M64ERR_SUCCESS)
{
DebugMessage(M64MSG_ERROR, "failed to open 'Transferpak' configuration section");
return rval;
}
rval = (*ConfigOpenSection)("UI-Console", &l_ConfigUI);
if (rval != M64ERR_SUCCESS)
{
@ -214,8 +287,25 @@ static m64p_error OpenConfigurationHandles(void)
(*ConfigSetDefaultString)(l_ConfigUI, "InputPlugin", "mupen64plus-input-sdl" OSAL_DLL_EXTENSION, "Filename of input plugin");
(*ConfigSetDefaultString)(l_ConfigUI, "RspPlugin", "mupen64plus-rsp-hle" OSAL_DLL_EXTENSION, "Filename of RSP plugin");
if (bSaveConfig && ConfigSaveSection != NULL) /* ConfigSaveSection was added in Config API v2.1.0 */
for(i = 1; i < 5; ++i) {
char key[64];
char desc[2048];
#define SET_DEFAULT_STRING(key_fmt, default_value, desc_fmt) \
do { \
snprintf(key, sizeof(key), key_fmt, i); \
snprintf(desc, sizeof(desc), desc_fmt, i); \
(*ConfigSetDefaultString)(l_ConfigTransferPak, key, default_value, desc); \
} while(0)
SET_DEFAULT_STRING("GB-rom-%u", "", "Filename of the GB ROM to load into transferpak %u");
SET_DEFAULT_STRING("GB-ram-%u", "", "Filename of the GB RAM to load into transferpak %u");
#undef SET_DEFAULT_STRING
}
if (bSaveConfig && ConfigSaveSection != NULL) { /* ConfigSaveSection was added in Config API v2.1.0 */
(*ConfigSaveSection)("UI-Console");
(*ConfigSaveSection)("Transferpak");
}
return M64ERR_SUCCESS;
}
@ -270,6 +360,8 @@ static void printUsage(const char *progname)
" --savestate (filepath) : savestate loaded at startup\n"
" --testshots (list) : take screenshots at frames given in comma-separated (list), then quit\n"
" --set (param-spec) : set a configuration variable, format: ParamSection[ParamName]=Value\n"
" --gb-rom-{1,2,3,4} : define GB cart rom to load inside transferpak {1,2,3,4}\n"
" --gb-ram-{1,2,3,4} : define GB cart ram to load inside transferpak {1,2,3,4}\n"
" --core-compare-send : use the Core Comparison debugging feature, in data sending mode\n"
" --core-compare-recv : use the Core Comparison debugging feature, in data receiving mode\n"
" --nosaveoptions : do not save the given command-line options in configuration file\n"
@ -584,6 +676,21 @@ static m64p_error ParseCommandLineFinal(int argc, const char **argv)
{
l_SaveOptions = 0;
}
#define PARSE_GB_CART_PARAM(param, key) \
else if (strcmp(argv[i], param) == 0) \
{ \
ConfigSetParameter(l_ConfigTransferPak, key, M64TYPE_STRING, argv[i+1]); \
i++; \
}
PARSE_GB_CART_PARAM("--gb-rom-1", "GB-rom-1")
PARSE_GB_CART_PARAM("--gb-ram-1", "GB-ram-1")
PARSE_GB_CART_PARAM("--gb-rom-2", "GB-rom-2")
PARSE_GB_CART_PARAM("--gb-ram-2", "GB-ram-2")
PARSE_GB_CART_PARAM("--gb-rom-3", "GB-rom-3")
PARSE_GB_CART_PARAM("--gb-ram-3", "GB-ram-3")
PARSE_GB_CART_PARAM("--gb-rom-4", "GB-rom-4")
PARSE_GB_CART_PARAM("--gb-ram-4", "GB-ram-4")
#undef PARSE_GB_CART_PARAM
else if (ArgsLeft == 0)
{
/* this is the last arg, it should be a ROM filename */
@ -606,6 +713,79 @@ static m64p_error ParseCommandLineFinal(int argc, const char **argv)
return M64ERR_INPUT_INVALID;
}
static char* gb_cart_loader_get_mem_file(void* cb_data, const char* mem, int control_id)
{
#define MUPEN64PLUS_CFG_NAME "mupen64plus.cfg"
m64p_handle core_config;
char key[64];
char value[4096];
const char* configdir = NULL;
char* cfgfilepath = NULL;
/* reset ROM filename */
char* mem_filename = NULL;
snprintf(key, sizeof(key), "GB-%s-%u", mem, control_id + 1);
/* XXX: use external config API to force reload of file content */
configdir = ConfigGetUserConfigPath();
if (configdir == NULL) {
DebugMessage(M64MSG_ERROR, "Can't get user config path !");
return NULL;
}
cfgfilepath = combinepath(configdir, MUPEN64PLUS_CFG_NAME);
if (cfgfilepath == NULL) {
DebugMessage(M64MSG_ERROR, "Can't get config file path: %s + %s!", configdir, MUPEN64PLUS_CFG_NAME);
return NULL;
}
if (ConfigExternalOpen(cfgfilepath, &core_config) != M64ERR_SUCCESS) {
DebugMessage(M64MSG_ERROR, "Can't open config file %s!", cfgfilepath);
goto release_cfgfilepath;
}
if (ConfigExternalGetParameter(core_config, "Transferpak", key, value, sizeof(value)) != M64ERR_SUCCESS) {
DebugMessage(M64MSG_ERROR, "Can't get parameter %s", key);
goto close_config;
}
size_t len = strlen(value);
if (len < 2 || value[0] != '"' || value[len-1] != '"') {
DebugMessage(M64MSG_ERROR, "Invalid string format %s", value);
goto close_config;
}
value[len-1] = '\0';
mem_filename = strdup(value + 1);
ConfigSetParameter(l_ConfigTransferPak, key, M64TYPE_STRING, mem_filename);
close_config:
ConfigExternalClose(core_config);
release_cfgfilepath:
free(cfgfilepath);
return mem_filename;
}
static char* gb_cart_loader_get_rom(void* cb_data, int control_id)
{
return gb_cart_loader_get_mem_file(cb_data, "rom", control_id);
}
static char* gb_cart_loader_get_ram(void* cb_data, int control_id)
{
return gb_cart_loader_get_mem_file(cb_data, "ram", control_id);
}
static m64p_gb_cart_loader l_gb_cart_loader =
{
NULL,
gb_cart_loader_get_rom,
gb_cart_loader_get_ram
};
/*********************************************************************************************************
* main function
*/
@ -785,6 +965,12 @@ int main(int argc, char *argv[])
}
}
/* set gb cart loader */
if ((*CoreDoCommand)(M64CMD_SET_GB_CART_LOADER, sizeof(l_gb_cart_loader), &l_gb_cart_loader) != M64ERR_SUCCESS)
{
DebugMessage(M64MSG_WARNING, "Couldn't set GB cart loader, transferpak and GB carts will not work.");
}
/* load savestate at startup */
if (l_SaveStatePath != NULL)
{

View file

@ -29,6 +29,26 @@
#include "m64p_types.h"
#include "osal_preproc.h"
/* some file-related preprocessor definitions */
#if defined(WIN32) && !defined(__MINGW32__)
#include <io.h> // For _unlink()
#define unlink _unlink
#define OSAL_DIR_SEPARATORS "\\/"
#define PATH_MAX _MAX_PATH
#else /* Not WIN32 */
#include <limits.h> // for PATH_MAX
#include <unistd.h> // for unlink()
#define OSAL_DIR_SEPARATORS "/"
/* PATH_MAX only may be defined by limits.h */
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
#endif
/* data structure for linked list of shared libraries found in a directory */
typedef struct _osal_lib_search {
char filepath[PATH_MAX];