1 /* Mednafen - Multi-system Emulator
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include "mednafen.h"
19 
20 #include <mednafen/cdrom/CDUtility.h>
21 #include <mednafen/cdrom/CDInterface.h>
22 
23 #include <mednafen/string/string.h>
24 #include <mednafen/string/escape.h>
25 
26 #include <mednafen/hash/md5.h>
27 #include <mednafen/MemoryStream.h>
28 #include <mednafen/Time.h>
29 
30 #include <mednafen/sound/Fir_Resampler.h>
31 #include <mednafen/sound/WAVRecord.h>
32 
33 #include <mednafen/NativeVFS.h>
34 
35 #include <minilzo/minilzo.h>
36 
37 #include <trio/trio.h>
38 
39 #include "netplay.h"
40 #include "netplay-driver.h"
41 #include "general.h"
42 
43 #include "state.h"
44 #include "movie.h"
45 #include "state_rewind.h"
46 #include "video.h"
47 #include "video/Deinterlacer.h"
48 #include "file.h"
49 #include "mempatcher.h"
50 #include "tests.h"
51 #include "video/tblur.h"
52 #include "qtrecord.h"
53 
54 namespace Mednafen
55 {
56 
57 NativeVFS NVFS;
58 
59 static void SettingChanged(const char* name);
60 
61 static const char *CSD_forcemono = gettext_noop("Force monophonic sound output.");
62 static const char *CSD_enable = gettext_noop("Enable (automatic) usage of this module.");
63 static const char *CSD_tblur = gettext_noop("Enable video temporal blur(50/50 previous/current frame by default).");
64 static const char *CSD_tblur_accum = gettext_noop("Accumulate color data rather than discarding it.");
65 static const char *CSD_tblur_accum_amount = gettext_noop("Blur amount in accumulation mode, specified in percentage of accumulation buffer to mix with the current frame.");
66 
67 static const MDFNSetting_EnumList VCodec_List[] =
68 {
69  { "raw", (int)QTRecord::VCODEC_RAW, "Raw",
70 	gettext_noop("A fast codec, computationally, but will cause enormous file size and may exceed your storage medium's sustained write rate.") },
71 
72  { "cscd", (int)QTRecord::VCODEC_CSCD, "CamStudio Screen Codec",
73 	gettext_noop("A good balance between performance and compression ratio.") },
74 
75  { "png", (int)QTRecord::VCODEC_PNG, "PNG",
76 	gettext_noop("Has a better compression ratio than \"cscd\", but is much more CPU intensive.  Use for compatibility with official QuickTime in cases where you have insufficient disk space for \"raw\".") },
77 
78  { NULL, 0 },
79 };
80 
81 static const MDFNSetting_EnumList Deinterlacer_List[] =
82 {
83  { "weave", Deinterlacer::DEINT_WEAVE, gettext_noop("Good for low-motion video; can be used in conjunction with negative <system>.scanlines setting values.") },
84  { "bob", Deinterlacer::DEINT_BOB, gettext_noop("Good for causing a headache.  All glory to Bob.") },
85  { "bob_offset", Deinterlacer::DEINT_BOB_OFFSET, gettext_noop("Good for high-motion video, but is a bit flickery; reduces the subjective vertical resolution.") },
86 
87  { "blend", Deinterlacer::DEINT_BLEND, gettext_noop("Blend fields together; reduces vertical and temporal resolution.") },
88  { "blend_rg", Deinterlacer::DEINT_BLEND_RG, gettext_noop("Like the \"blend\" deinterlacer, but the blending is done in a manner that respects gamma, reducing unwanted brightness changes, at the cost of increased CPU usage.") },
89 
90  { NULL, 0 },
91 };
92 
93 static const char* const fname_extra = gettext_noop("See fname_format.txt for more information.  Edit at your own risk.");
94 
95 static const MDFNSetting MednafenSettings[] =
96 {
97   { "netplay.password", MDFNSF_NOFLAGS, gettext_noop("Server password."), gettext_noop("Password to connect to the netplay server."), MDFNST_STRING, "" },
98   { "netplay.localplayers", MDFNSF_NOFLAGS, gettext_noop("Local player count."), gettext_noop("Number of local players for network play.  This number is advisory to the server, and the server may assign fewer players if the number of players requested is higher than the number of controllers currently available."), MDFNST_UINT, "1", "0", "16" },
99   { "netplay.nick", MDFNSF_NOFLAGS, gettext_noop("Nickname."), gettext_noop("Nickname to use for network play chat."), MDFNST_STRING, "" },
100   { "netplay.gamekey", MDFNSF_NOFLAGS, gettext_noop("Key to hash with the MD5 hash of the game."), NULL, MDFNST_STRING, "" },
101 
102   { "srwframes", MDFNSF_NOFLAGS, gettext_noop("Number of frames to keep states for when state rewinding is enabled."),
103 	gettext_noop("WARNING: Setting this to a large value may cause excessive RAM usage in some circumstances, such as with games that stream large volumes of data off of CDs."), MDFNST_UINT, "600", "10", "99999" },
104 
105   { "cd.image_memcache", MDFNSF_NOFLAGS, gettext_noop("Cache entire CD images in memory."), gettext_noop("Reads the entire CD image(s) into memory at startup(which will cause a small delay).  Can help obviate emulation hiccups due to emulated CD access.  May cause more harm than good on low memory systems, systems with swap enabled, and/or when the disc images in question are on a fast SSD."), MDFNST_BOOL, "0" },
106 
107   { "filesys.untrusted_fip_check", MDFNSF_NOFLAGS, gettext_noop("Enable untrusted file-inclusion path security check."),
108 	gettext_noop("When this setting is set to \"1\", the default, paths to files referenced from files like CUE sheets and PSF rips are checked for certain characters that can be used in directory traversal, and if found, loading is aborted.  Set it to \"0\" if you want to allow constructs like absolute paths in CUE sheets, but only if you understand the security implications of doing so(see \"Security Issues\" section in the documentation)."), MDFNST_BOOL, "1" },
109 
110   { "filesys.path_snap", MDFNSF_CAT_PATH, gettext_noop("Path to directory for screen snapshots."), NULL, MDFNST_STRING, "snaps" },
111   { "filesys.path_sav", MDFNSF_CAT_PATH, gettext_noop("Path to directory for save games and nonvolatile memory."), gettext_noop("WARNING: Do not set this path to a directory that contains Famicom Disk System disk images, or you will corrupt them when you load an FDS game and exit Mednafen."), MDFNST_STRING, "sav" },
112   { "filesys.path_savbackup", MDFNSF_CAT_PATH, gettext_noop("Path to directory for backups of save games and nonvolatile memory."), NULL, MDFNST_STRING, "b" },
113   { "filesys.path_state", MDFNSF_CAT_PATH, gettext_noop("Path to directory for save states."), NULL, MDFNST_STRING, "mcs" },
114   { "filesys.path_movie", MDFNSF_CAT_PATH, gettext_noop("Path to directory for movies."), NULL, MDFNST_STRING, "mcm" },
115   { "filesys.path_cheat", MDFNSF_CAT_PATH, gettext_noop("Path to directory for cheats."), NULL, MDFNST_STRING, "cheats" },
116   { "filesys.path_palette", MDFNSF_CAT_PATH, gettext_noop("Path to directory for custom palettes."), NULL, MDFNST_STRING, "palettes" },
117   { "filesys.path_pgconfig", MDFNSF_CAT_PATH, gettext_noop("Path to directory for per-game configuration override files."), NULL, MDFNST_STRING, "pgconfig" },
118   { "filesys.path_firmware", MDFNSF_CAT_PATH, gettext_noop("Path to directory for firmware."), NULL, MDFNST_STRING, "firmware" },
119 
120   { "filesys.fname_movie", MDFNSF_CAT_PATH, gettext_noop("Format string for movie filename."), fname_extra, MDFNST_STRING, "%f.%M%p.%x" },
121   { "filesys.fname_state", MDFNSF_CAT_PATH, gettext_noop("Format string for state filename."), fname_extra, MDFNST_STRING, "%f.%M%X" /*"%F.%M%p.%x"*/ },
122   { "filesys.fname_sav", MDFNSF_CAT_PATH, gettext_noop("Format string for save games filename."), gettext_noop("WARNING: %x should always be included, otherwise you run the risk of overwriting save data for games that create multiple save data files.\n\nSee fname_format.txt for more information.  Edit at your own risk."), MDFNST_STRING, "%f.%M%x" },
123   { "filesys.fname_savbackup", MDFNSF_CAT_PATH, gettext_noop("Format string for save game backups filename."), gettext_noop("WARNING: %x and %p should always be included.\n\nSee fname_format.txt for more information.  Edit at your own risk."), MDFNST_STRING, "%f.%m%z%p.%x" },
124   { "filesys.fname_snap", MDFNSF_CAT_PATH, gettext_noop("Format string for screen snapshot filenames."), gettext_noop("WARNING: %x or %p should always be included, otherwise there will be a conflict between the numeric counter text file and the image data file.\n\nSee fname_format.txt for more information.  Edit at your own risk."), MDFNST_STRING, "%f-%p.%x" },
125 
126   { "filesys.state_comp_level", MDFNSF_NOFLAGS, gettext_noop("Save state file compression level."), gettext_noop("gzip/deflate compression level for save states saved to files.  -1 will disable gzip compression and wrapping entirely."), MDFNST_INT, "6", "-1", "9" },
127 
128 
129   { "qtrecord.w_double_threshold", MDFNSF_NOFLAGS, gettext_noop("Double the raw image's width if it's below this threshold."), NULL, MDFNST_UINT, "384", "0", "1073741824" },
130   { "qtrecord.h_double_threshold", MDFNSF_NOFLAGS, gettext_noop("Double the raw image's height if it's below this threshold."), NULL, MDFNST_UINT, "256", "0", "1073741824" },
131 
132   { "qtrecord.vcodec", MDFNSF_NOFLAGS, gettext_noop("Video codec to use."), NULL, MDFNST_ENUM, "cscd", NULL, NULL, NULL, NULL, VCodec_List },
133 
134   { "video.deinterlacer", MDFNSF_CAT_VIDEO, gettext_noop("Deinterlacer to use for interlaced video."), NULL, MDFNST_ENUM, "weave", NULL, NULL, NULL, SettingChanged, Deinterlacer_List },
135 
136   { "affinity.cd", MDFNSF_NOFLAGS, gettext_noop("CD read threads CPU affinity mask."), gettext_noop("Set to 0 to disable changing affinity."), MDFNST_UINT, "0", "0x0000000000000000", "0xFFFFFFFFFFFFFFFF" },
137 
138   { NULL }
139 };
140 
141 static const MDFNSetting RenamedSettings[] =
142 {
143  { "path_snap", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS  , 	"filesys.path_snap"	},
144  { "path_sav", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS  , 	"filesys.path_sav"	},
145  { "path_state", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS  ,	"filesys.path_state"	},
146  { "path_movie", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS  , 	"filesys.path_movie"	},
147  { "path_cheat", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS  , 	"filesys.path_cheat"	},
148  { "path_palette", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS  , 	"filesys.path_palette"	},
149  { "path_firmware", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS  , "filesys.path_firmware"	},
150 
151  { "sounddriver", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS  , "sound.driver"      },
152  { "sounddevice", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS  , "sound.device"      },
153  { "soundrate", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS    , "sound.rate"        },
154  { "soundvol", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS     , "sound.volume"      },
155  { "soundbufsize", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "sound.buffer_time" },
156 
157  { "nethost", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS         , "netplay.host"   },
158  { "netport", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS         , "netplay.port"   },
159  { "netpassword", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS     , "netplay.password"},
160  { "netlocalplayers", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "netplay.localplayers" },
161  { "netnick", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS         , "netplay.nick"   },
162  { "netgamekey", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS      , "netplay.gamekey"        },
163  { "netsmallfont", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS    , "netplay.smallfont" },
164 
165  { "frameskip", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS       , "video.frameskip" },
166  { "vdriver", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS         , "video.driver" },
167  { "glvsync", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS         , "video.glvsync" },
168  { "fs", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS              , "video.fs" },
169 
170  { "autofirefreq", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS    , "input.autofirefreq" },
171  { "analogthreshold", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS , "input.joystick.axis_threshold" },
172  { "ckdelay", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS         , "input.ckdelay" },
173 
174 
175  { "psx.input.port1.multitap", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS         , "psx.input.pport1.multitap" },
176  { "psx.input.port2.multitap", MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS         , "psx.input.pport2.multitap" },
177 
178  { "snes_faust.spexf",	       MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS         , "snes_faust.spex" },
179 
180  { "netplay.smallfont",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "netplay.console.font" },
181 
182  { "cdplay.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "cdplay.shader" },
183  { "demo.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "demo.shader" },
184  { "gb.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "gb.shader" },
185  { "gba.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "gba.shader" },
186  { "gg.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "gg.shader" },
187  { "lynx.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "lynx.shader" },
188  { "md.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "md.shader" },
189  { "nes.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "nes.shader" },
190  { "ngp.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "ngp.shader" },
191  { "pce.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "pce.shader" },
192  { "pce_fast.pixshader",	MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "pce_fast.shader" },
193  { "pcfx.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "pcfx.shader" },
194  { "player.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "player.shader" },
195  { "psx.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "psx.shader" },
196  { "sms.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "sms.shader" },
197  { "snes.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "snes.shader" },
198  { "snes_faust.pixshader",	MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "snes_faust.shader" },
199  { "ss.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "ss.shader" },
200  { "ssfplay.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "ssfplay.shader" },
201  { "vb.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "vb.shader" },
202  { "wswan.pixshader",		MDFNSF_NOFLAGS, NULL, NULL, MDFNST_ALIAS	, "wswan.shader" },
203 
204  { NULL }
205 };
206 
207 static uint8* CustomPalette = NULL;
208 static uint32 CustomPaletteNumEntries = 0;
209 
210 static uint32 PortDevice[16];
211 static uint8* PortData[16];
212 static uint32 PortDataLen[16];
213 
214 MDFNGI *MDFNGameInfo = NULL;
215 
216 static QTRecord *qtrecorder = NULL;
217 static WAVRecord *wavrecorder = NULL;
218 static Fir_Resampler<16> ff_resampler;
219 static double LastSoundMultiplier;
220 static double last_sound_rate;
221 static MDFN_PixelFormat last_pixel_format;
222 static bool PrevInterlaced;
223 static std::unique_ptr<Deinterlacer> deint;
224 
225 static bool FFDiscard = false; // TODO:  Setting to discard sound samples instead of increasing pitch
226 
227 static std::vector<CDInterface *> CDInterfaces;	// FIXME: Cleanup on error out.
228 
229 struct DriveMediaStatus
230 {
231  uint32 state_idx = 0;
232  uint32 media_idx = 0;
233  uint32 orientation_idx = 0;
234 };
235 
236 static std::vector<DriveMediaStatus> DMStatus, DMStatusSaveStateTemp;
237 static std::vector<uint32> DMSNoMedia;
238 static bool ValidateDMS(const std::vector<DriveMediaStatus>& dms);
239 
SettingChanged(const char * name)240 static void SettingChanged(const char* name)
241 {
242  if(!strcmp(name, "video.deinterlacer"))
243  {
244   deint.reset(nullptr);
245   deint.reset(Deinterlacer::Create(MDFN_GetSettingUI(name)));
246  }
247 }
248 
MDFNI_StartWAVRecord(const char * path,double SoundRate)249 bool MDFNI_StartWAVRecord(const char *path, double SoundRate)
250 {
251  try
252  {
253   wavrecorder = new WAVRecord(path, SoundRate, MDFNGameInfo->soundchan);
254  }
255  catch(std::exception &e)
256  {
257   MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
258   return(false);
259  }
260 
261  return(true);
262 }
263 
MDFNI_StartAVRecord(const char * path,double SoundRate)264 bool MDFNI_StartAVRecord(const char *path, double SoundRate)
265 {
266  try
267  {
268   QTRecord::VideoSpec spec;
269 
270   memset(&spec, 0, sizeof(spec));
271 
272   spec.SoundRate = SoundRate;
273   spec.SoundChan = MDFNGameInfo->soundchan;
274   spec.VideoWidth = MDFNGameInfo->lcm_width;
275   spec.VideoHeight = MDFNGameInfo->lcm_height;
276   spec.VideoCodec = MDFN_GetSettingI("qtrecord.vcodec");
277   spec.MasterClock = MDFNGameInfo->MasterClock;
278 
279   if(spec.VideoWidth < MDFN_GetSettingUI("qtrecord.w_double_threshold"))
280    spec.VideoWidth *= 2;
281 
282   if(spec.VideoHeight < MDFN_GetSettingUI("qtrecord.h_double_threshold"))
283    spec.VideoHeight *= 2;
284 
285 
286   spec.AspectXAdjust = ((double)MDFNGameInfo->nominal_width * 2) / spec.VideoWidth;
287   spec.AspectYAdjust = ((double)MDFNGameInfo->nominal_height * 2) / spec.VideoHeight;
288 
289   MDFN_printf("\n");
290   MDFN_printf(_("Starting QuickTime recording to file \"%s\":\n"), path);
291   MDFN_indent(1);
292   MDFN_printf(_("Video width: %u\n"), spec.VideoWidth);
293   MDFN_printf(_("Video height: %u\n"), spec.VideoHeight);
294   MDFN_printf(_("Video codec: %s\n"), MDFN_GetSettingS("qtrecord.vcodec").c_str());
295 
296   if(spec.SoundRate && spec.SoundChan)
297   {
298    MDFN_printf(_("Sound rate: %u\n"), std::min<uint32>(spec.SoundRate, 64000));
299    MDFN_printf(_("Sound channels: %u\n"), spec.SoundChan);
300   }
301   else
302    MDFN_printf(_("Sound: Disabled\n"));
303 
304   MDFN_indent(-1);
305   MDFN_printf("\n");
306 
307   qtrecorder = new QTRecord(path, spec);
308  }
309  catch(std::exception &e)
310  {
311   MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
312   return(false);
313  }
314  return(true);
315 }
316 
MDFNI_StopAVRecord(void)317 void MDFNI_StopAVRecord(void)
318 {
319  if(qtrecorder)
320  {
321   delete qtrecorder;
322   qtrecorder = NULL;
323  }
324 }
325 
MDFNI_StopWAVRecord(void)326 void MDFNI_StopWAVRecord(void)
327 {
328  if(wavrecorder)
329  {
330   delete wavrecorder;
331   wavrecorder = NULL;
332  }
333 }
334 
MDFNI_CloseGame(void)335 void MDFNI_CloseGame(void)
336 {
337  if(MDFNGameInfo)
338  {
339   MDFNI_NetplayDisconnect();
340 
341   MDFNSRW_End();
342   MDFNMOV_Stop();
343 
344   if(MDFNGameInfo->GameType != GMT_PLAYER)
345    MDFN_FlushGameCheats(0);
346 
347   MDFNGameInfo->CloseGame();
348 
349   //
350   //
351   //
352   MDFNGameInfo->name.clear();
353 
354   if(MDFNGameInfo->RMD)
355   {
356    delete MDFNGameInfo->RMD;
357    MDFNGameInfo->RMD = NULL;
358   }
359 
360   MDFNMP_Kill();
361 
362 
363   //
364   //
365   memset(MDFNGameInfo->MD5, 0, sizeof(MDFNGameInfo->MD5));
366   MDFNGameInfo = NULL;
367 
368   for(unsigned i = 0; i < CDInterfaces.size(); i++)
369    delete CDInterfaces[i];
370   CDInterfaces.clear();
371  }
372  TBlur_Kill();
373 
374  #ifdef WANT_DEBUGGER
375  MDFNDBG_Kill();
376  #endif
377 
378  for(unsigned x = 0; x < 16; x++)
379  {
380   if(PortData[x])
381   {
382    free(PortData[x]);
383    PortData[x] = NULL;
384   }
385 
386   PortDevice[x] = ~0U;
387   PortDataLen[x] = 0;
388  }
389 
390  if(CustomPalette != NULL)
391  {
392   delete[] CustomPalette;
393   CustomPalette = NULL;
394  }
395  CustomPaletteNumEntries = 0;
396 
397  MDFN_ClearAllOverrideSettings();
398 }
399 
400 }
401 
402 #ifdef WANT_APPLE2_EMU
403 extern Mednafen::MDFNGI EmulatedApple2;
404 #endif
405 
406 #ifdef WANT_NES_EMU
407 extern Mednafen::MDFNGI EmulatedNES;
408 #endif
409 
410 #ifdef WANT_NES_NEW_EMU
411 extern Mednafen::MDFNGI EmulatedNES_New;
412 #endif
413 
414 #ifdef WANT_SNES_EMU
415 extern Mednafen::MDFNGI EmulatedSNES;
416 #endif
417 
418 #ifdef WANT_SNES_FAUST_EMU
419 extern Mednafen::MDFNGI EmulatedSNES_Faust;
420 #endif
421 
422 #ifdef WANT_GBA_EMU
423 extern Mednafen::MDFNGI EmulatedGBA;
424 #endif
425 
426 #ifdef WANT_GB_EMU
427 extern Mednafen::MDFNGI EmulatedGB;
428 #endif
429 
430 #ifdef WANT_LYNX_EMU
431 extern Mednafen::MDFNGI EmulatedLynx;
432 #endif
433 
434 #ifdef WANT_MD_EMU
435 extern Mednafen::MDFNGI EmulatedMD;
436 #endif
437 
438 #ifdef WANT_NGP_EMU
439 extern Mednafen::MDFNGI EmulatedNGP;
440 #endif
441 
442 #ifdef WANT_PCE_EMU
443 extern Mednafen::MDFNGI EmulatedPCE;
444 #endif
445 
446 #ifdef WANT_PCE_FAST_EMU
447 extern Mednafen::MDFNGI EmulatedPCE_Fast;
448 #endif
449 
450 #ifdef WANT_PCFX_EMU
451 extern Mednafen::MDFNGI EmulatedPCFX;
452 #endif
453 
454 #ifdef WANT_PSX_EMU
455 extern Mednafen::MDFNGI EmulatedPSX;
456 #endif
457 
458 #ifdef WANT_SS_EMU
459 extern Mednafen::MDFNGI EmulatedSS;
460 #endif
461 
462 #ifdef WANT_SSFPLAY_EMU
463 extern Mednafen::MDFNGI EmulatedSSFPlay;
464 #endif
465 
466 #ifdef WANT_VB_EMU
467 extern Mednafen::MDFNGI EmulatedVB;
468 #endif
469 
470 #ifdef WANT_WSWAN_EMU
471 extern Mednafen::MDFNGI EmulatedWSwan;
472 #endif
473 
474 #ifdef WANT_SMS_EMU
475 extern Mednafen::MDFNGI EmulatedSMS, EmulatedGG;
476 #endif
477 
478 extern Mednafen::MDFNGI EmulatedCDPlay;
479 extern Mednafen::MDFNGI EmulatedDEMO;
480 
481 namespace Mednafen
482 {
483 std::vector<MDFNGI *> MDFNSystems;
484 static std::list<MDFNGI *> MDFNSystemsPrio;
485 
MDFNSystemsPrio_CompareFunc(const MDFNGI * first,const MDFNGI * second)486 bool MDFNSystemsPrio_CompareFunc(const MDFNGI* first, const MDFNGI* second)
487 {
488  if(first->ModulePriority > second->ModulePriority)
489   return true;
490 
491  return false;
492 }
493 
AddSystem(MDFNGI * system)494 static void AddSystem(MDFNGI *system)
495 {
496  MDFNSystems.push_back(system);
497 }
498 
MDFNI_DumpModulesDef(const char * fn)499 void MDFNI_DumpModulesDef(const char *fn)
500 {
501  FileStream fp(fn, FileStream::MODE_WRITE);
502 
503  fp.print_format("%s\n", MEDNAFEN_VERSION);
504 
505  for(unsigned int i = 0; i < MDFNSystems.size(); i++)
506  {
507   fp.print_format("%s\n", MDFNSystems[i]->shortname);
508   fp.print_format("%s\n", MDFNSystems[i]->fullname);
509   fp.print_format("%d\n", MDFNSystems[i]->nominal_width);
510   fp.print_format("%d\n", MDFNSystems[i]->nominal_height);
511 
512   size_t cpcount = 0;
513 
514   if(MDFNSystems[i]->CPInfo)
515    for(auto cpi = MDFNSystems[i]->CPInfo; cpi->description || cpi->name_override; cpi++)
516     cpcount++;
517 
518   fp.print_format("%zu\n", cpcount);
519 
520   if(MDFNSystems[i]->CPInfo)
521   {
522    for(auto cpi = MDFNSystems[i]->CPInfo; cpi->description || cpi->name_override; cpi++)
523    {
524     fp.print_format("%s.pal\n", cpi->name_override ? cpi->name_override : MDFNSystems[i]->shortname);
525     fp.print_format("%s\n", cpi->description);
526     for(unsigned vec : cpi->valid_entry_count)
527     {
528      if(!vec)
529       break;
530      fp.print_format("%u ", vec);
531     }
532     fp.print_format("\n");
533    }
534   }
535 
536   std::vector<GameDB_Database> gamedb;
537 
538   if(MDFNSystems[i]->GetInternalDB)
539    MDFNSystems[i]->GetInternalDB(&gamedb);
540 
541   fp.print_format("%zu\n", gamedb.size());
542 
543   for(const GameDB_Database& db : gamedb)
544   {
545    fp.print_format("%s\n", MDFN_strescape(db.ShortName).c_str());
546    fp.print_format("%s\n", MDFN_strescape(db.FullName).c_str());
547    fp.print_format("%s\n", MDFN_strescape(db.Description).c_str());
548 
549    fp.print_format("%zu\n", db.Entries.size());
550    for(const GameDB_Entry& gdbe : db.Entries)
551    {
552     fp.print_format("%s\n", MDFN_strescape(gdbe.Name).c_str());
553     fp.print_format("%s\n", MDFN_strescape(gdbe.GameID).c_str());
554     fp.print_format("%u\n", gdbe.GameIDIsHash);
555     fp.print_format("%s\n", MDFN_strescape(gdbe.Setting).c_str());
556     fp.print_format("%s\n", MDFN_strescape(gdbe.Purpose).c_str());
557    }
558   }
559  }
560 
561  fp.close();
562 }
563 
564 struct M3U_ListEntry
565 {
566  std::string path;
567  std::unique_ptr<std::string> name;
568 };
569 
ReadM3U(std::vector<M3U_ListEntry> & file_list,size_t * default_cd,VirtualFS * vfs,std::string path,unsigned depth=0)570 static MDFN_COLD void ReadM3U(std::vector<M3U_ListEntry> &file_list, size_t* default_cd, VirtualFS* vfs, std::string path, unsigned depth = 0)
571 {
572  MemoryStream m3u_file(new FileStream(path, FileStream::MODE_READ));
573  std::string dir_path;
574  std::string linebuf;
575  std::unique_ptr<std::string> name;
576  int termc;
577 
578  if(!m3u_file.read_utf8_bom())
579  {
580   m3u_file.mswin_utf8_convert_kludge();
581  }
582 
583  vfs->get_file_path_components(path, &dir_path);
584 
585  linebuf.reserve(2048);
586 
587  while((termc = m3u_file.get_line(linebuf)) >= 0)
588  {
589   std::string efp;
590 
591   MDFN_rtrim(&linebuf);
592 
593   // Blank line, skip it.
594   if(linebuf.size() == 0)
595    continue;
596 
597   // Comment line, skip it.
598   if(linebuf[0] == '#')
599   {
600    if(!strcmp(linebuf.c_str(), "#MEDNAFEN_DEFAULT"))
601     *default_cd = file_list.size();
602    else if(!strncmp(linebuf.c_str(), "#MEDNAFEN_LABEL", 15))
603    {
604     name.reset(new std::string(linebuf.substr(15)));
605     UTF8_sanitize(name.get());
606     MDFN_zapctrlchars(name.get());
607     MDFN_trim(name.get());
608    }
609    continue;
610   }
611 
612   efp = vfs->eval_fip(dir_path, linebuf);
613 
614   if(efp.size() >= 4 && !MDFN_strazicmp(efp.substr(efp.size() - 4).c_str(), ".m3u"))
615   {
616    if(efp == path)
617     throw(MDFN_Error(0, _("M3U at \"%s\" references self."), efp.c_str()));
618 
619    if(depth == 99)
620     throw(MDFN_Error(0, _("M3U load recursion too deep!")));
621 
622    ReadM3U(file_list, default_cd, vfs, efp, depth++);
623   }
624   else
625    file_list.emplace_back(M3U_ListEntry({efp, std::move(name)}));
626  }
627 }
628 
PrintDiscsLayout(std::vector<CDInterface * > * ifaces)629 static MDFN_COLD void PrintDiscsLayout(std::vector<CDInterface *> *ifaces)
630 {
631  MDFN_AutoIndent aind(1);
632 
633  for(unsigned i = 0; i < (*ifaces).size(); i++)
634  {
635   CDUtility::TOC toc;
636 
637   (*ifaces)[i]->ReadTOC(&toc);
638 
639   MDFN_printf(_("CD %u TOC:\n"), i + 1);
640   {
641    MDFN_AutoIndent aindd(1);
642    int32 eff_lt = 0;
643    const char* disc_type_string;
644 
645    switch(toc.disc_type)
646    {
647     default:
648 	disc_type_string = "";
649 	break;
650 
651     case CDUtility::DISC_TYPE_CDDA_OR_M1:
652 	disc_type_string = _(" (CD-DA or Mode 1)");
653 	break;
654 
655     case CDUtility::DISC_TYPE_CD_I:
656 	disc_type_string = _(" (CD-i)");
657 	break;
658 
659     case CDUtility::DISC_TYPE_CD_XA:
660 	disc_type_string = _(" (CD-XA)");
661 	break;
662    }
663 
664    MDFN_printf(_("Disc Type: 0x%02x%s\n"), toc.disc_type, disc_type_string);
665    MDFN_printf(_("First Track: %2d\n"), toc.first_track);
666    MDFN_printf(_("Last Track:  %2d\n"), toc.last_track);
667 
668    for(int32 track = 1; track <= 99; track++)
669    {
670     if(!toc.tracks[track].valid)
671      continue;
672 
673     eff_lt = track;
674 
675     uint8 m, s, f;
676 
677     CDUtility::LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f);
678 
679     MDFN_printf(_("Track %2d, MSF: %02d:%02d:%02d, LBA: %6d  %s%s\n"),
680 		track,
681 		m, s, f,
682 		toc.tracks[track].lba,
683 		(toc.tracks[track].control & 0x4) ? "DATA" : "AUDIO",
684 		(track < toc.first_track || track > toc.last_track) ? _(" (Hidden)") : "");
685    }
686 
687    MDFN_printf(_("Leadout: %6d  %s\n"), toc.tracks[100].lba, (toc.tracks[100].control & 0x4) ? "DATA" : "AUDIO");
688 
689    if((toc.tracks[eff_lt].control & 0x4) != (toc.tracks[100].control & 0x4))
690     MDFN_printf(_("WARNING:  DATA/AUDIO TYPE MISMATCH BETWEEN LAST TRACK AND LEADOUT AREA."));
691 
692    MDFN_printf("\n");
693   }
694  }
695 }
696 
CalcDiscsLayoutMD5(std::vector<CDInterface * > * ifaces,uint8 out_md5[16])697 static MDFN_COLD void CalcDiscsLayoutMD5(std::vector<CDInterface *> *ifaces, uint8 out_md5[16])
698 {
699   md5_context layout_md5;
700 
701   layout_md5.starts();
702 
703   for(unsigned i = 0; i < (*ifaces).size(); i++)
704   {
705    CDUtility::TOC toc;
706 
707    (*ifaces)[i]->ReadTOC(&toc);
708 
709    layout_md5.update_u32_as_lsb(toc.first_track);
710    layout_md5.update_u32_as_lsb(toc.last_track);
711    layout_md5.update_u32_as_lsb(toc.tracks[100].lba);
712 
713    for(uint32 track = 1; track <= 99; track++)
714    {
715     if(!toc.tracks[track].valid)
716      continue;
717 
718     layout_md5.update_u32_as_lsb(toc.tracks[track].lba);
719     layout_md5.update_u32_as_lsb(toc.tracks[track].control & 0x4);
720    }
721   }
722 
723   layout_md5.finish(out_md5);
724 }
725 
LoadCustomPalette(void)726 static MDFN_COLD void LoadCustomPalette(void)
727 {
728  if(!MDFNGameInfo->CPInfo)
729   return;
730 
731  for(auto cpi = MDFNGameInfo->CPInfo; cpi->description || cpi->name_override; cpi++)
732  {
733   if(!(MDFNGameInfo->CPInfoActiveBF & (1U << (cpi - MDFNGameInfo->CPInfo))))
734    continue;
735 
736   std::string colormap_fn = MDFN_MakeFName(MDFNMKF_PALETTE, 0, cpi->name_override);
737 
738   MDFN_printf("\n");
739   MDFN_printf(_("Loading custom palette from \"%s\"...\n"),  colormap_fn.c_str());
740   {
741    MDFN_AutoIndent aind(1);
742 
743    try
744    {
745     FileStream fp(colormap_fn, FileStream::MODE_READ);
746     const uint64 fpsz = fp.size();
747 
748     for(auto vec = cpi->valid_entry_count; *vec; vec++)
749     {
750      if(fpsz == *vec * 3)
751      {
752       CustomPaletteNumEntries = *vec;
753       CustomPalette = new uint8[CustomPaletteNumEntries * 3];
754 
755       fp.read(CustomPalette, CustomPaletteNumEntries * 3);
756 
757       return;
758      }
759     }
760 
761     //
762     // File size is not valid, print out an error message with helpful information.
763     //
764     std::string vfszs;
765     for(auto vec = cpi->valid_entry_count; *vec; vec++)
766     {
767      if(vfszs.size())
768       vfszs += _(", ");
769 
770      vfszs += std::to_string(3 * *vec);
771     }
772 
773     throw MDFN_Error(0, _("Custom palette file's size(%llu bytes) is incorrect.  Valid sizes are: %s"), (unsigned long long)fpsz, vfszs.c_str());
774    }
775    catch(MDFN_Error &e)
776    {
777     MDFN_printf(_("Error: %s\n"), e.what());
778 
779     if(e.GetErrno() != ENOENT)
780      throw;
781 
782     return;
783    }
784    catch(std::exception &e)
785    {
786     MDFN_printf(_("Error: %s\n"), e.what());
787     throw;
788    }
789   }
790   break;
791  }
792 }
793 
LoadCommonPost(VirtualFS * vfs,const char * path)794 static MDFN_COLD void LoadCommonPost(VirtualFS* vfs, const char* path)
795 {
796 	DMStatus.resize(MDFNGameInfo->RMD->Drives.size());
797         DMStatusSaveStateTemp.resize(DMStatus.size());
798 
799 	DMSNoMedia.clear();
800 	for(uint32 drive_idx = 0; drive_idx < MDFNGameInfo->RMD->Drives.size(); drive_idx++)
801 	{
802          const RMD_Drive& drive = MDFNGameInfo->RMD->Drives[drive_idx];
803 
804          for(uint32 state_idx = 0; state_idx < drive.PossibleStates.size(); state_idx++)
805 	 {
806           const RMD_State& state = drive.PossibleStates[state_idx];
807           if(!state.MediaPresent)
808           {
809            DMSNoMedia.push_back(state_idx);
810            break;
811           }
812          }
813          assert(DMSNoMedia.size() == drive_idx + 1);
814 	}
815         //
816         //
817 	if(MDFNGameInfo->name.size() == 0 && path)
818 	{
819 	 vfs->get_file_path_components(path, NULL, &MDFNGameInfo->name);
820 
821 	 for(auto& c : MDFNGameInfo->name)
822 	  if(c == '_' || (uint8)c < 0x20)
823 	   c = ' ';
824 
825 	 MDFN_trim(&MDFNGameInfo->name);
826 	}
827 
828         //
829         //
830         //
831 
832 	LoadCustomPalette();
833 	if(MDFNGameInfo->GameType != GMT_PLAYER)
834 	{
835 	 MDFN_LoadGameCheats(NULL);
836 	 MDFNMP_InstallReadPatches();
837 	}
838 
839 	MDFNI_SetLayerEnableMask(~0ULL);
840 
841 	#ifdef WANT_DEBUGGER
842 	MDFNDBG_PostGameLoad();
843 	#endif
844 
845 	MDFNSS_CheckStates();
846 	MDFNMOV_CheckMovies();
847 
848 	PrevInterlaced = false;
849 	SettingChanged("video.deinterlacer");
850 
851 	TBlur_Init();
852 
853 	MDFNSRW_Begin();
854 
855 	LastSoundMultiplier = 1;
856 	last_sound_rate = -1;
857 	last_pixel_format = MDFN_PixelFormat();
858 }
859 
LoadCD(const char * force_module,VirtualFS * vfs,const char * path,CDInterface * cdif=nullptr)860 static MDFNGI *LoadCD(const char *force_module, VirtualFS* vfs, const char *path, CDInterface* cdif = nullptr)
861 {
862  std::vector<M3U_ListEntry> file_list;
863  uint8 LayoutMD5[16];
864  size_t default_cd = 0;
865 
866  try
867  {
868   MDFN_AutoIndent aind(1);
869   const bool image_memcache = MDFN_GetSettingB("cd.image_memcache");
870   const uint64 affinity = MDFN_GetSettingUI("affinity.cd");
871 
872   if(cdif)
873   {
874    file_list.emplace_back(M3U_ListEntry({ path }));
875    CDInterfaces.resize(1);
876    CDInterfaces[0] = cdif;
877   }
878   else
879   {
880    if(strlen(path) > 4 && !MDFN_strazicmp(path + strlen(path) - 4, ".m3u"))
881     ReadM3U(file_list, &default_cd, vfs, path);
882    else
883     file_list.emplace_back(M3U_ListEntry({ path }));
884 
885    CDInterfaces.resize(file_list.size());
886    for(size_t i = 0; i < file_list.size(); i++)
887     CDInterfaces[i] = CDInterface::Open(vfs, file_list[i].path, image_memcache, affinity);
888   }
889 
890   GetFileBase(path);
891  }
892  catch(std::exception &e)
893  {
894   MDFN_Notify(MDFN_NOTICE_ERROR, _("Error opening CD: %s"), e.what());
895 
896   for(unsigned i = 0; i < CDInterfaces.size(); i++)
897   {
898    if(CDInterfaces[i])
899    {
900     delete CDInterfaces[i];
901     CDInterfaces[i] = NULL;
902    }
903   }
904   CDInterfaces.clear();
905 
906   MDFNGameInfo = NULL;
907 
908   return(NULL);
909  }
910 
911  MDFN_printf("\n");
912 
913  //
914  // Print out a track list for all discs.
915  //
916  PrintDiscsLayout(&CDInterfaces);
917 
918  //
919  // Calculate layout MD5.  The system emulation LoadCD() code is free to ignore this value and calculate
920  // its own, or to use it to look up a game in its database.
921  //
922  CalcDiscsLayoutMD5(&CDInterfaces, LayoutMD5);
923 
924  try
925  {
926 	std::unique_ptr<RMD_Layout> rmd(new RMD_Layout());
927 	MDFNGameInfo = NULL;
928 
929 	{
930   	 RMD_Drive dr;
931 
932 	 dr.Name = "Virtual CD Drive";
933 	 dr.PossibleStates.push_back(RMD_State({"Tray Open", false, false, true}));
934 	 dr.PossibleStates.push_back(RMD_State({"Tray Closed (Empty)", false, false, false}));
935 	 dr.PossibleStates.push_back(RMD_State({"Tray Closed", true, true, false}));
936 	 dr.CompatibleMedia.push_back(0);
937 	 dr.MediaMtoPDelay = 2000;
938 
939 	 rmd->Drives.push_back(dr);
940 	 rmd->DrivesDefaults.push_back(RMD_DriveDefaults({0, 0, 0}));
941 	 rmd->MediaTypes.push_back(RMD_MediaType({"CD"}));
942 	}
943 
944 	for(size_t i = 0; i < CDInterfaces.size(); i++)
945 	{
946          if(i == default_cd)
947          {
948 	  rmd->DrivesDefaults[0].State = 2;	// Tray Closed
949 	  rmd->DrivesDefaults[0].Media = i;
950           rmd->DrivesDefaults[0].Orientation = 0;
951          }
952 
953 	 if(file_list[i].name)
954 	  rmd->Media.push_back(RMD_Media({std::string(1, '"') + *file_list[i].name + '"', 0}));
955 	 else
956 	 {
957 	  char namebuf[128];
958 	  trio_snprintf(namebuf, sizeof(namebuf), _("Disc %zu of %zu"), i + 1, CDInterfaces.size());
959 	  rmd->Media.push_back(RMD_Media({namebuf, 0}));
960 	 }
961 	}
962 
963         for(std::list<MDFNGI *>::iterator it = MDFNSystemsPrio.begin(); it != MDFNSystemsPrio.end(); it++)  //_unsigned int x = 0; x < MDFNSystems.size(); x++)
964         {
965          if(force_module)
966          {
967           if(!strcmp(force_module, (*it)->shortname))
968           {
969            MDFNGameInfo = *it;
970            break;
971           }
972          }
973          else
974          {
975           char tmpstr[256];
976           trio_snprintf(tmpstr, 256, "%s.enable", (*it)->shortname);
977 
978           // Is module enabled?
979           if(!MDFN_GetSettingB(tmpstr))
980 	  {
981 	   MDFN_printf(_("Skipping module \"%s\" per \"%s\" setting.\n"), (*it)->shortname, tmpstr);
982            continue;
983 	  }
984 
985           if(!(*it)->LoadCD || !(*it)->TestMagicCD)
986            continue;
987 
988           if((*it)->TestMagicCD(&CDInterfaces))
989           {
990            MDFNGameInfo = *it;
991            break;
992           }
993          }
994         }
995 
996         if(!MDFNGameInfo)
997         {
998 	 if(force_module)
999 	  throw MDFN_Error(0, _("Unrecognized system \"%s\"!"), force_module);
1000 	 else
1001  	  throw MDFN_Error(0, _("Could not find a system that supports this CD."));
1002         }
1003 
1004 	// This if statement will be true if force_module references a system without CDROM support.
1005         if(!MDFNGameInfo->LoadCD)
1006          throw MDFN_Error(0, _("Specified system \"%s\" doesn't support CDs!"), force_module);
1007 
1008         MDFN_printf(_("Using module: %s(%s)\n"), MDFNGameInfo->shortname, MDFNGameInfo->fullname);
1009 	{
1010 	 MDFN_AutoIndent aindentgm(1);
1011 
1012 	 assert(MDFNGameInfo->soundchan != 0);
1013 
1014          MDFNGameInfo->name.clear();
1015 	 MDFNGameInfo->DesiredInput.clear();
1016          MDFNGameInfo->rotated = 0;
1017 	 MDFNGameInfo->RMD = rmd.get();
1018 
1019 	 memcpy(MDFNGameInfo->MD5, LayoutMD5, 16);
1020 
1021 	 {
1022 	  std::string modoverride_settings_file_path = MDFN_GetBaseDirectory() + PSS + MDFNGameInfo->shortname + ".cfg";
1023 	  MDFN_LoadSettings(modoverride_settings_file_path.c_str(), true);
1024 	 }
1025 	 {
1026 	  std::string pgcoverride_settings_file_path = MDFN_MakeFName(MDFNMKF_PGCONFIG, 0, NULL);
1027 	  MDFN_LoadSettings(pgcoverride_settings_file_path.c_str(), true);
1028  	 }
1029 
1030 	 MDFN_printf("\n");
1031 
1032 	 MDFNGameInfo->LoadCD(&CDInterfaces);
1033 	}
1034 
1035 	LoadCommonPost(vfs, path);
1036 
1037 	//
1038 	//
1039 	rmd.release();
1040  }
1041  catch(std::exception &e)
1042  {
1043 	MDFN_Notify(MDFN_NOTICE_ERROR, "%s", e.what());
1044 
1045 	for(unsigned i = 0; i < CDInterfaces.size(); i++)
1046 	{
1047 	 if(CDInterfaces[i])
1048 	 {
1049 	  delete CDInterfaces[i];
1050 	  CDInterfaces[i] = NULL;
1051 	 }
1052 	}
1053 	CDInterfaces.clear();
1054 
1055 	if(MDFNGameInfo != NULL)
1056 	{
1057 	 memset(MDFNGameInfo->MD5, 0, sizeof(MDFNGameInfo->MD5));
1058 	 MDFNGameInfo->RMD = NULL;
1059 	 MDFNGameInfo = NULL;
1060 	}
1061 
1062 	if(CustomPalette != NULL)
1063 	{
1064 	 delete[] CustomPalette;
1065 	 CustomPalette = NULL;
1066 	}
1067 	CustomPaletteNumEntries = 0;
1068 
1069 	MDFN_ClearAllOverrideSettings();
1070 
1071 	return NULL;
1072  }
1073 
1074  return MDFNGameInfo;
1075 }
1076 
MDFNI_LoadExternalCD(const char * force_module,const char * path_hint,CDInterface * cdif)1077 MDFNGI *MDFNI_LoadExternalCD(const char* force_module, const char* path_hint, CDInterface* cdif)
1078 {
1079  return LoadCD(force_module, &::Mednafen::NVFS, path_hint, cdif);
1080 }
1081 
LoadIPS(MDFNFILE * mfgf,const std::string & path)1082 static MDFN_COLD void LoadIPS(MDFNFILE* mfgf, const std::string& path)
1083 {
1084  MDFN_printf(_("Applying IPS file \"%s\"...\n"), path.c_str());
1085 
1086  try
1087  {
1088   FileStream IPSFile(path, FileStream::MODE_READ);
1089 
1090   mfgf->ApplyIPS(&IPSFile);
1091  }
1092  catch(MDFN_Error &e)
1093  {
1094   MDFN_indent(1);
1095   MDFN_printf(_("Failed: %s\n"), e.what());
1096   MDFN_indent(-1);
1097   if(e.GetErrno() != ENOENT)
1098    throw;
1099  }
1100  catch(std::exception &e)
1101  {
1102   MDFN_indent(1);
1103   MDFN_printf(_("Failed: %s\n"), e.what());
1104   MDFN_indent(-1);
1105   throw;
1106  }
1107 }
1108 
IsModuleEnabled(MDFNGI * gi)1109 static bool IsModuleEnabled(MDFNGI* gi)
1110 {
1111  char tmpstr[256];
1112  trio_snprintf(tmpstr, 256, "%s.enable", gi->shortname);
1113 
1114  // Is module enabled?
1115  return MDFN_GetSettingB(tmpstr);
1116 }
1117 
FindCompatibleModule(const char * force_module,GameFile * gf)1118 static MDFN_COLD MDFNGI* FindCompatibleModule(const char* force_module, GameFile* gf)
1119 {
1120  //for(unsigned pass = 0; pass < 2; pass++)
1121  //{
1122 	for(std::list<MDFNGI *>::iterator it = MDFNSystemsPrio.begin(); it != MDFNSystemsPrio.end(); it++)  //_unsigned int x = 0; x < MDFNSystems.size(); x++)
1123 	{
1124 	 if(force_module)
1125 	 {
1126           if(!strcmp(force_module, (*it)->shortname))
1127           {
1128 	   if(!(*it)->Load)
1129 	   {
1130 	    if((*it)->LoadCD)
1131              throw MDFN_Error(0, _("Specified system only supports CD(physical, or image files, such as *.cue and *.toc) loading."));
1132 	    else
1133              throw MDFN_Error(0, _("Specified system does not support normal file loading."));
1134 	   }
1135            return(*it);
1136           }
1137 	 }
1138 	 else
1139 	 {
1140 	  char tmpstr[256];
1141 	  trio_snprintf(tmpstr, 256, "%s.enable", (*it)->shortname);
1142 
1143 	  // Is module enabled?
1144 	  if(!MDFN_GetSettingB(tmpstr))
1145 	  {
1146 	   MDFN_printf(_("Skipping module \"%s\" per \"%s\" setting.\n"), (*it)->shortname, tmpstr);
1147 	   continue;
1148 	  }
1149 
1150 	  if(!(*it)->Load || !(*it)->TestMagic)
1151 	   continue;
1152 
1153 	  gf->stream->rewind();
1154 
1155 	  if((*it)->TestMagic(gf))
1156 	  {
1157 	   return(*it);
1158 	  }
1159 	 }
1160 	}
1161  //}
1162 
1163  return(NULL);
1164 }
1165 
MDFNI_LoadGame(const char * force_module,VirtualFS * vfs,const char * path,bool force_cd)1166 MDFNGI *MDFNI_LoadGame(const char *force_module, VirtualFS* vfs, const char* path, bool force_cd)
1167 {
1168  assert(path != nullptr);
1169  const size_t path_len = strlen(path);
1170 
1171  MDFNI_CloseGame();
1172 
1173  MDFN_printf(_("Loading %s...\n"), path);
1174 
1175  if(force_cd || (path_len > 4 && (!MDFN_strazicmp(path + path_len - 4, ".cue") || !MDFN_strazicmp(path + path_len - 4, ".toc") || !MDFN_strazicmp(path + path_len - 4, ".ccd") || !MDFN_strazicmp(path + path_len - 4, ".m3u"))))
1176  {
1177   return LoadCD(force_module, vfs, path);
1178  }
1179 
1180  try
1181  {
1182 	std::vector<FileExtensionSpecStruct> valid_iae;
1183 
1184 	// Construct a list of known file extensions for MDFNFILE
1185 	for(unsigned int i = 0; i < MDFNSystems.size(); i++)
1186 	{
1187 	 const FileExtensionSpecStruct *curexts = MDFNSystems[i]->FileExtensions;
1188 
1189 	 // If we're forcing a module, only look for extensions corresponding to that module
1190 	 if(force_module && strcmp(MDFNSystems[i]->shortname, force_module))
1191 	  continue;
1192 
1193 	 if(!force_module && !IsModuleEnabled(MDFNSystems[i]))
1194 	  continue;
1195 
1196 	 if(curexts)
1197          {
1198  	  while(curexts->extension && curexts->description)
1199 	  {
1200 	   valid_iae.push_back(*curexts);
1201            curexts++;
1202  	  }
1203          }
1204 	}
1205 
1206 /*
1207         //
1208 	// CD format extensions; refer to git.h for priorities.
1209         //
1210 	valid_iae.push_back(FileExtensionSpecStruct({ "m3u", -40, "M3U" }));
1211 	valid_iae.push_back(FileExtensionSpecStruct({ "ccd", -50, "CloneCD" }));
1212 	valid_iae.push_back(FileExtensionSpecStruct({ "cue", -60, "CUE" }));
1213 	valid_iae.push_back(FileExtensionSpecStruct({ "toc", -70, "CDRDAO TOC" }));
1214 */
1215 
1216 	MDFNFILE mfgf(vfs, path, valid_iae, _("game"));
1217 
1218 /*
1219         if(mfgf.active_vfs() != vfs && (mfgf.ext == "m3u" || mfgf.ext == "ccd" || mfgf.ext == "cue" || mfgf.ext == "toc"))
1220 	{
1221          static std::unique_ptr<VirtualFS> vfs_save;
1222 
1223          vfs_save = mfgf.steal_archive_vfs();
1224 
1225 	 if(!MDFN_GetSettingB("cd.image_memcache"))
1226           throw MDFN_Error(0, _("Setting \"cd.image_memcache\" must be set to \"1\" to allow loading a CD image from a ZIP archive."));
1227 
1228 	 return LoadCD(force_module, vfs_save.get(), mfgf.active_path().c_str());
1229 	}
1230 */
1231 	//printf("FBASE=%s,EXT=%s\n", GameFile.fbase, GameFile.ext);
1232 	//
1233 	//
1234 	//
1235 	//
1236 	MDFN_AutoIndent aind(1);
1237 	std::unique_ptr<RMD_Layout> rmd(new RMD_Layout());
1238 
1239 	MDFNGameInfo = NULL;
1240 
1241         GetFileBase(path);
1242 	//
1243 	//
1244 	//
1245 
1246 	LoadIPS(&mfgf, MDFN_MakeFName(MDFNMKF_IPS, 0, 0));
1247 
1248 	//
1249 	std::string outside_dir, outside_fbase;
1250 	vfs->get_file_path_components(path, &outside_dir, &outside_fbase);
1251 	//
1252 	GameFile gf({ mfgf.active_vfs(), mfgf.active_dir_path(), mfgf.stream(), mfgf.ext, mfgf.fbase, vfs, outside_dir, outside_fbase });
1253 
1254 	MDFNGameInfo = FindCompatibleModule(force_module, &gf);
1255 
1256         if(!MDFNGameInfo)
1257         {
1258 	 if(force_module)
1259           throw MDFN_Error(0, _("Unrecognized system \"%s\"!"), force_module);
1260 	 else
1261           throw MDFN_Error(0, _("Unrecognized file format."));
1262         }
1263 
1264 	MDFN_printf(_("Using module: %s(%s)\n"), MDFNGameInfo->shortname, MDFNGameInfo->fullname);
1265 	{
1266 	 MDFN_AutoIndent aindentgm(1);
1267 
1268 	 assert(MDFNGameInfo->soundchan != 0);
1269 
1270          MDFNGameInfo->name.clear();
1271 	 MDFNGameInfo->DesiredInput.clear();
1272          MDFNGameInfo->rotated = 0;
1273 	 MDFNGameInfo->RMD = rmd.get();
1274 
1275          {
1276 	  std::string modoverride_settings_file_path = MDFN_GetBaseDirectory() + PSS + MDFNGameInfo->shortname + ".cfg";
1277 	  MDFN_LoadSettings(modoverride_settings_file_path.c_str(), true);
1278          }
1279 	 {
1280 	  std::string pgcoverride_settings_file_path = MDFN_MakeFName(MDFNMKF_PGCONFIG, 0, NULL);
1281 	  MDFN_LoadSettings(pgcoverride_settings_file_path.c_str(), true);
1282  	 }
1283 
1284 	 MDFN_printf("\n");
1285 
1286 	 gf.stream->rewind();
1287          MDFNGameInfo->Load(&gf);
1288 	}
1289 
1290 	LoadCommonPost(vfs, path);
1291 	rmd.release();
1292  }
1293  catch(std::exception &e)
1294  {
1295   MDFN_Notify(MDFN_NOTICE_ERROR, "%s", e.what());
1296 
1297   if(MDFNGameInfo != NULL)
1298   {
1299    MDFNGameInfo->RMD = NULL;
1300    MDFNGameInfo = NULL;
1301   }
1302 
1303   if(CustomPalette != NULL)
1304   {
1305    delete[] CustomPalette;
1306    CustomPalette = NULL;
1307   }
1308   CustomPaletteNumEntries = 0;
1309 
1310   MDFN_ClearAllOverrideSettings();
1311 
1312   return(NULL);
1313  }
1314 
1315  return(MDFNGameInfo);
1316 }
1317 
BuildDynamicSetting(MDFNSetting * setting,const char * system_name,const char * name,uint32 flags,const char * description,MDFNSettingType type,const char * default_value,const char * minimum=NULL,const char * maximum=NULL,bool (* validate_func)(const char * name,const char * value)=NULL,void (* ChangeNotification)(const char * name)=NULL)1318 static void BuildDynamicSetting(MDFNSetting *setting, const char *system_name, const char *name, uint32 flags, const char *description, MDFNSettingType type,
1319         const char *default_value, const char *minimum = NULL, const char *maximum = NULL,
1320         bool (*validate_func)(const char *name, const char *value) = NULL, void (*ChangeNotification)(const char *name) = NULL)
1321 {
1322  char setting_name[256];
1323 
1324  memset(setting, 0, sizeof(MDFNSetting));
1325 
1326  trio_snprintf(setting_name, 256, "%s.%s", system_name, name);
1327 
1328  setting->name = strdup(setting_name);
1329  setting->description = description;
1330  setting->type = type;
1331  setting->flags = flags;
1332  setting->default_value = default_value;
1333  setting->minimum = minimum;
1334  setting->maximum = maximum;
1335  setting->validate_func = validate_func;
1336  setting->ChangeNotification = ChangeNotification;
1337 }
1338 
MDFNI_InitializeModules(void)1339 bool MDFNI_InitializeModules(void)
1340 {
1341  Time::Time_Init();
1342  CDUtility::CDUtility_Init();
1343  //
1344  //
1345  //
1346  static MDFNGI *InternalSystems[] =
1347  {
1348   #ifdef WANT_APPLE2_EMU
1349   &EmulatedApple2,
1350   #endif
1351 
1352   #ifdef WANT_NES_EMU
1353   &EmulatedNES,
1354   #endif
1355 
1356   #ifdef WANT_NES_NEW_EMU
1357   &EmulatedNES_New,
1358   #endif
1359 
1360   #ifdef WANT_SNES_EMU
1361   &EmulatedSNES,
1362   #endif
1363 
1364   #ifdef WANT_SNES_FAUST_EMU
1365   &EmulatedSNES_Faust,
1366   #endif
1367 
1368   #ifdef WANT_GB_EMU
1369   &EmulatedGB,
1370   #endif
1371 
1372   #ifdef WANT_GBA_EMU
1373   &EmulatedGBA,
1374   #endif
1375 
1376   #ifdef WANT_PCE_EMU
1377   &EmulatedPCE,
1378   #endif
1379 
1380   #ifdef WANT_PCE_FAST_EMU
1381   &EmulatedPCE_Fast,
1382   #endif
1383 
1384   #ifdef WANT_LYNX_EMU
1385   &EmulatedLynx,
1386   #endif
1387 
1388   #ifdef WANT_MD_EMU
1389   &EmulatedMD,
1390   #endif
1391 
1392   #ifdef WANT_PCFX_EMU
1393   &EmulatedPCFX,
1394   #endif
1395 
1396   #ifdef WANT_NGP_EMU
1397   &EmulatedNGP,
1398   #endif
1399 
1400   #ifdef WANT_PSX_EMU
1401   &EmulatedPSX,
1402   #endif
1403 
1404   #ifdef WANT_SS_EMU
1405   &EmulatedSS,
1406   #endif
1407 
1408   #ifdef WANT_SSFPLAY_EMU
1409   &EmulatedSSFPlay,
1410   #endif
1411 
1412   #ifdef WANT_VB_EMU
1413   &EmulatedVB,
1414   #endif
1415 
1416   #ifdef WANT_WSWAN_EMU
1417   &EmulatedWSwan,
1418   #endif
1419 
1420   #ifdef WANT_SMS_EMU
1421   &EmulatedSMS,
1422   &EmulatedGG,
1423   #endif
1424 
1425   &EmulatedCDPlay,
1426   &EmulatedDEMO
1427  };
1428  static_assert(MEDNAFEN_VERSION_NUMERIC >= 0x00102601, "Bad MEDNAFEN_VERSION_NUMERIC");
1429 
1430  for(unsigned int i = 0; i < sizeof(InternalSystems) / sizeof(MDFNGI *); i++)
1431   AddSystem(InternalSystems[i]);
1432 
1433  for(unsigned int i = 0; i < MDFNSystems.size(); i++)
1434   MDFNSystemsPrio.push_back(MDFNSystems[i]);
1435 
1436  MDFNSystemsPrio.sort(MDFNSystemsPrio_CompareFunc);
1437  //
1438  //
1439  //
1440  std::string modules_string;
1441  for(auto& m : MDFNSystemsPrio)
1442  {
1443   if(modules_string.size())
1444    modules_string += " ";
1445   modules_string += m->shortname;
1446  }
1447  MDFNI_printf(_("Emulation modules: %s\n"), modules_string.c_str());
1448 
1449  return(1);
1450 }
1451 
MDFNI_Initialize(const char * basedir,const std::vector<MDFNSetting> & DriverSettings)1452 int MDFNI_Initialize(const char *basedir, const std::vector<MDFNSetting> &DriverSettings)
1453 {
1454 	// FIXME static
1455 	static std::vector<MDFNSetting> dynamic_settings;
1456 
1457 	// DO NOT REMOVE/DISABLE THESE MATH AND COMPILER SANITY TESTS.  THEY EXIST FOR A REASON.
1458 	//uint64 st = Time::MonoUS();
1459 	if(!MDFN_RunMathTests())
1460 	{
1461 	 return(0);
1462 	}
1463 	//printf("tests time: %llu\n", Time::MonoUS() - st);
1464 
1465 	for(unsigned x = 0; x < 16; x++)
1466 	{
1467 	 PortDevice[x] = ~0U;
1468 	 PortData[x] = NULL;
1469 	 PortDataLen[x] = 0;
1470 	}
1471 
1472 	lzo_init();
1473 
1474 	MDFN_SetBaseDirectory(basedir);
1475 
1476 	MDFN_InitFontData();
1477 
1478 	// Generate dynamic settings
1479 	for(unsigned int i = 0; i < MDFNSystems.size(); i++)
1480 	{
1481 	 MDFNSetting setting;
1482 	 const char *sysname;
1483 
1484 	 sysname = (const char *)MDFNSystems[i]->shortname;
1485 
1486 	 if(!MDFNSystems[i]->soundchan)
1487 	  printf("0 sound channels for %s????\n", sysname);
1488 
1489 	 if(MDFNSystems[i]->soundchan == 2)
1490 	 {
1491 	  BuildDynamicSetting(&setting, sysname, "forcemono", MDFNSF_COMMON_TEMPLATE | MDFNSF_CAT_SOUND, CSD_forcemono, MDFNST_BOOL, "0");
1492 	  dynamic_settings.push_back(setting);
1493 	 }
1494 
1495 	 BuildDynamicSetting(&setting, sysname, "enable", MDFNSF_COMMON_TEMPLATE, CSD_enable, MDFNST_BOOL, "1");
1496 	 dynamic_settings.push_back(setting);
1497 
1498 	 BuildDynamicSetting(&setting, sysname, "tblur", MDFNSF_COMMON_TEMPLATE | MDFNSF_CAT_VIDEO, CSD_tblur, MDFNST_BOOL, "0");
1499          dynamic_settings.push_back(setting);
1500 
1501          BuildDynamicSetting(&setting, sysname, "tblur.accum", MDFNSF_COMMON_TEMPLATE | MDFNSF_CAT_VIDEO, CSD_tblur_accum, MDFNST_BOOL, "0");
1502          dynamic_settings.push_back(setting);
1503 
1504          BuildDynamicSetting(&setting, sysname, "tblur.accum.amount", MDFNSF_COMMON_TEMPLATE | MDFNSF_CAT_VIDEO, CSD_tblur_accum_amount, MDFNST_FLOAT, "50", "0", "100");
1505 	 dynamic_settings.push_back(setting);
1506 	}
1507 
1508 	// First merge all settable settings, then load the settings from the SETTINGS FILE OF DOOOOM
1509 	MDFN_MergeSettings(MednafenSettings);
1510         MDFN_MergeSettings(dynamic_settings);
1511 	MDFN_MergeSettings(MDFNMP_Settings);
1512 
1513 	if(DriverSettings.size())
1514  	 MDFN_MergeSettings(DriverSettings);
1515 
1516 	for(unsigned int x = 0; x < MDFNSystems.size(); x++)
1517 	{
1518 	 if(MDFNSystems[x]->Settings)
1519 	  MDFN_MergeSettings(MDFNSystems[x]->Settings);
1520 	}
1521 
1522 	MDFN_MergeSettings(RenamedSettings);
1523 
1524 	MDFN_FinalizeSettings();
1525 
1526 	#ifdef WANT_DEBUGGER
1527 	MDFNDBG_Init();
1528 	#endif
1529 
1530         return(1);
1531 }
1532 
MDFNI_LoadSettings(const char * path)1533 int MDFNI_LoadSettings(const char* path)
1534 {
1535  try
1536  {
1537   if(!MDFN_LoadSettings(path))
1538    return -1;
1539  }
1540  catch(std::exception &e)
1541  {
1542   MDFN_Notify(MDFN_NOTICE_ERROR, "%s", e.what());
1543   return 0;
1544  }
1545 
1546  return 1;
1547 }
1548 
MDFNI_SaveSettings(const char * path)1549 bool MDFNI_SaveSettings(const char* path)
1550 {
1551  try
1552  {
1553   MDFN_SaveSettings(path);
1554  }
1555  catch(std::exception &e)
1556  {
1557   MDFN_Notify(MDFN_NOTICE_ERROR, "%s", e.what());
1558   return false;
1559  }
1560  return true;
1561 }
1562 
MDFNI_Kill(void)1563 void MDFNI_Kill(void)
1564 {
1565  MDFN_KillSettings();
1566 }
1567 
1568 static double multiplier_save, volume_save;
1569 static std::vector<int16> SoundBufPristine;
1570 
ProcessAudio(EmulateSpecStruct * espec)1571 static void ProcessAudio(EmulateSpecStruct *espec)
1572 {
1573  if(espec->SoundVolume != 1)
1574   volume_save = espec->SoundVolume;
1575 
1576  if(espec->soundmultiplier != 1)
1577   multiplier_save = espec->soundmultiplier;
1578 
1579  if(espec->SoundBuf && espec->SoundBufSize)
1580  {
1581   int16 *const SoundBuf = espec->SoundBuf + espec->SoundBufSize_InternalProcessed * MDFNGameInfo->soundchan;
1582   int32 SoundBufSize = espec->SoundBufSize - espec->SoundBufSize_InternalProcessed;
1583   const int32 SoundBufMaxSize = espec->SoundBufMaxSize - espec->SoundBufSize_InternalProcessed;
1584 
1585   //
1586   // Sound reverse code goes before copying sound data to SoundBufPristine.
1587   //
1588   if(espec->NeedSoundReverse)
1589   {
1590    int16 *yaybuf = SoundBuf;
1591    int32 slen = SoundBufSize;
1592 
1593    if(MDFNGameInfo->soundchan == 1)
1594    {
1595     for(int x = 0; x < (slen / 2); x++)
1596     {
1597      int16 cha = yaybuf[slen - x - 1];
1598      yaybuf[slen - x - 1] = yaybuf[x];
1599      yaybuf[x] = cha;
1600     }
1601    }
1602    else if(MDFNGameInfo->soundchan == 2)
1603    {
1604     for(int x = 0; x < (slen * 2) / 2; x++)
1605     {
1606      int16 cha = yaybuf[slen * 2 - (x&~1) - ((x&1) ^ 1) - 1];
1607      yaybuf[slen * 2 - (x&~1) - ((x&1) ^ 1) - 1] = yaybuf[x];
1608      yaybuf[x] = cha;
1609     }
1610    }
1611   }
1612 
1613 
1614   if(qtrecorder && (volume_save != 1 || multiplier_save != 1))
1615   {
1616    int32 orig_size = SoundBufPristine.size();
1617 
1618    SoundBufPristine.resize(orig_size + SoundBufSize * MDFNGameInfo->soundchan);
1619    for(int i = 0; i < SoundBufSize * MDFNGameInfo->soundchan; i++)
1620     SoundBufPristine[orig_size + i] = SoundBuf[i];
1621   }
1622 
1623   try
1624   {
1625    if(wavrecorder)
1626     wavrecorder->WriteSound(SoundBuf, SoundBufSize);
1627   }
1628   catch(std::exception &e)
1629   {
1630    MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
1631    delete wavrecorder;
1632    wavrecorder = NULL;
1633   }
1634 
1635   if(multiplier_save != LastSoundMultiplier)
1636   {
1637    ff_resampler.time_ratio(multiplier_save, 0.9965);
1638    LastSoundMultiplier = multiplier_save;
1639   }
1640 
1641   if(multiplier_save != 1)
1642   {
1643    if(FFDiscard)
1644    {
1645     if(SoundBufSize >= multiplier_save)
1646      SoundBufSize /= multiplier_save;
1647    }
1648    else
1649    {
1650     if(MDFNGameInfo->soundchan == 2)
1651     {
1652      assert(ff_resampler.max_write() >= SoundBufSize * 2);
1653 
1654      for(int i = 0; i < SoundBufSize * 2; i++)
1655       ff_resampler.buffer()[i] = SoundBuf[i];
1656     }
1657     else
1658     {
1659      assert(ff_resampler.max_write() >= SoundBufSize * 2);
1660 
1661      for(int i = 0; i < SoundBufSize; i++)
1662      {
1663       ff_resampler.buffer()[i * 2] = SoundBuf[i];
1664       ff_resampler.buffer()[i * 2 + 1] = 0;
1665      }
1666     }
1667     ff_resampler.write(SoundBufSize * 2);
1668 
1669     int avail = ff_resampler.avail();
1670     int real_read = std::min((int)(SoundBufMaxSize * MDFNGameInfo->soundchan), avail);
1671 
1672     if(MDFNGameInfo->soundchan == 2)
1673      SoundBufSize = ff_resampler.read(SoundBuf, real_read ) >> 1;
1674     else
1675      SoundBufSize = ff_resampler.read_mono_hack(SoundBuf, real_read );
1676 
1677     avail -= real_read;
1678 
1679     if(avail > 0)
1680     {
1681      printf("ff_resampler.avail() > espec->SoundBufMaxSize * MDFNGameInfo->soundchan - %d\n", avail);
1682      ff_resampler.clear();
1683     }
1684    }
1685   }
1686 
1687   if(volume_save != 1)
1688   {
1689    if(volume_save < 1)
1690    {
1691     int volume = (int)(16384 * volume_save);
1692 
1693     for(int i = 0; i < SoundBufSize * MDFNGameInfo->soundchan; i++)
1694      SoundBuf[i] = (SoundBuf[i] * volume) >> 14;
1695    }
1696    else
1697    {
1698     int volume = (int)(256 * volume_save);
1699 
1700     for(int i = 0; i < SoundBufSize * MDFNGameInfo->soundchan; i++)
1701     {
1702      int temp = ((SoundBuf[i] * volume) >> 8) + 32768;
1703 
1704      temp = clamp_to_u16(temp);
1705 
1706      SoundBuf[i] = temp - 32768;
1707     }
1708    }
1709   }
1710 
1711   // TODO: Optimize this.
1712   if(MDFNGameInfo->soundchan == 2 && MDFN_GetSettingB(std::string(MDFNGameInfo->shortname) + ".forcemono"))
1713   {
1714    for(int i = 0; i < SoundBufSize * MDFNGameInfo->soundchan; i += 2)
1715    {
1716     // We should use division instead of arithmetic right shift for correctness(rounding towards 0 instead of negative infinitininintinity), but I like speed.
1717     int32 mixed = (SoundBuf[i + 0] + SoundBuf[i + 1]) >> 1;
1718 
1719     SoundBuf[i + 0] =
1720     SoundBuf[i + 1] = mixed;
1721    }
1722   }
1723 
1724   espec->SoundBufSize = espec->SoundBufSize_InternalProcessed + SoundBufSize;
1725  } // end to:  if(espec->SoundBuf && espec->SoundBufSize)
1726 }
1727 
MDFN_MidSync(EmulateSpecStruct * espec,const unsigned flags)1728 void MDFN_MidSync(EmulateSpecStruct *espec, const unsigned flags)
1729 {
1730  ProcessAudio(espec);
1731  espec->SoundBufSize_InternalProcessed = espec->SoundBufSize;
1732  espec->MasterCycles_InternalProcessed = espec->MasterCycles;
1733  //
1734  // We could act as if flags = 0 during netplay, and call MDFND_MidSync(), but
1735  // we'd need to fix the kludgy driver-side code that handles sound buffer underruns.
1736  //
1737  if(!MDFNnetplay)
1738  {
1739   MDFND_MidSync(espec, flags);
1740   //
1741   if((flags & MIDSYNC_FLAG_UPDATE_INPUT) && MDFNGameInfo->TransformInput)	// Call after MDFND_MidSync, and before MDFNMOV_ProcessInput
1742    MDFNGameInfo->TransformInput();
1743  }
1744 
1745  if(flags & MIDSYNC_FLAG_UPDATE_INPUT)
1746  {
1747   // Call even during netplay, so input-recording movies recorded during netplay will play back properly.
1748   MDFNMOV_ProcessInput(PortData, PortDataLen, MDFNGameInfo->PortInfo.size());
1749  }
1750 }
1751 
MDFN_MidLineUpdate(EmulateSpecStruct * espec,int y)1752 void MDFN_MidLineUpdate(EmulateSpecStruct *espec, int y)
1753 {
1754  //MDFND_MidLineUpdate(espec, y);
1755 }
1756 
MDFNI_Emulate(EmulateSpecStruct * espec)1757 void MDFNI_Emulate(EmulateSpecStruct *espec)
1758 {
1759 #if 0
1760  {
1761   static unsigned osc = 0;
1762   MDFN_PixelFormat nf;
1763 
1764   osc = (osc + 1) % 3;
1765 
1766   nf.bpp = 16;
1767   nf.colorspace = MDFN_COLORSPACE_RGB;
1768   if(osc == 0)
1769   {
1770    nf.Rshift = 10;
1771    nf.Gshift = 5;
1772    nf.Bshift = 0;
1773    nf.Rprec = 5;
1774    nf.Gprec = 5;
1775    nf.Bprec = 5;
1776   }
1777   else if(osc == 1)
1778   {
1779    nf.Rshift = 11;
1780    nf.Gshift = 5;
1781    nf.Bshift = 0;
1782    nf.Rprec = 5;
1783    nf.Gprec = 6;
1784    nf.Bprec = 5;
1785   }
1786   nf.Ashift = 16;
1787   nf.Aprec = 8;
1788 
1789   if(osc == 2)
1790    nf = espec->surface->format;
1791   //
1792   espec->surface->SetFormat(nf, false);
1793   //
1794   MDFN_Notify(MDFN_NOTICE_STATUS, "%2d %d\n", nf.bpp, nf.Gprec);
1795  }
1796 #endif
1797  //
1798 #if 0
1799  {
1800   static const double rates[8] = { 22050, 22222, 44100, 45454, 48000, 64000, 96000, 192000 };
1801   espec->SoundRate = rates[(rand() >> 14) & 0x7];
1802  }
1803 #endif
1804  //
1805  multiplier_save = 1;
1806  volume_save = 1;
1807 
1808  if(!espec->CustomPalette)
1809  {
1810   espec->CustomPalette = CustomPalette;
1811   espec->CustomPaletteNumEntries = CustomPaletteNumEntries;
1812  }
1813 
1814  // Initialize some espec member data to zero, to catch some types of bugs.
1815  espec->DisplayRect.x = 0;
1816  espec->DisplayRect.w = 0;
1817  espec->DisplayRect.y = 0;
1818  espec->DisplayRect.h = 0;
1819 
1820  assert((bool)(espec->SoundBuf != NULL) == (bool)espec->SoundRate && (bool)espec->SoundRate == (bool)espec->SoundBufMaxSize);
1821 
1822  espec->SoundBufSize = 0;
1823 
1824  if(last_pixel_format != espec->surface->format)
1825  {
1826   espec->VideoFormatChanged = true;
1827 
1828   last_pixel_format = espec->surface->format;
1829  }
1830 
1831  if(fabs(espec->SoundRate - last_sound_rate) >= 0.5)
1832  {
1833   //puts("Rate Change");
1834   espec->SoundFormatChanged = true;
1835   last_sound_rate = espec->SoundRate;
1836 
1837   ff_resampler.buffer_size((espec->SoundRate / 2) * 2);
1838  }
1839 
1840  // We want to record movies without any dropped video frames and without fast-forwarding sound distortion and without custom volume.
1841  // The same goes for WAV recording(sans the dropped video frames bit :b).
1842  if(qtrecorder || wavrecorder)
1843  {
1844   multiplier_save = espec->soundmultiplier;
1845   espec->soundmultiplier = 1;
1846 
1847   volume_save = espec->SoundVolume;
1848   espec->SoundVolume = 1;
1849  }
1850 
1851  if(MDFNGameInfo->TransformInput)
1852   MDFNGameInfo->TransformInput();
1853 
1854  Netplay_Update(PortDevice, PortData, PortDataLen);
1855 
1856  MDFNMOV_ProcessInput(PortData, PortDataLen, MDFNGameInfo->PortInfo.size());
1857 
1858  if(qtrecorder)
1859   espec->skip = 0;
1860 
1861  if(TBlur_IsOn())
1862   espec->skip = 0;
1863 
1864  if(espec->NeedRewind)
1865  {
1866   if(MDFNnetplay)
1867   {
1868    espec->NeedRewind = false;
1869    MDFN_Notify(MDFN_NOTICE_STATUS, _("Can't rewind during netplay."));
1870   }
1871  }
1872 
1873  // Don't even save states with state rewinding if netplay is enabled, it will degrade netplay performance, and can cause
1874  // desynchs with some emulation(IE SNES based on bsnes).
1875 
1876  if(MDFNnetplay)
1877   espec->NeedSoundReverse = false;
1878  else
1879   espec->NeedSoundReverse = MDFNSRW_Frame(espec->NeedRewind);
1880 
1881  MDFNGameInfo->Emulate(espec);
1882 
1883  if(MDFNnetplay)
1884   Netplay_PostProcess(PortDevice, PortData, PortDataLen);
1885 
1886  //
1887  // Sanity checks
1888  //
1889  if(!espec->skip || espec->InterlaceOn)
1890  {
1891   if(espec->DisplayRect.h <= 0)
1892   {
1893    fprintf(stderr, "[BUG] espec->DisplayRect.h <= 0: %d\n", espec->DisplayRect.h);
1894   }
1895 
1896   if(espec->DisplayRect.y < 0)
1897   {
1898    fprintf(stderr, "[BUG] espec->DisplayRect.y < 0: %d\n", espec->DisplayRect.y);
1899   }
1900 
1901   if(espec->LineWidths[0] == ~0)
1902   {
1903    if(espec->DisplayRect.w <= 0)
1904    {
1905     fprintf(stderr, "[BUG] espec->DisplayRect.w <= 0: %d\n", espec->DisplayRect.w);
1906    }
1907   }
1908   else
1909   {
1910    for(int32 y = 0; y < espec->DisplayRect.h; y++)
1911    {
1912     if((y ^ espec->InterlaceField) & espec->InterlaceOn)
1913      continue;
1914 
1915     const int32& lw = espec->LineWidths[espec->DisplayRect.y + y];
1916 
1917     if(lw <= 0)
1918      fprintf(stderr, "[BUG] espec->LineWidths[%d] <= 0: %d\n", espec->DisplayRect.y + y, lw);
1919 #ifndef MDFN_ENABLE_DEV_BUILD
1920     break;	// Only check one line unless this is a dev build, for (very minor) performance reasons.
1921 #endif
1922    }
1923   }
1924  }
1925 
1926  if(!espec->MasterCycles)
1927  {
1928   fprintf(stderr, "[BUG] espec->MasterCycles == 0\n");
1929  }
1930 
1931  if(espec->MasterCycles < espec->MasterCycles_InternalProcessed)
1932  {
1933   fprintf(stderr, "[BUG] espec->MasterCycles < espec->MasterCycles_InternalProcessed\n");
1934  }
1935 
1936  //
1937  //
1938  //
1939 
1940  if(espec->InterlaceOn)
1941  {
1942   if(!PrevInterlaced)
1943    deint->ClearState();
1944 
1945   deint->Process(espec->surface, espec->DisplayRect, espec->LineWidths, espec->InterlaceField);
1946   PrevInterlaced = true;
1947  }
1948  else
1949   PrevInterlaced = false;
1950 
1951  ProcessAudio(espec);
1952 
1953  if(qtrecorder)
1954  {
1955   int16 *sb_backup = espec->SoundBuf;
1956   int32 sbs_backup = espec->SoundBufSize;
1957 
1958   if(SoundBufPristine.size())
1959   {
1960    espec->SoundBuf = &SoundBufPristine[0];
1961    espec->SoundBufSize = SoundBufPristine.size() / MDFNGameInfo->soundchan;
1962   }
1963 
1964   try
1965   {
1966    qtrecorder->WriteFrame(espec->surface, espec->DisplayRect, espec->LineWidths, espec->SoundBuf, espec->SoundBufSize, espec->MasterCycles);
1967   }
1968   catch(std::exception &e)
1969   {
1970    MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
1971    delete qtrecorder;
1972    qtrecorder = NULL;
1973   }
1974 
1975   SoundBufPristine.clear();
1976 
1977   espec->SoundBuf = sb_backup;
1978   espec->SoundBufSize = sbs_backup;
1979  }
1980 
1981  if(TBlur_IsOn())
1982   TBlur_Run(espec);
1983 }
1984 
StateAction_RINP(StateMem * sm,const unsigned load,const bool data_only)1985 static void StateAction_RINP(StateMem* sm, const unsigned load, const bool data_only)
1986 {
1987  char namebuf[16][2 + 8 + 1];
1988 
1989  if(!data_only)
1990  {
1991   for(unsigned x = 0; x < 16; x++)
1992   {
1993    trio_snprintf(namebuf[x], sizeof(namebuf[x]), "%02x%08x", x, PortDevice[x]);
1994   }
1995  }
1996 
1997  #define SFRIH(x) SFPTR8N(PortData[x], PortDataLen[x], namebuf[x])
1998  SFORMAT StateRegs[] =
1999  {
2000   SFRIH(0), SFRIH(1), SFRIH(2), SFRIH(3), SFRIH(4), SFRIH(5), SFRIH(6), SFRIH(7),
2001   SFRIH(8), SFRIH(9), SFRIH(10), SFRIH(11), SFRIH(12), SFRIH(13), SFRIH(14), SFRIH(15),
2002   SFEND
2003  };
2004  #undef SFRIH
2005 
2006  MDFNSS_StateAction(sm, load, data_only, StateRegs, "MDFNRINP", true);
2007 }
2008 
MDFN_StateAction(StateMem * sm,const unsigned load,const bool data_only)2009 void MDFN_StateAction(StateMem *sm, const unsigned load, const bool data_only)
2010 {
2011  if(DMStatus.size())
2012  {
2013   std::copy(DMStatus.begin(), DMStatus.end(), DMStatusSaveStateTemp.begin());
2014   //
2015   //
2016   SFORMAT StateRegs[] =
2017   {
2018    SFVARN(DMStatusSaveStateTemp.data()->state_idx, DMStatusSaveStateTemp.size(), sizeof(*DMStatusSaveStateTemp.data()), DMStatusSaveStateTemp.data(), "state_idx"),
2019    SFVARN(DMStatusSaveStateTemp.data()->media_idx, DMStatusSaveStateTemp.size(), sizeof(*DMStatusSaveStateTemp.data()), DMStatusSaveStateTemp.data(), "media_idx"),
2020    SFVARN(DMStatusSaveStateTemp.data()->orientation_idx, DMStatusSaveStateTemp.size(), sizeof(*DMStatusSaveStateTemp.data()), DMStatusSaveStateTemp.data(), "orientation_idx"),
2021 
2022    SFEND
2023   };
2024 
2025   if(MDFNSS_StateAction(sm, load, data_only, StateRegs, "MDFNDRIVE_00000000", true) && load)
2026   {
2027    // Be sure to set media before loading the emulation module state, as setting media
2028    // may affect what state is saved in the emulation module code, and setting media
2029    // can also have side effects(that will be undone by the state load).
2030 
2031    if(ValidateDMS(DMStatusSaveStateTemp))
2032    {
2033     //
2034     // Internally(to the emulation core) set all drives to a no-media-present state so the core won't freak
2035     // out by the temporary insertion of the same medium into different drives simultaneously.
2036     //
2037     for(uint32 drive_idx = 0; drive_idx < DMSNoMedia.size(); drive_idx++)
2038      MDFNGameInfo->SetMedia(drive_idx, DMSNoMedia[drive_idx], 0, 0);
2039     //
2040     //
2041     for(uint32 drive_idx = 0; drive_idx < DMStatusSaveStateTemp.size(); drive_idx++)
2042     {
2043      auto const& dmssste = DMStatusSaveStateTemp[drive_idx];
2044      const bool change_notif = dmssste.state_idx != DMStatus[drive_idx].state_idx ||
2045 			       dmssste.media_idx != DMStatus[drive_idx].media_idx ||
2046                                dmssste.orientation_idx != DMStatus[drive_idx].orientation_idx;
2047      //
2048      DMStatus[drive_idx] = dmssste;
2049      //
2050      //
2051      MDFNGameInfo->SetMedia(drive_idx, dmssste.state_idx, dmssste.media_idx, dmssste.orientation_idx);
2052 
2053      if(change_notif)
2054       MDFND_MediaSetNotification(drive_idx, dmssste.state_idx, dmssste.media_idx, dmssste.orientation_idx);
2055     }
2056    }
2057   }
2058  }
2059  //
2060  //
2061  //
2062  StateAction_RINP(sm, load, data_only);
2063 
2064  if(data_only)
2065   MDFNMOV_StateAction(sm, load);
2066 
2067  MDFNGameInfo->StateAction(sm, load, data_only);
2068 }
2069 
2070 static int curindent = 0;
2071 
MDFN_indent(int indent)2072 void MDFN_indent(int indent)
2073 {
2074  curindent += indent;
2075  if(curindent < 0)
2076  {
2077   fprintf(stderr, "MDFN_indent negative!\n");
2078   curindent = 0;
2079  }
2080 }
2081 
2082 static uint8 lastchar = 0;
MDFN_printf(const char * format,...)2083 void MDFN_printf(const char *format, ...) noexcept
2084 {
2085  char *format_temp;
2086  char *temp;
2087  unsigned int x, newlen;
2088 
2089  va_list ap;
2090  va_start(ap,format);
2091 
2092 
2093  // First, determine how large our format_temp buffer needs to be.
2094  uint8 lastchar_backup = lastchar; // Save lastchar!
2095  for(newlen=x=0;x<strlen(format);x++)
2096  {
2097   if(lastchar == '\n' && format[x] != '\n')
2098   {
2099    int y;
2100    for(y=0;y<curindent;y++)
2101     newlen++;
2102   }
2103   newlen++;
2104   lastchar = format[x];
2105  }
2106 
2107  format_temp = (char *)malloc(newlen + 1); // Length + NULL character, duh
2108 
2109  // Now, construct our format_temp string
2110  lastchar = lastchar_backup; // Restore lastchar
2111  for(newlen=x=0;x<strlen(format);x++)
2112  {
2113   if(lastchar == '\n' && format[x] != '\n')
2114   {
2115    int y;
2116    for(y=0;y<curindent;y++)
2117     format_temp[newlen++] = ' ';
2118   }
2119   format_temp[newlen++] = format[x];
2120   lastchar = format[x];
2121  }
2122 
2123  format_temp[newlen] = 0;
2124 
2125  temp = trio_vaprintf(format_temp, ap);
2126  free(format_temp);
2127 
2128  MDFND_OutputInfo(temp);
2129  free(temp);
2130 
2131  va_end(ap);
2132 }
2133 
MDFN_Notify(MDFN_NoticeType t,const char * format,...)2134 void MDFN_Notify(MDFN_NoticeType t, const char* format, ...) noexcept
2135 {
2136  char* s;
2137  va_list ap;
2138 
2139  va_start(ap, format);
2140 
2141  s = trio_vaprintf(format, ap);
2142  if(!s)
2143  {
2144   MDFND_OutputNotice(t, "Error allocating memory for the message!");
2145  }
2146  else
2147  {
2148   MDFND_OutputNotice(t, s);
2149   free(s);
2150  }
2151 
2152  va_end(ap);
2153 }
2154 
MDFN_DebugPrintReal(const char * file,const int line,const char * format,...)2155 void MDFN_DebugPrintReal(const char *file, const int line, const char *format, ...)
2156 {
2157  char *temp;
2158 
2159  va_list ap;
2160 
2161  va_start(ap, format);
2162 
2163  temp = trio_vaprintf(format, ap);
2164  printf("%s:%d  %s\n", file, line, temp);
2165  free(temp);
2166 
2167  va_end(ap);
2168 }
2169 
MDFN_DoSimpleCommand(int cmd)2170 void MDFN_DoSimpleCommand(int cmd)
2171 {
2172  MDFNGameInfo->DoSimpleCommand(cmd);
2173 }
2174 
MDFN_QSimpleCommand(int cmd)2175 void MDFN_QSimpleCommand(int cmd)
2176 {
2177  if(MDFNnetplay)
2178   NetplaySendCommand(cmd, 0);
2179  else
2180  {
2181   if(!MDFNMOV_IsPlaying())
2182   {
2183    MDFN_DoSimpleCommand(cmd);
2184    MDFNMOV_AddCommand(cmd);
2185   }
2186  }
2187 }
2188 
MDFNI_Power(void)2189 void MDFNI_Power(void)
2190 {
2191  assert(MDFNGameInfo);
2192 
2193  MDFN_QSimpleCommand(MDFN_MSC_POWER);
2194 }
2195 
MDFNI_Reset(void)2196 void MDFNI_Reset(void)
2197 {
2198  assert(MDFNGameInfo);
2199 
2200  MDFN_QSimpleCommand(MDFN_MSC_RESET);
2201 }
2202 
2203 // Arcade-support functions
2204 
2205 
2206 //
2207 // Quick and dirty kludge until we can (re-)abstract DIP switch handling properly.
2208 //
2209 }
2210 
2211 #ifdef WANT_NES_EMU
2212 namespace MDFN_IEN_NES
2213 {
2214 void MDFN_VSUniToggleDIPView(void);
2215 }
2216 #endif
2217 
2218 namespace Mednafen
2219 {
MDFNI_ToggleDIPView(void)2220 void MDFNI_ToggleDIPView(void)
2221 {
2222 #ifdef WANT_NES_EMU
2223  if(MDFNGameInfo == &EmulatedNES)
2224  {
2225   MDFN_IEN_NES::MDFN_VSUniToggleDIPView();
2226  }
2227 #endif
2228 }
2229 
MDFNI_ToggleDIP(int which)2230 void MDFNI_ToggleDIP(int which)
2231 {
2232  assert(MDFNGameInfo);
2233  assert(which >= 0);
2234 
2235  MDFN_QSimpleCommand(MDFN_MSC_TOGGLE_DIP0 + which);
2236 }
2237 
MDFNI_InsertCoin(void)2238 void MDFNI_InsertCoin(void)
2239 {
2240  assert(MDFNGameInfo);
2241 
2242  MDFN_QSimpleCommand(MDFN_MSC_INSERT_COIN);
2243 }
2244 
2245 //
2246 // Disk/Disc-based system support functions
2247 //
2248 
ValidateDMS(const std::vector<DriveMediaStatus> & dms)2249 static bool ValidateDMS(const std::vector<DriveMediaStatus>& dms)
2250 {
2251  for(uint32 drive_idx = 0; drive_idx < dms.size(); drive_idx++)
2252  {
2253   const RMD_Drive& drive = MDFNGameInfo->RMD->Drives[drive_idx];
2254   const uint32 state_idx = dms[drive_idx].state_idx;
2255   const uint32 media_idx = dms[drive_idx].media_idx;
2256   const uint32 orientation_idx = dms[drive_idx].orientation_idx;
2257 
2258   // Ensure state is valid
2259   if(state_idx >= drive.PossibleStates.size())
2260   {
2261    MDFN_Notify(MDFN_NOTICE_WARNING, _("Denied attempt to put drive into non-existent state(drive_idx=0x%08x, state_idx=0x%08x, media_idx=0x%08x, orientation_idx=0x%08x)."), drive_idx, state_idx, media_idx, orientation_idx);
2262    return false;
2263   }
2264   const RMD_State& state = drive.PossibleStates[state_idx];
2265 
2266   // Ensure media(and orientation) is valid
2267   if(state.MediaPresent)
2268   {
2269    if(media_idx >= MDFNGameInfo->RMD->Media.size())
2270    {
2271     MDFN_Notify(MDFN_NOTICE_WARNING, _("Denied attempt to put non-existent medium into drive(drive_idx=0x%08x, state_idx=0x%08x, media_idx=0x%08x, orientation_idx=0x%08x)."), drive_idx, state_idx, media_idx, orientation_idx);
2272     return false;
2273    }
2274 
2275    if(orientation_idx && orientation_idx >= MDFNGameInfo->RMD->Media[media_idx].Orientations.size())
2276    {
2277     MDFN_Notify(MDFN_NOTICE_WARNING, _("Denied attempt to put medium with non-existent orientation into drive(drive_idx=0x%08x, state_idx=0x%08x, media_idx=0x%08x, orientation_idx=0x%08x)."), drive_idx, state_idx, media_idx, orientation_idx);
2278     return false;
2279    }
2280    //
2281    //
2282    {
2283     const uint32 media_type = MDFNGameInfo->RMD->Media[media_idx].MediaType;
2284     bool cm_ok = false;
2285 
2286     for(auto const& cme : MDFNGameInfo->RMD->Drives[drive_idx].CompatibleMedia)
2287     {
2288      if(cme == media_type)
2289      {
2290       cm_ok = true;
2291       break;
2292      }
2293     }
2294 
2295     if(!cm_ok)
2296     {
2297      MDFN_Notify(MDFN_NOTICE_WARNING, _("Denied attempt to put incompatible medium into drive(drive_idx=0x%08x, state_idx=0x%08x, media_idx=0x%08x, orientation_idx=0x%08x)."), drive_idx, state_idx, media_idx, orientation_idx);
2298      return false;
2299     }
2300    }
2301    //
2302    //
2303    for(uint32 check_drive_idx = 0; check_drive_idx < MDFNGameInfo->RMD->Drives.size(); check_drive_idx++)
2304    {
2305     if(check_drive_idx == drive_idx)
2306      continue;
2307     //
2308     const RMD_Drive& check_drive = MDFNGameInfo->RMD->Drives[check_drive_idx];
2309     const RMD_State& check_state = check_drive.PossibleStates[dms[check_drive_idx].state_idx];
2310     const uint32 check_media_idx = dms[check_drive_idx].media_idx;
2311 
2312     if(check_state.MediaPresent && media_idx == check_media_idx)
2313     {
2314      MDFN_Notify(MDFN_NOTICE_WARNING, _("Denied attempt to put in-use medium into another drive(drive_idx=0x%08x, state_idx=0x%08x, media_idx=0x%08x, orientation_idx=0x%08x - check_drive_idx=0x%08x)."), drive_idx, state_idx, media_idx, orientation_idx, check_drive_idx);
2315      return false;
2316     }
2317    }
2318   }
2319  }
2320 
2321  return true;
2322 }
2323 
2324 /* Normal chain:
2325 
2326    MDFNI_SetMedia()
2327       NetplaySendCommand()
2328       MDFNMOVAddCommand()
2329       MDFN_UntrustedSetMedia()
2330          ValidateDMS()
2331          MDFNGameInfo->SetMedia()
2332 	 MDFND_MediaSetNotification()
2333 
2334    MDFN_StateAction()
2335       ValidateDMS()
2336       MDFNGameInfo->SetMedia()
2337       MDFND_MediaSetNotification()
2338 
2339   MDFN_UntrustedSetMedia() may be called from the movie and netplay code after receiving command data.
2340 */
MDFN_UntrustedSetMedia(uint32 drive_idx,uint32 state_idx,uint32 media_idx,uint32 orientation_idx)2341 bool MDFN_UntrustedSetMedia(uint32 drive_idx, uint32 state_idx, uint32 media_idx, uint32 orientation_idx)
2342 {
2343  //printf("MDFN_UntrustedSetMedia: %d %d %d %d\n", drive_idx, state_idx, media_idx, orientation_idx);
2344 
2345  if(!MDFNGameInfo->SetMedia)
2346   return false;
2347 
2348  // Ensure drive is valid.
2349  if(drive_idx >= MDFNGameInfo->RMD->Drives.size())
2350  {
2351   MDFN_Notify(MDFN_NOTICE_WARNING, _("Rejected attempt to insert medium into non-existent drive(drive_idx=0x%08x, state_idx=0x%08x, media_idx=0x%08x, orientation_idx=0x%08x)."), drive_idx, state_idx, media_idx, orientation_idx);
2352   return false;
2353  }
2354  //
2355  //
2356  //
2357  assert(drive_idx < DMStatus.size());
2358  //
2359  std::vector<DriveMediaStatus> new_dms = DMStatus;
2360  DriveMediaStatus& dmse = new_dms[drive_idx];
2361 
2362  dmse.state_idx = state_idx;
2363  dmse.media_idx = media_idx;
2364  dmse.orientation_idx = orientation_idx;
2365 
2366  if(!ValidateDMS(new_dms))
2367   return false;
2368  //
2369  //
2370  //
2371  DMStatus[drive_idx] = dmse;
2372  MDFNGameInfo->SetMedia(drive_idx, dmse.state_idx, dmse.media_idx, dmse.orientation_idx);
2373  MDFND_MediaSetNotification(drive_idx, dmse.state_idx, dmse.media_idx, dmse.orientation_idx);
2374 
2375  return true;
2376 }
2377 
MDFNI_SetMedia(uint32 drive_idx,uint32 state_idx,uint32 media_idx,uint32 orientation_idx)2378 bool MDFNI_SetMedia(uint32 drive_idx, uint32 state_idx, uint32 media_idx, uint32 orientation_idx)
2379 {
2380  assert(MDFNGameInfo);
2381 
2382  if(MDFNnetplay || MDFNMOV_IsRecording())
2383  {
2384   uint8 buf[4 * 4];
2385 
2386   MDFN_en32lsb(&buf[0x0], drive_idx);
2387   MDFN_en32lsb(&buf[0x4], state_idx);
2388   MDFN_en32lsb(&buf[0x8], media_idx);
2389   MDFN_en32lsb(&buf[0xC], orientation_idx);
2390 
2391   if(MDFNnetplay)
2392    NetplaySendCommand(MDFNNPCMD_SET_MEDIA, sizeof(buf), buf);
2393 
2394   if(MDFNMOV_IsRecording())
2395    MDFNMOV_AddCommand(MDFNNPCMD_SET_MEDIA, sizeof(buf), buf);
2396  }
2397 
2398  if(!MDFNnetplay && !MDFNMOV_IsPlaying())
2399   return MDFN_UntrustedSetMedia(drive_idx, state_idx, media_idx, orientation_idx);
2400  else
2401   return false;
2402 }
2403 
MDFNI_SetLayerEnableMask(uint64 mask)2404 void MDFNI_SetLayerEnableMask(uint64 mask)
2405 {
2406  if(MDFNGameInfo && MDFNGameInfo->SetLayerEnableMask)
2407  {
2408   MDFNGameInfo->SetLayerEnableMask(mask);
2409  }
2410 }
2411 
MDFNI_SetInput(const uint32 port,const uint32 type)2412 uint8* MDFNI_SetInput(const uint32 port, const uint32 type)
2413 {
2414  if(MDFNGameInfo)
2415  {
2416   assert(port < 16 && port < MDFNGameInfo->PortInfo.size());
2417   assert(type < MDFNGameInfo->PortInfo[port].DeviceInfo.size());
2418 
2419   if(type != PortDevice[port])
2420   {
2421    size_t tmp_len = MDFNGameInfo->PortInfo[port].DeviceInfo[type].IDII.InputByteSize;
2422    uint8* tmp_ptr;
2423 
2424    tmp_ptr = (uint8*)malloc(tmp_len ? tmp_len : 1);	// Ensure PortData[port], for valid port, is never NULL, for easier handling in regards to stuff like memcpy()
2425 							// (which may have "undefined" behavior when a pointer argument is NULL even when length == 0).
2426    memset(tmp_ptr, 0, tmp_len);
2427 
2428    if(PortData[port] != NULL)
2429     free(PortData[port]);
2430 
2431    PortData[port] = tmp_ptr;
2432    PortDataLen[port] = tmp_len;
2433    PortDevice[port] = type;
2434 
2435    MDFNGameInfo->SetInput(port, MDFNGameInfo->PortInfo[port].DeviceInfo[type].ShortName, PortData[port]);
2436    //MDFND_InputSetNotification(port, type, PortData[port]);
2437   }
2438 
2439   return PortData[port];
2440  }
2441  else
2442   return(NULL);
2443 }
2444 
2445 }
2446