mupen64plus-core/src/api/frontend.c
Logan McNaughton 1fccc3ba6c Netplay
2020-06-06 16:09:45 -06:00

413 lines
15 KiB
C

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Mupen64plus-core - api/frontend.c *
* Mupen64Plus homepage: https://mupen64plus.org/ *
* Copyright (C) 2012 CasualJames *
* Copyright (C) 2009 Richard Goedeken *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* This file contains the Core front-end functions which will be exported
* outside of the core library.
*/
#include <SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <md5.h>
#define M64P_CORE_PROTOTYPES 1
#include "callbacks.h"
#include "config.h"
#include "m64p_config.h"
#include "m64p_frontend.h"
#include "m64p_types.h"
#include "main/cheat.h"
#include "main/eventloop.h"
#include "main/main.h"
#include "main/rom.h"
#include "main/savestates.h"
#include "main/util.h"
#include "main/version.h"
#include "main/workqueue.h"
#include "main/screenshot.h"
#include "main/netplay.h"
#include "plugin/plugin.h"
#include "vidext.h"
/* some local state variables */
static int l_CoreInit = 0;
static int l_ROMOpen = 0;
static int l_CallerUsingSDL = 0;
/* functions exported outside of libmupen64plus to front-end application */
EXPORT m64p_error CALL CoreStartup(int APIVersion, const char *ConfigPath, const char *DataPath, void *Context,
void (*DebugCallback)(void *, int, const char *), void *Context2,
void (*StateCallback)(void *, m64p_core_param, int))
{
if (l_CoreInit)
return M64ERR_ALREADY_INIT;
/* check wether the caller has already initialized SDL */
l_CallerUsingSDL = (SDL_WasInit(0) != 0);
/* very first thing is to set the callback functions for debug info and state changing*/
SetDebugCallback(DebugCallback, Context);
SetStateCallback(StateCallback, Context2);
/* check front-end's API version */
if ((APIVersion & 0xffff0000) != (FRONTEND_API_VERSION & 0xffff0000))
{
DebugMessage(M64MSG_ERROR, "CoreStartup(): Front-end (API version %i.%i.%i) is incompatible with this core (API %i.%i.%i)",
VERSION_PRINTF_SPLIT(APIVersion), VERSION_PRINTF_SPLIT(FRONTEND_API_VERSION));
return M64ERR_INCOMPATIBLE;
}
/* set up the default (dummy) plugins */
plugin_connect(M64PLUGIN_GFX, NULL);
plugin_connect(M64PLUGIN_AUDIO, NULL);
plugin_connect(M64PLUGIN_INPUT, NULL);
plugin_connect(M64PLUGIN_CORE, NULL);
savestates_init();
/* next, start up the configuration handling code by loading and parsing the config file */
if (ConfigInit(ConfigPath, DataPath) != M64ERR_SUCCESS)
return M64ERR_INTERNAL;
/* set default configuration parameter values for Core */
if (ConfigOpenSection("Core", &g_CoreConfig) != M64ERR_SUCCESS || g_CoreConfig == NULL)
return M64ERR_INTERNAL;
if (!main_set_core_defaults())
return M64ERR_INTERNAL;
/* allocate base memory */
g_mem_base = init_mem_base();
if (g_mem_base == NULL) {
return M64ERR_NO_MEMORY;
}
/* The ROM database contains MD5 hashes, goodnames, and some game-specific parameters */
romdatabase_open();
workqueue_init();
l_CoreInit = 1;
return M64ERR_SUCCESS;
}
EXPORT m64p_error CALL CoreShutdown(void)
{
if (!l_CoreInit)
return M64ERR_NOT_INIT;
/* close down some core sub-systems */
romdatabase_close();
ConfigShutdown();
workqueue_shutdown();
savestates_deinit();
/* if the calling code is using SDL, don't shut it down */
if (!l_CallerUsingSDL)
SDL_Quit();
/* deallocate base memory */
release_mem_base(g_mem_base);
g_mem_base = NULL;
l_CoreInit = 0;
return M64ERR_SUCCESS;
}
EXPORT m64p_error CALL CoreAttachPlugin(m64p_plugin_type PluginType, m64p_dynlib_handle PluginLibHandle)
{
m64p_error rval;
if (!l_CoreInit)
return M64ERR_NOT_INIT;
if (g_EmulatorRunning || !l_ROMOpen)
return M64ERR_INVALID_STATE;
rval = plugin_connect(PluginType, PluginLibHandle);
if (rval != M64ERR_SUCCESS)
return rval;
rval = plugin_start(PluginType);
if (rval != M64ERR_SUCCESS)
return rval;
return M64ERR_SUCCESS;
}
EXPORT m64p_error CALL CoreDetachPlugin(m64p_plugin_type PluginType)
{
if (!l_CoreInit)
return M64ERR_NOT_INIT;
if (g_EmulatorRunning)
return M64ERR_INVALID_STATE;
return plugin_connect(PluginType, NULL);
}
EXPORT m64p_error CALL CoreDoCommand(m64p_command Command, int ParamInt, void *ParamPtr)
{
m64p_error rval;
int keysym, keymod;
if (!l_CoreInit)
return M64ERR_NOT_INIT;
switch(Command)
{
case M64CMD_NOP:
return M64ERR_SUCCESS;
case M64CMD_ROM_OPEN:
if (g_EmulatorRunning || l_ROMOpen)
return M64ERR_INVALID_STATE;
if (ParamPtr == NULL || ParamInt < 4096)
return M64ERR_INPUT_ASSERT;
rval = open_rom((const unsigned char *) ParamPtr, ParamInt);
if (rval == M64ERR_SUCCESS)
{
l_ROMOpen = 1;
ScreenshotRomOpen();
cheat_init(&g_cheat_ctx);
}
return rval;
case M64CMD_ROM_CLOSE:
if (g_EmulatorRunning || !l_ROMOpen)
return M64ERR_INVALID_STATE;
l_ROMOpen = 0;
cheat_delete_all(&g_cheat_ctx);
cheat_uninit(&g_cheat_ctx);
return close_rom();
case M64CMD_ROM_GET_HEADER:
if (!l_ROMOpen)
return M64ERR_INVALID_STATE;
if (ParamPtr == NULL)
return M64ERR_INPUT_ASSERT;
if ((int)sizeof(m64p_rom_header) < ParamInt)
ParamInt = sizeof(m64p_rom_header);
memcpy(ParamPtr, &ROM_HEADER, ParamInt);
// Mupen64Plus used to keep a m64p_rom_header with a clean ROM name
// Keep returning a clean ROM name for backwards compatibility
if (ParamInt >= 0x20)
{
int size = (ParamInt >= 0x20 + 20) ? 20 : (ParamInt - 0x20);
memcpy((char *)ParamPtr + 0x20, ROM_PARAMS.headername, size);
}
return M64ERR_SUCCESS;
case M64CMD_ROM_GET_SETTINGS:
if (!l_ROMOpen)
return M64ERR_INVALID_STATE;
if (ParamPtr == NULL)
return M64ERR_INPUT_ASSERT;
if ((int)sizeof(m64p_rom_settings) < ParamInt)
ParamInt = sizeof(m64p_rom_settings);
memcpy(ParamPtr, &ROM_SETTINGS, ParamInt);
return M64ERR_SUCCESS;
case M64CMD_EXECUTE:
if (g_EmulatorRunning || !l_ROMOpen)
return M64ERR_INVALID_STATE;
/* print out plugin-related warning messages */
plugin_check();
/* the main_run() function will not return until the player has quit the game */
rval = main_run();
return rval;
case M64CMD_STOP:
if (!g_EmulatorRunning)
return M64ERR_INVALID_STATE;
/* this stop function is asynchronous. The emulator may not terminate until later */
return main_core_state_set(M64CORE_EMU_STATE, M64EMU_STOPPED);
case M64CMD_PAUSE:
if (!g_EmulatorRunning)
return M64ERR_INVALID_STATE;
return main_core_state_set(M64CORE_EMU_STATE, M64EMU_PAUSED);
case M64CMD_RESUME:
if (!g_EmulatorRunning)
return M64ERR_INVALID_STATE;
return main_core_state_set(M64CORE_EMU_STATE, M64EMU_RUNNING);
case M64CMD_CORE_STATE_QUERY:
if (ParamPtr == NULL)
return M64ERR_INPUT_ASSERT;
return main_core_state_query((m64p_core_param) ParamInt, (int *) ParamPtr);
case M64CMD_CORE_STATE_SET:
if (ParamPtr == NULL)
return M64ERR_INPUT_ASSERT;
return main_core_state_set((m64p_core_param) ParamInt, *((int *)ParamPtr));
case M64CMD_STATE_LOAD:
main_state_load((char *) ParamPtr);
return M64ERR_SUCCESS;
case M64CMD_STATE_SAVE:
if (!g_EmulatorRunning)
return M64ERR_INVALID_STATE;
if (ParamPtr != NULL && (ParamInt < 1 || ParamInt > 3))
return M64ERR_INPUT_INVALID;
main_state_save(ParamInt, (char *) ParamPtr);
return M64ERR_SUCCESS;
case M64CMD_STATE_SET_SLOT:
if (ParamInt < 0 || ParamInt > 9)
return M64ERR_INPUT_INVALID;
return main_core_state_set(M64CORE_SAVESTATE_SLOT, ParamInt);
case M64CMD_SEND_SDL_KEYDOWN:
if (!g_EmulatorRunning)
return M64ERR_INVALID_STATE;
keysym = ParamInt & 0xffff;
keymod = (ParamInt >> 16) & 0xffff;
event_sdl_keydown(keysym, keymod);
return M64ERR_SUCCESS;
case M64CMD_SEND_SDL_KEYUP:
if (!g_EmulatorRunning)
return M64ERR_INVALID_STATE;
keysym = ParamInt & 0xffff;
keymod = (ParamInt >> 16) & 0xffff;
event_sdl_keyup(keysym, keymod);
return M64ERR_SUCCESS;
case M64CMD_SET_FRAME_CALLBACK:
*(void**)&g_FrameCallback = ParamPtr;
return M64ERR_SUCCESS;
case M64CMD_TAKE_NEXT_SCREENSHOT:
if (!g_EmulatorRunning)
return M64ERR_INVALID_STATE;
main_take_next_screenshot();
return M64ERR_SUCCESS;
case M64CMD_READ_SCREEN:
if (!g_EmulatorRunning)
return M64ERR_INVALID_STATE;
if (ParamPtr == NULL)
return M64ERR_INPUT_ASSERT;
if (ParamInt < 0 || ParamInt > 1)
return M64ERR_INPUT_INVALID;
return main_read_screen(ParamPtr, ParamInt);
case M64CMD_RESET:
if (!g_EmulatorRunning)
return M64ERR_INVALID_STATE;
if (ParamInt < 0 || ParamInt > 1)
return M64ERR_INPUT_INVALID;
return main_reset(ParamInt);
case M64CMD_ADVANCE_FRAME:
if (!g_EmulatorRunning)
return M64ERR_INVALID_STATE;
main_advance_one();
return M64ERR_SUCCESS;
case M64CMD_SET_MEDIA_LOADER:
if (ParamInt != sizeof(m64p_media_loader) || ParamPtr == NULL)
return M64ERR_INPUT_INVALID;
g_media_loader = *(m64p_media_loader*)ParamPtr;
return M64ERR_SUCCESS;
case M64CMD_NETPLAY_INIT:
if (ParamInt < 1 || ParamPtr == NULL)
return M64ERR_INPUT_INVALID;
return netplay_start(ParamPtr, ParamInt);
case M64CMD_NETPLAY_CONTROL_PLAYER:
if (ParamInt < 1 || ParamInt > 4 || ParamPtr == NULL)
return M64ERR_INPUT_INVALID;
if (netplay_register_player(ParamInt - 1, Controls[netplay_next_controller()].Plugin, Controls[netplay_next_controller()].RawData, *(uint32_t*)ParamPtr))
{
netplay_set_controller(ParamInt - 1);
return M64ERR_SUCCESS;
}
else
return M64ERR_INPUT_ASSERT; // player already in use
case M64CMD_NETPLAY_GET_VERSION:
if (ParamPtr == NULL)
return M64ERR_INPUT_INVALID;
*(uint32_t*)ParamPtr = NETPLAY_CORE_VERSION;
if (ParamInt == NETPLAY_API_VERSION)
return M64ERR_SUCCESS;
else
return M64ERR_INCOMPATIBLE;
case M64CMD_NETPLAY_CLOSE:
return netplay_stop();
default:
return M64ERR_INPUT_INVALID;
}
return M64ERR_INTERNAL;
}
EXPORT m64p_error CALL CoreOverrideVidExt(m64p_video_extension_functions *VideoFunctionStruct)
{
if (!l_CoreInit)
return M64ERR_NOT_INIT;
return OverrideVideoFunctions(VideoFunctionStruct); /* in vidext.c */
}
EXPORT m64p_error CALL CoreAddCheat(const char *CheatName, m64p_cheat_code *CodeList, int NumCodes)
{
if (!l_CoreInit)
return M64ERR_NOT_INIT;
if (CheatName == NULL || CodeList == NULL)
return M64ERR_INPUT_ASSERT;
if (strlen(CheatName) < 1 || NumCodes < 1)
return M64ERR_INPUT_INVALID;
if (cheat_add_new(&g_cheat_ctx, CheatName, CodeList, NumCodes))
return M64ERR_SUCCESS;
return M64ERR_INPUT_INVALID;
}
EXPORT m64p_error CALL CoreCheatEnabled(const char *CheatName, int Enabled)
{
if (!l_CoreInit)
return M64ERR_NOT_INIT;
if (CheatName == NULL)
return M64ERR_INPUT_ASSERT;
if (cheat_set_enabled(&g_cheat_ctx, CheatName, Enabled))
return M64ERR_SUCCESS;
return M64ERR_INPUT_INVALID;
}
EXPORT m64p_error CALL CoreGetRomSettings(m64p_rom_settings *RomSettings, int RomSettingsLength, int Crc1, int Crc2)
{
romdatabase_entry* entry;
int i;
if (!l_CoreInit)
return M64ERR_NOT_INIT;
if (RomSettings == NULL)
return M64ERR_INPUT_ASSERT;
if (RomSettingsLength < (int)sizeof(m64p_rom_settings))
return M64ERR_INPUT_INVALID;
/* Look up this ROM in the .ini file and fill in goodname, etc */
entry = ini_search_by_crc(Crc1, Crc2);
if (entry == NULL)
return M64ERR_INPUT_NOT_FOUND;
strncpy(RomSettings->goodname, entry->goodname, 255);
RomSettings->goodname[255] = '\0';
for (i = 0; i < 16; i++)
sprintf(RomSettings->MD5 + i*2, "%02X", entry->md5[i]);
RomSettings->MD5[32] = '\0';
RomSettings->savetype = entry->savetype;
RomSettings->status = entry->status;
RomSettings->players = entry->players;
RomSettings->rumble = entry->rumble;
RomSettings->transferpak = entry->transferpak;
RomSettings->mempak = entry->mempak;
return M64ERR_SUCCESS;
}