BizHawk/mednafen/libretro/minifen/minifen.cpp

964 lines
22 KiB
C++

#include "mednafen.h"
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <trio/trio.h>
#include <list>
#include <algorithm>
#include "general.h"
#include "state.h"
#include "video.h"
#ifdef NEED_DEINTERLACER
#include "video/Deinterlacer.h"
#endif
#include "file.h"
#include "FileWrapper.h"
#ifdef NEED_CD
#include "cdrom/cdromif.h"
#include "cdrom/CDUtility.h"
#endif
#include "mempatcher.h"
#include "md5.h"
#include "clamp.h"
#ifdef NEED_RESAMPLER
#include "Fir_Resampler.h"
#endif
MDFNGI *MDFNGameInfo = NULL;
static double LastSoundMultiplier;
static MDFN_PixelFormat last_pixel_format;
static double last_sound_rate;
#ifdef NEED_DEINTERLACER
static bool PrevInterlaced;
static Deinterlacer deint;
#endif
#ifdef NEED_CD
static std::vector<CDIF *> CDInterfaces; // FIXME: Cleanup on error out.
#endif
void MDFNI_CloseGame(void)
{
if(MDFNGameInfo)
{
MDFN_FlushGameCheats(0);
MDFNGameInfo->CloseGame();
if(MDFNGameInfo->name)
{
free(MDFNGameInfo->name);
MDFNGameInfo->name=0;
}
MDFNMP_Kill();
MDFNGameInfo = NULL;
#ifdef NEED_CD
for(unsigned i = 0; i < CDInterfaces.size(); i++)
delete CDInterfaces[i];
CDInterfaces.clear();
#endif
}
#ifdef WANT_DEBUGGER
MDFNDBG_Kill();
#endif
}
std::vector<MDFNGI *> MDFNSystems;
static std::list<MDFNGI *> MDFNSystemsPrio;
bool MDFNSystemsPrio_CompareFunc(MDFNGI *first, MDFNGI *second)
{
if(first->ModulePriority > second->ModulePriority)
return(true);
return(false);
}
static void AddSystem(MDFNGI *system)
{
MDFNSystems.push_back(system);
}
#ifdef NEED_CD
bool CDIF_DumpCD(const char *fn);
void MDFNI_DumpModulesDef(const char *fn)
{
FILE *fp = fopen(fn, "wb");
for(unsigned int i = 0; i < MDFNSystems.size(); i++)
{
fprintf(fp, "%s\n", MDFNSystems[i]->shortname);
fprintf(fp, "%s\n", MDFNSystems[i]->fullname);
fprintf(fp, "%d\n", MDFNSystems[i]->nominal_width);
fprintf(fp, "%d\n", MDFNSystems[i]->nominal_height);
}
fclose(fp);
}
static void ReadM3U(std::vector<std::string> &file_list, std::string path, unsigned depth = 0)
{
std::vector<std::string> ret;
FileWrapper m3u_file(path.c_str(), FileWrapper::MODE_READ, _("M3U CD Set"));
std::string dir_path;
char linebuf[2048];
MDFN_GetFilePathComponents(path, &dir_path);
while(m3u_file.get_line(linebuf, sizeof(linebuf)))
{
std::string efp;
if(linebuf[0] == '#') continue;
MDFN_rtrim(linebuf);
if(linebuf[0] == 0) continue;
efp = MDFN_EvalFIP(dir_path, std::string(linebuf));
if(efp.size() >= 4 && efp.substr(efp.size() - 4) == ".m3u")
{
if(efp == path)
throw(MDFN_Error(0, _("M3U at \"%s\" references self."), efp.c_str()));
if(depth == 99)
throw(MDFN_Error(0, _("M3U load recursion too deep!")));
ReadM3U(file_list, efp, depth++);
}
else
file_list.push_back(efp);
}
}
// TODO: LoadCommon()
MDFNGI *MDFNI_LoadCD(const char *force_module, const char *devicename)
{
uint8 LayoutMD5[16];
MDFNI_CloseGame();
LastSoundMultiplier = 1;
MDFN_printf(_("Loading %s...\n\n"), devicename ? devicename : _("PHYSICAL CD"));
try
{
if(devicename && strlen(devicename) > 4 && !strcasecmp(devicename + strlen(devicename) - 4, ".m3u"))
{
std::vector<std::string> file_list;
ReadM3U(file_list, devicename);
for(unsigned i = 0; i < file_list.size(); i++)
CDInterfaces.push_back(new CDIF_MT(file_list[i].c_str()));
}
else
CDInterfaces.push_back(new CDIF_MT(devicename));
}
catch(std::exception &e)
{
MDFND_PrintError(e.what());
MDFN_PrintError(_("Error opening CD."));
return(0);
}
/* Print out a track list for all discs. */
MDFN_indent(1);
for(unsigned i = 0; i < CDInterfaces.size(); i++)
{
CDUtility::TOC toc;
CDInterfaces[i]->ReadTOC(&toc);
MDFN_printf(_("CD %d Layout:\n"), i + 1);
MDFN_indent(1);
for(int32 track = toc.first_track; track <= toc.last_track; track++)
{
MDFN_printf(_("Track %2d, LBA: %6d %s\n"), track, toc.tracks[track].lba, (toc.tracks[track].control & 0x4) ? "DATA" : "AUDIO");
}
MDFN_printf("Leadout: %6d\n", toc.tracks[100].lba);
MDFN_indent(-1);
MDFN_printf("\n");
}
MDFN_indent(-1);
// Calculate layout MD5. The system emulation LoadCD() code is free to ignore this value and calculate
// its own, or to use it to look up a game in its database.
{
md5_context layout_md5;
layout_md5.starts();
for(unsigned i = 0; i < CDInterfaces.size(); i++)
{
CD_TOC toc;
CDInterfaces[i]->ReadTOC(&toc);
layout_md5.update_u32_as_lsb(toc.first_track);
layout_md5.update_u32_as_lsb(toc.last_track);
layout_md5.update_u32_as_lsb(toc.tracks[100].lba);
for(uint32 track = toc.first_track; track <= toc.last_track; track++)
{
layout_md5.update_u32_as_lsb(toc.tracks[track].lba);
layout_md5.update_u32_as_lsb(toc.tracks[track].control & 0x4);
}
}
layout_md5.finish(LayoutMD5);
}
MDFNGameInfo = NULL;
for(std::list<MDFNGI *>::iterator it = MDFNSystemsPrio.begin(); it != MDFNSystemsPrio.end(); it++)
{
char tmpstr[256];
trio_snprintf(tmpstr, 256, "%s.enable", (*it)->shortname);
if(force_module)
{
if(!strcmp(force_module, (*it)->shortname))
{
MDFNGameInfo = *it;
break;
}
}
else
{
// Is module enabled?
if(!MDFN_GetSettingB(tmpstr))
continue;
if(!(*it)->LoadCD || !(*it)->TestMagicCD)
continue;
if((*it)->TestMagicCD(&CDInterfaces))
{
MDFNGameInfo = *it;
break;
}
}
}
if(!MDFNGameInfo)
{
if(force_module)
{
MDFN_PrintError(_("Unrecognized system \"%s\"!"), force_module);
return(0);
}
// This code path should never be taken, thanks to "cdplay"
MDFN_PrintError(_("Could not find a system that supports this CD."));
return(0);
}
// This if statement will be true if force_module references a system without CDROM support.
if(!MDFNGameInfo->LoadCD)
{
MDFN_PrintError(_("Specified system \"%s\" doesn't support CDs!"), force_module);
return(0);
}
MDFN_printf(_("Using module: %s(%s)\n\n"), MDFNGameInfo->shortname, MDFNGameInfo->fullname);
// TODO: include module name in hash
memcpy(MDFNGameInfo->MD5, LayoutMD5, 16);
if(!(MDFNGameInfo->LoadCD(&CDInterfaces)))
{
for(unsigned i = 0; i < CDInterfaces.size(); i++)
delete CDInterfaces[i];
CDInterfaces.clear();
MDFNGameInfo = NULL;
return(0);
}
MDFNI_SetLayerEnableMask(~0ULL);
#ifdef WANT_DEBUGGER
MDFNDBG_PostGameLoad();
#endif
MDFN_ResetMessages(); // Save state, status messages, etc.
MDFN_LoadGameCheats(NULL);
MDFNMP_InstallReadPatches();
last_sound_rate = -1;
memset(&last_pixel_format, 0, sizeof(MDFN_PixelFormat));
return(MDFNGameInfo);
}
#endif
// Return FALSE on fatal error(IPS file found but couldn't be applied),
// or TRUE on success(IPS patching succeeded, or IPS file not found).
static bool LoadIPS(MDFNFILE &GameFile, const char *path)
{
return true;
}
MDFNGI *MDFNI_LoadGame(const char *force_module, const char *name)
{
MDFNFILE GameFile;
std::vector<FileExtensionSpecStruct> valid_iae;
#ifdef NEED_CD
if(strlen(name) > 4 && (!strcasecmp(name + strlen(name) - 4, ".cue") || !strcasecmp(name + strlen(name) - 4, ".toc") || !strcasecmp(name + strlen(name) - 4, ".m3u")))
return(MDFNI_LoadCD(force_module, name));
#endif
MDFNI_CloseGame();
LastSoundMultiplier = 1;
MDFNGameInfo = NULL;
MDFN_printf(_("Loading %s...\n"),name);
MDFN_indent(1);
// Construct a NULL-delimited list of known file extensions for MDFN_fopen()
for(unsigned int i = 0; i < MDFNSystems.size(); i++)
{
const FileExtensionSpecStruct *curexts = MDFNSystems[i]->FileExtensions;
// If we're forcing a module, only look for extensions corresponding to that module
if(force_module && strcmp(MDFNSystems[i]->shortname, force_module))
continue;
if(curexts)
while(curexts->extension && curexts->description)
{
valid_iae.push_back(*curexts);
curexts++;
}
}
{
FileExtensionSpecStruct tmpext = { NULL, NULL };
valid_iae.push_back(tmpext);
}
if(!GameFile.Open(name, &valid_iae[0], _("game")))
{
MDFNGameInfo = NULL;
return 0;
}
if(!LoadIPS(GameFile, MDFN_MakeFName(MDFNMKF_IPS, 0, 0).c_str()))
{
MDFNGameInfo = NULL;
GameFile.Close();
return(0);
}
MDFNGameInfo = NULL;
for(std::list<MDFNGI *>::iterator it = MDFNSystemsPrio.begin(); it != MDFNSystemsPrio.end(); it++) //_unsigned int x = 0; x < MDFNSystems.size(); x++)
{
char tmpstr[256];
trio_snprintf(tmpstr, 256, "%s.enable", (*it)->shortname);
if(force_module)
{
if(!strcmp(force_module, (*it)->shortname))
{
if(!(*it)->Load)
{
GameFile.Close();
#ifdef NEED_CD
if((*it)->LoadCD)
MDFN_PrintError(_("Specified system only supports CD(physical, or image files, such as *.cue and *.toc) loading."));
else
#endif
MDFN_PrintError(_("Specified system does not support normal file loading."));
MDFN_indent(-1);
MDFNGameInfo = NULL;
return 0;
}
MDFNGameInfo = *it;
break;
}
}
else
{
// Is module enabled?
if(!MDFN_GetSettingB(tmpstr))
continue;
if(!(*it)->Load || !(*it)->TestMagic)
continue;
if((*it)->TestMagic(name, &GameFile))
{
MDFNGameInfo = *it;
break;
}
}
}
if(!MDFNGameInfo)
{
GameFile.Close();
if(force_module)
MDFN_PrintError(_("Unrecognized system \"%s\"!"), force_module);
else
MDFN_PrintError(_("Unrecognized file format. Sorry."));
MDFN_indent(-1);
MDFNGameInfo = NULL;
return 0;
}
MDFN_printf(_("Using module: %s(%s)\n\n"), MDFNGameInfo->shortname, MDFNGameInfo->fullname);
MDFN_indent(1);
MDFNGameInfo->soundrate = 0;
MDFNGameInfo->name = NULL;
MDFNGameInfo->rotated = 0;
//
// Load per-game settings
//
// Maybe we should make a "pgcfg" subdir, and automatically load all files in it?
// End load per-game settings
//
if(MDFNGameInfo->Load(name, &GameFile) <= 0)
{
GameFile.Close();
MDFN_indent(-2);
MDFNGameInfo = NULL;
return(0);
}
MDFN_LoadGameCheats(NULL);
MDFNMP_InstallReadPatches();
MDFNI_SetLayerEnableMask(~0ULL);
#ifdef WANT_DEBUGGER
MDFNDBG_PostGameLoad();
#endif
MDFN_ResetMessages(); // Save state, status messages, etc.
MDFN_indent(-2);
if(!MDFNGameInfo->name)
{
unsigned int x;
char *tmp;
MDFNGameInfo->name = (UTF8 *)strdup(GetFNComponent(name));
for(x=0;x<strlen((char *)MDFNGameInfo->name);x++)
{
if(MDFNGameInfo->name[x] == '_')
MDFNGameInfo->name[x] = ' ';
}
if((tmp = strrchr((char *)MDFNGameInfo->name, '.')))
*tmp = 0;
}
#ifdef NEED_DEINTERLACER
PrevInterlaced = false;
deint.ClearState();
#endif
last_sound_rate = -1;
memset(&last_pixel_format, 0, sizeof(MDFN_PixelFormat));
return(MDFNGameInfo);
}
#if defined(WANT_PSX_EMU)
extern MDFNGI EmulatedPSX;
#elif defined(WANT_PCE_EMU)
extern MDFNGI EmulatedPCE;
#elif defined(WANT_PCE_FAST_EMU)
extern MDFNGI EmulatedPCE_Fast;
#elif defined(WANT_WSWAN_EMU)
extern MDFNGI EmulatedWSwan;
#endif
bool MDFNI_InitializeModules(const std::vector<MDFNGI *> &ExternalSystems)
{
static MDFNGI *InternalSystems[] =
{
#ifdef WANT_NES_EMU
&EmulatedNES,
#endif
#ifdef WANT_SNES_EMU
&EmulatedSNES,
#endif
#ifdef WANT_GB_EMU
&EmulatedGB,
#endif
#ifdef WANT_GBA_EMU
&EmulatedGBA,
#endif
#ifdef WANT_PCE_EMU
&EmulatedPCE,
#endif
#ifdef WANT_PCE_FAST_EMU
&EmulatedPCE_Fast,
#endif
#ifdef WANT_LYNX_EMU
&EmulatedLynx,
#endif
#ifdef WANT_MD_EMU
&EmulatedMD,
#endif
#ifdef WANT_PCFX_EMU
&EmulatedPCFX,
#endif
#ifdef WANT_NGP_EMU
&EmulatedNGP,
#endif
#ifdef WANT_PSX_EMU
&EmulatedPSX,
#endif
#ifdef WANT_VB_EMU
&EmulatedVB,
#endif
#ifdef WANT_WSWAN_EMU
&EmulatedWSwan,
#endif
#ifdef WANT_SMS_EMU
&EmulatedSMS,
&EmulatedGG,
#endif
};
std::string i_modules_string, e_modules_string;
for(unsigned int i = 0; i < sizeof(InternalSystems) / sizeof(MDFNGI *); i++)
{
AddSystem(InternalSystems[i]);
if(i)
i_modules_string += " ";
i_modules_string += std::string(InternalSystems[i]->shortname);
}
MDFNI_printf(_("Internal emulation modules: %s\n"), i_modules_string.c_str());
for(unsigned int i = 0; i < MDFNSystems.size(); i++)
MDFNSystemsPrio.push_back(MDFNSystems[i]);
MDFNSystemsPrio.sort(MDFNSystemsPrio_CompareFunc);
#ifdef NEED_CD
CDUtility::CDUtility_Init();
#endif
return(1);
}
static std::string settings_file_path;
int MDFNI_Initialize(const char *basedir)
{
#ifdef WANT_DEBUGGER
MDFNDBG_Init();
#endif
return(1);
}
void MDFNI_Kill(void)
{
/* save settings */
}
#if defined(NEED_RESAMPLER)
static double multiplier_save, volume_save;
static Fir_Resampler<16> ff_resampler;
static void ProcessAudio(EmulateSpecStruct *espec)
{
if(espec->SoundVolume != 1)
volume_save = espec->SoundVolume;
if(espec->soundmultiplier != 1)
multiplier_save = espec->soundmultiplier;
if(espec->SoundBuf && espec->SoundBufSize)
{
int16 *const SoundBuf = espec->SoundBuf + espec->SoundBufSizeALMS * MDFNGameInfo->soundchan;
int32 SoundBufSize = espec->SoundBufSize - espec->SoundBufSizeALMS;
const int32 SoundBufMaxSize = espec->SoundBufMaxSize - espec->SoundBufSizeALMS;
if(multiplier_save != LastSoundMultiplier)
{
ff_resampler.time_ratio(multiplier_save, 0.9965);
LastSoundMultiplier = multiplier_save;
}
if(multiplier_save != 1)
{
{
if(MDFNGameInfo->soundchan == 2)
{
for(int i = 0; i < SoundBufSize * 2; i++)
ff_resampler.buffer()[i] = SoundBuf[i];
}
else
{
for(int i = 0; i < SoundBufSize; i++)
{
ff_resampler.buffer()[i * 2] = SoundBuf[i];
ff_resampler.buffer()[i * 2 + 1] = 0;
}
}
ff_resampler.write(SoundBufSize * 2);
int avail = ff_resampler.avail();
int real_read = std::min((int)(SoundBufMaxSize * MDFNGameInfo->soundchan), avail);
if(MDFNGameInfo->soundchan == 2)
SoundBufSize = ff_resampler.read(SoundBuf, real_read ) >> 1;
else
SoundBufSize = ff_resampler.read_mono_hack(SoundBuf, real_read );
avail -= real_read;
if(avail > 0)
{
printf("ff_resampler.avail() > espec->SoundBufMaxSize * MDFNGameInfo->soundchan - %d\n", avail);
ff_resampler.clear();
}
}
}
if(volume_save != 1)
{
if(volume_save < 1)
{
int volume = (int)(16384 * volume_save);
for(int i = 0; i < SoundBufSize * MDFNGameInfo->soundchan; i++)
SoundBuf[i] = (SoundBuf[i] * volume) >> 14;
}
else
{
int volume = (int)(256 * volume_save);
for(int i = 0; i < SoundBufSize * MDFNGameInfo->soundchan; i++)
{
int temp = ((SoundBuf[i] * volume) >> 8) + 32768;
temp = clamp_to_u16(temp);
SoundBuf[i] = temp - 32768;
}
}
}
// TODO: Optimize this.
if(MDFNGameInfo->soundchan == 2 && MDFN_GetSettingB(std::string(std::string(MDFNGameInfo->shortname) + ".forcemono").c_str()))
{
for(int i = 0; i < SoundBufSize * MDFNGameInfo->soundchan; i += 2)
{
// We should use division instead of arithmetic right shift for correctness(rounding towards 0 instead of negative infinitininintinity), but I like speed.
int32 mixed = (SoundBuf[i + 0] + SoundBuf[i + 1]) >> 1;
SoundBuf[i + 0] =
SoundBuf[i + 1] = mixed;
}
}
espec->SoundBufSize = espec->SoundBufSizeALMS + SoundBufSize;
} // end to: if(espec->SoundBuf && espec->SoundBufSize)
}
#else
static void ProcessAudio(EmulateSpecStruct *espec)
{
if(espec->SoundBuf && espec->SoundBufSize)
{
int16 *const SoundBuf = espec->SoundBuf + espec->SoundBufSizeALMS * MDFNGameInfo->soundchan;
int32 SoundBufSize = espec->SoundBufSize - espec->SoundBufSizeALMS;
const int32 SoundBufMaxSize = espec->SoundBufMaxSize - espec->SoundBufSizeALMS;
espec->SoundBufSize = espec->SoundBufSizeALMS + SoundBufSize;
} // end to: if(espec->SoundBuf && espec->SoundBufSize)
}
#endif
void MDFN_MidSync(EmulateSpecStruct *espec)
{
ProcessAudio(espec);
MDFND_MidSync(espec);
espec->SoundBufSizeALMS = espec->SoundBufSize;
espec->MasterCyclesALMS = espec->MasterCycles;
}
void MDFNI_Emulate(EmulateSpecStruct *espec)
{
// Initialize some espec member data to zero, to catch some types of bugs.
espec->DisplayRect.x = 0;
espec->DisplayRect.w = 0;
espec->DisplayRect.y = 0;
espec->DisplayRect.h = 0;
espec->SoundBufSize = 0;
espec->VideoFormatChanged = false;
espec->SoundFormatChanged = false;
if(memcmp(&last_pixel_format, &espec->surface->format, sizeof(MDFN_PixelFormat)))
{
espec->VideoFormatChanged = TRUE;
last_pixel_format = espec->surface->format;
}
if(espec->SoundRate != last_sound_rate)
{
espec->SoundFormatChanged = true;
last_sound_rate = espec->SoundRate;
#ifdef NEED_RESAMPLER
ff_resampler.buffer_size((espec->SoundRate / 2) * 2);
#endif
}
espec->NeedSoundReverse = false;
MDFNGameInfo->Emulate(espec);
#ifdef NEED_DEINTERLACER
if(espec->InterlaceOn)
{
if(!PrevInterlaced)
deint.ClearState();
deint.Process(espec->surface, espec->DisplayRect, espec->LineWidths, espec->InterlaceField);
PrevInterlaced = true;
espec->InterlaceOn = false;
espec->InterlaceField = 0;
}
else
PrevInterlaced = false;
#endif
ProcessAudio(espec);
}
// This function should only be called for state rewinding.
// FIXME: Add a macro for SFORMAT structure access instead of direct access
int MDFN_RawInputStateAction(StateMem *sm, int load, int data_only)
{
static const char *stringies[16] = { "RI00", "RI01", "RI02", "RI03", "RI04", "RI05", "RI06", "RI07", "RI08", "RI09", "RI0a", "RI0b", "RI0c", "RI0d", "RI0e", "RI0f" };
SFORMAT StateRegs[17];
int x;
for(x = 0; x < 16; x++)
{
StateRegs[x].name = stringies[x];
StateRegs[x].flags = 0;
StateRegs[x].v = NULL;
StateRegs[x].size = 0;
}
StateRegs[x].v = NULL;
StateRegs[x].size = 0;
StateRegs[x].name = NULL;
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "rinp");
return(ret);
}
static int curindent = 0;
void MDFN_indent(int indent)
{
curindent += indent;
}
static uint8 lastchar = 0;
void MDFN_printf(const char *format, ...) throw()
{
char *format_temp;
char *temp;
unsigned int x, newlen;
va_list ap;
va_start(ap,format);
// First, determine how large our format_temp buffer needs to be.
uint8 lastchar_backup = lastchar; // Save lastchar!
for(newlen=x=0;x<strlen(format);x++)
{
if(lastchar == '\n' && format[x] != '\n')
{
int y;
for(y=0;y<curindent;y++)
newlen++;
}
newlen++;
lastchar = format[x];
}
format_temp = (char *)malloc(newlen + 1); // Length + NULL character, duh
// Now, construct our format_temp string
lastchar = lastchar_backup; // Restore lastchar
for(newlen=x=0;x<strlen(format);x++)
{
if(lastchar == '\n' && format[x] != '\n')
{
int y;
for(y=0;y<curindent;y++)
format_temp[newlen++] = ' ';
}
format_temp[newlen++] = format[x];
lastchar = format[x];
}
format_temp[newlen] = 0;
temp = trio_vaprintf(format_temp, ap);
free(format_temp);
MDFND_Message(temp);
free(temp);
va_end(ap);
}
void MDFN_PrintError(const char *format, ...) throw()
{
char *temp;
va_list ap;
va_start(ap, format);
temp = trio_vaprintf(format, ap);
MDFND_PrintError(temp);
free(temp);
va_end(ap);
}
void MDFN_DebugPrintReal(const char *file, const int line, const char *format, ...)
{
char *temp;
va_list ap;
va_start(ap, format);
temp = trio_vaprintf(format, ap);
printf("%s:%d %s\n", file, line, temp);
free(temp);
va_end(ap);
}
void MDFN_DoSimpleCommand(int cmd)
{
MDFNGameInfo->DoSimpleCommand(cmd);
}
void MDFN_QSimpleCommand(int cmd)
{
}
void MDFNI_Power(void)
{
MDFN_QSimpleCommand(MDFN_MSC_POWER);
}
void MDFNI_Reset(void)
{
MDFN_QSimpleCommand(MDFN_MSC_RESET);
}
// Arcade-support functions
void MDFNI_ToggleDIPView(void)
{
}
void MDFNI_ToggleDIP(int which)
{
MDFN_QSimpleCommand(MDFN_MSC_TOGGLE_DIP0 + which);
}
void MDFNI_InsertCoin(void)
{
MDFN_QSimpleCommand(MDFN_MSC_INSERT_COIN);
}
// Disk/Disc-based system support functions
void MDFNI_DiskInsert(int which)
{
MDFN_QSimpleCommand(MDFN_MSC_INSERT_DISK0 + which);
}
void MDFNI_DiskSelect()
{
MDFN_QSimpleCommand(MDFN_MSC_SELECT_DISK);
}
void MDFNI_DiskInsert()
{
MDFN_QSimpleCommand(MDFN_MSC_INSERT_DISK);
}
void MDFNI_DiskEject()
{
MDFN_QSimpleCommand(MDFN_MSC_EJECT_DISK);
}
void MDFNI_SetLayerEnableMask(uint64 mask)
{
MDFNGameInfo->SetLayerEnableMask(mask);
}
void MDFNI_SetInput(int port, const char *type, void *ptr, uint32 ptr_len_thingy)
{
MDFNGameInfo->SetInput(port, type, ptr);
}