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/mednafen.h>
19 #include <mednafen/hash/md5.h>
20 #include <mednafen/general.h>
21 #include <mednafen/mempatcher.h>
22 #include <mednafen/SNSFLoader.h>
23 #include <mednafen/player.h>
24 #include <mednafen/FileStream.h>
25 #include <mednafen/resampler/resampler.h>
26 #include <mednafen/cheat_formats/snes.h>
27 
28 #include "src/base.hpp"
29 
30 using namespace Mednafen;
31 
32 extern MDFNGI EmulatedSNES;
33 
34 static void Cleanup(void);
35 
36 static SpeexResamplerState *resampler = NULL;
37 static int32 ResampInPos;
38 static int16 ResampInBuffer[2048][2];
39 static bool PrevFrameInterlaced;
40 static int PrevLine;
41 
42 static bSNES_v059::Interface Interface;
43 static SNSFLoader *snsf_loader = NULL;
44 
45 static bool InProperEmu;
46 static bool SoundOn;
47 static double SoundLastRate = 0;
48 
49 static int32 CycleCounter;
50 static MDFN_Surface *tsurf = NULL;
51 static int32 *tlw = NULL;
52 static MDFN_Rect *tdr = NULL;
53 static EmulateSpecStruct *es = NULL;
54 static bool EnableHBlend;
55 
56 static int InputType[2];
57 static uint8 *InputPtr[8] = { NULL };
58 static uint16 PadLatch[8];
59 static bool MultitapEnabled[2];
60 static bool HasPolledThisFrame;
61 
62 static int16 MouseXLatch[2];
63 static int16 MouseYLatch[2];
64 static uint8 MouseBLatch[2];
65 
66 static int16 ScopeXLatch[2];
67 static int16 ScopeYLatch[2];
68 static uint8 ScopeBLatch[2];
69 static uint8 ScopeOSCounter[2];
70 static const uint8 ScopeOSCounter_StartVal = 10;
71 static const uint8 ScopeOSCounter_TriggerStartThresh = 6;
72 static const uint8 ScopeOSCounter_TriggerEndThresh = 2;
73 
74 static std::vector<uint32> ColorMap;	// [32768]
75 
BuildColorMap(MDFN_PixelFormat & format,uint8 * CustomColorMap)76 static void BuildColorMap(MDFN_PixelFormat &format, uint8* CustomColorMap)
77 {
78  for(int x = 0; x < 32768; x++)
79  {
80   int r, g, b;
81 
82   r = (x & (0x1F <<  0)) << 3;
83   g = (x & (0x1F <<  5)) >> (5 - 3);
84   b = (x & (0x1F << 10)) >> (5 * 2 - 3);
85 
86   //r = ((((x >> 0) & 0x1F) * 255 + 15) / 31);
87   //g = ((((x >> 5) & 0x1F) * 255 + 15) / 31);
88   //b = ((((x >> 10) & 0x1F) * 255 + 15) / 31);
89 
90   if(CustomColorMap)
91   {
92    r = CustomColorMap[x * 3 + 0];
93    g = CustomColorMap[x * 3 + 1];
94    b = CustomColorMap[x * 3 + 2];
95   }
96 
97   ColorMap[x] = format.MakeColor(r, g, b);
98  }
99 }
100 
101 static const CustomPalette_Spec CPInfo[] =
102 {
103  { gettext_noop("SNES 15-bit BGR"), NULL, { 32768, 0 } },
104 
105  { NULL, NULL }
106 };
107 
BlankMissingLines(int ystart,int ybound,const bool interlaced,const bool field)108 static void BlankMissingLines(int ystart, int ybound, const bool interlaced, const bool field)
109 {
110  for(int y = ystart; y < ybound; y++)
111  {
112   //printf("Blanked: %d\n", y);
113   uint32 *dest_line = tsurf->pixels + (field * tsurf->pitch32) + (y * tsurf->pitch32);
114   dest_line[0] = tsurf->MakeColor(0, 0/*rand() & 0xFF*/, 0);
115   tlw[(y << interlaced) + field] = 1;
116  }
117 }
118 
BlendFunc(const uint32 pp,const uint32 p,const uint32 pn)119 static INLINE uint32 BlendFunc(const uint32 pp, const uint32 p, const uint32 pn)
120 {
121  const uint32 pp_pn = (((uint64)pp + pn) - ((pp ^ pn) & 0x01010101)) >> 1;
122 
123 #if 1
124  return (((((pp_pn & 0x00FF00FF) * 120) + ((p & 0x00FF00FF) * 136)) >> 8) & 0x00FF00FF) +
125    		   (((((uint64)(pp_pn & 0xFF00FF00) * 120) + ((uint64)(p & 0xFF00FF00) * 136)) >> 8) & 0xFF00FF00);
126 #else
127  return (((uint64)pp_pn + p) - ((pp_pn ^ p) & 0x01010101)) >> 1;
128 #endif
129 }
130 
video_scanline(uint16_t * data,unsigned line,unsigned width,unsigned height,bool interlaced,bool field)131 void bSNES_v059::Interface::video_scanline(uint16_t *data, unsigned line, unsigned width, unsigned height, bool interlaced, bool field)
132 {
133  const int ppline = PrevLine;
134 
135  //if(rand() & 1)
136  // return;
137 
138  if((int)line <= PrevLine || (PrevLine == -1 && line > 32)) // Second part for PAL 224 line mode
139   return;
140 
141  PrevLine = line;
142  PrevFrameInterlaced = interlaced;
143 
144  if(snsf_loader)
145   return;
146 
147  if(!tsurf || !tlw || !tdr)
148   return;
149 
150  if(es->skip && !interlaced)
151   return;
152 
153  if(!interlaced)
154   field = 0;
155 
156 
157  BlankMissingLines(ppline + 1, line, interlaced, field);
158  //if(line == 0)
159  // printf("ZOOM: 0x%04x, %d %d, %d\n", data[0], interlaced, field, width);
160 
161  const unsigned y = line;
162  const uint16 *source_line = data;
163  uint32 *dest_line = tsurf->pixels + (field * tsurf->pitch32) + ((y << interlaced) * tsurf->pitch32);
164 
165  if(EnableHBlend && width == 256)
166  {
167   for(int x = 0; x < width; x++)
168   {
169    const uint32 p = ColorMap[source_line[x] & 0x7FFF];
170    dest_line[(x << 1) + 0] = p;
171    dest_line[(x << 1) + 1] = p;
172   }
173   width = 512;
174  }
175  else if(!EnableHBlend && width == 512 && (source_line[0] & 0x8000))
176  {
177   width = 256;
178   for(int x = 0; x < 256; x++)
179   {
180    uint16 p1 = source_line[(x << 1) | 0] & 0x7FFF;
181    uint16 p2 = source_line[(x << 1) | 1] & 0x7FFF;
182    dest_line[x] = ColorMap[(p1 + p2 - ((p1 ^ p2) & 0x0421)) >> 1];
183   }
184  }
185  else
186  {
187   for(int x = 0; x < width; x++)
188    dest_line[x] = ColorMap[source_line[x] & 0x7FFF];
189  }
190 
191  if(EnableHBlend)
192  {
193   const uint32 black = tsurf->MakeColor(0, 0, 0);
194   uint32 pp = black;
195 
196   //for(int x = 0; x < 512; x++)
197   // dest_line[x] = (!(x % 5)) ? tsurf->MakeColor(0xFF, 0xFF, 0xFF) : tsurf->MakeColor(0, 0, 0);
198 
199   for(int x = 0; x < 511; x++)
200   {
201    const uint32 p = dest_line[x + 0];
202    const uint32 pn = dest_line[x + 1];
203 
204    dest_line[x] = BlendFunc(pp, p, pn);
205    pp = p;
206   }
207 
208   dest_line[511] = BlendFunc(pp, dest_line[511], black);
209  }
210 
211  tlw[(y << interlaced) + field] = width;
212  tdr->w = width;
213  tdr->h = height << interlaced;
214 
215  es->InterlaceOn = interlaced;
216  es->InterlaceField = (interlaced && field);
217 
218  MDFN_MidLineUpdate(es, (y << interlaced) + field);
219 }
220 
audio_sample(uint16_t l_sample,uint16_t r_sample)221 void bSNES_v059::Interface::audio_sample(uint16_t l_sample, uint16_t r_sample)
222 {
223  CycleCounter++;
224 
225  if(!SoundOn)
226   return;
227 
228  if(ResampInPos < 2048)
229  {
230   //l_sample = (rand() & 0x7FFF) - 0x4000;
231   //r_sample = (rand() & 0x7FFF) - 0x4000;
232   ResampInBuffer[ResampInPos][0] = (int16)l_sample;
233   ResampInBuffer[ResampInPos][1] = (int16)r_sample;
234   ResampInPos++;
235  }
236  else
237  {
238   MDFN_Notify(MDFN_NOTICE_WARNING, "SNES resample buffer overflow.");
239  }
240 }
241 
242 #if 0
243 class Input {
244 public:
245   enum Device {
246     DeviceNone,
247     DeviceJoypad,
248     DeviceMultitap,
249     DeviceMouse,
250     DeviceSuperScope,
251     DeviceJustifier,
252     DeviceJustifiers,
253   };
254 
255   enum JoypadID {
256     JoypadB      =  0, JoypadY     =  1,
257     JoypadSelect =  2, JoypadStart =  3,
258     JoypadUp     =  4, JoypadDown  =  5,
259     JoypadLeft   =  6, JoypadRight =  7,
260     JoypadA      =  8, JoypadX     =  9,
261     JoypadL      = 10, JoypadR     = 11,
262   };
263 #endif
264 
input_poll()265 void bSNES_v059::Interface::input_poll()
266 {
267  if(!InProperEmu)
268   return;
269 
270  HasPolledThisFrame = true;
271 
272  for(int port = 0; port < 2; port++)
273  {
274   switch(InputType[port])
275   {
276    case bSNES_v059::Input::DeviceJoypad:
277 	PadLatch[port] = MDFN_de16lsb(InputPtr[port]);
278 	break;
279 
280    case bSNES_v059::Input::DeviceMultitap:
281 	for(int index = 0; index < 4; index++)
282         {
283          if(!index)
284           PadLatch[port] = MDFN_de16lsb(InputPtr[port]);
285          else
286 	 {
287 	  int pi = 2 + 3 * (port ^ 1) + (index - 1);
288           PadLatch[pi] = MDFN_de16lsb(InputPtr[pi]);
289 	 }
290         }
291         break;
292 
293    case bSNES_v059::Input::DeviceMouse:
294 	MouseXLatch[port] = (int16)MDFN_de16lsb(InputPtr[port] + 0);
295 	MouseYLatch[port] = (int16)MDFN_de16lsb(InputPtr[port] + 2);
296 	MouseBLatch[port] = *(uint8 *)(InputPtr[port] + 4);
297 	break;
298 
299    case bSNES_v059::Input::DeviceSuperScope:
300 	{
301 	 bool old_ost = (ScopeBLatch[port] & 0x02);
302 
303 	 ScopeXLatch[port] = (int16)MDFN_de16lsb(InputPtr[port] + 0);
304 	 ScopeYLatch[port] = (int16)MDFN_de16lsb(InputPtr[port] + 2);
305 	 ScopeBLatch[port] = *(uint8 *)(InputPtr[port] + 4);
306 
307 	 if(!old_ost && (ScopeBLatch[port] & 0x02))
308 	  ScopeOSCounter[port] = ScopeOSCounter_StartVal;
309 	}
310 	break;
311   }
312  }
313 }
314 
sats32tos16(int32 val)315 static INLINE int16 sats32tos16(int32 val)
316 {
317  if(val > 32767)
318   val = 32767;
319  if(val < -32768)
320   val = -32768;
321 
322  return(val);
323 }
324 
input_poll(bool port,unsigned device,unsigned index,unsigned id)325 int16_t bSNES_v059::Interface::input_poll(bool port, unsigned device, unsigned index, unsigned id)
326 {
327  if(!HasPolledThisFrame)
328   printf("input_poll(...) before input_poll() for frame, %d %d %d %d\n", port, device, index, id);
329 
330  switch(device)
331  {
332  	case bSNES_v059::Input::DeviceJoypad:
333 	{
334 	  return((PadLatch[port] >> id) & 1);
335 	}
336 	break;
337 
338 	case bSNES_v059::Input::DeviceMultitap:
339 	{
340 	 if(!index)
341           return((PadLatch[port] >> id) & 1);
342          else
343 	  return((PadLatch[2 + 3 * (port ^ 1) + (index - 1)] >> id) & 1);
344 	}
345 	break;
346 
347 	case bSNES_v059::Input::DeviceMouse:
348 	{
349 	 switch(id)
350 	 {
351 	  case bSNES_v059::Input::MouseX:
352 		return(sats32tos16(MouseXLatch[port]));
353 		break;
354 
355 	  case bSNES_v059::Input::MouseY:
356 		return(sats32tos16(MouseYLatch[port]));
357 		break;
358 
359 	  case bSNES_v059::Input::MouseLeft:
360 		return((int)(bool)(MouseBLatch[port] & 1));
361 		break;
362 
363 	  case bSNES_v059::Input::MouseRight:
364 		return((int)(bool)(MouseBLatch[port] & 2));
365 		break;
366 	 }
367 	}
368 	break;
369 
370 	case bSNES_v059::Input::DeviceSuperScope:
371 	{
372 	 switch(id)
373 	 {
374 	  case bSNES_v059::Input::SuperScopeX:
375 		return(ScopeOSCounter[port] ? 1000 : ScopeXLatch[port]);
376 		break;
377 
378 	  case bSNES_v059::Input::SuperScopeY:
379 		return(ScopeOSCounter[port] ? 1000 : ScopeYLatch[port]);
380 		break;
381 
382 	  case bSNES_v059::Input::SuperScopeTrigger:
383 		{
384 		 bool trigo = (bool)(ScopeBLatch[port] & 0x01);
385 
386 		 if(ScopeOSCounter[port] >= ScopeOSCounter_TriggerEndThresh)
387 		  trigo = 1;
388 
389 		 if(ScopeOSCounter[port] >= ScopeOSCounter_TriggerStartThresh)
390 		  trigo = 0;
391 
392 		 return(trigo);
393 		}
394 		break;
395 
396 	  case bSNES_v059::Input::SuperScopeCursor:
397 		return((bool)(ScopeBLatch[port] & 0x10));
398 		break;
399 
400 	  case bSNES_v059::Input::SuperScopeTurbo:
401 		return((bool)(ScopeBLatch[port] & 0x08));
402 		break;
403 
404 	  case bSNES_v059::Input::SuperScopePause:
405 		return((bool)(ScopeBLatch[port] & 0x04));
406 		break;
407 	 }
408 	}
409 	break;
410  }
411 
412  return(0);
413 }
414 
415 #if 0
416 void bSNES_v059::Interface::init()
417 {
418 
419 
420 }
421 
422 void bSNES_v059::Interface::term()
423 {
424 
425 
426 }
427 #endif
428 
429 #if 0
430 
431 namespace memory {
432   extern MappedRAM cartrom, cartram, cartrtc;
433   extern MappedRAM bsxflash, bsxram, bsxpram;
434   extern MappedRAM stArom, stAram;
435   extern MappedRAM stBrom, stBram;
436   extern MappedRAM gbrom, gbram;
437 };
438 
439 #endif
440 
SaveMemorySub(bool load,const char * extension,bSNES_v059::MappedRAM * memoryA,bSNES_v059::MappedRAM * memoryB=NULL)441 static void SaveMemorySub(bool load, const char *extension, bSNES_v059::MappedRAM *memoryA, bSNES_v059::MappedRAM *memoryB = NULL)
442 {
443  const std::string path = MDFN_MakeFName(MDFNMKF_SAV, 0, extension);
444  const size_t total_size = ((memoryA && memoryA->size() != 0 && memoryA->size() != -1U) ? memoryA->size() : 0) +
445 		       	   ((memoryB && memoryB->size() != 0 && memoryB->size() != -1U) ? memoryB->size() : 0);
446 
447 
448  if(!total_size)
449   return;
450 
451  if(load)
452  {
453   try
454   {
455    std::unique_ptr<Stream> gp = MDFN_AmbigGZOpenHelper(path, std::vector<size_t>({ total_size }));
456 
457    if(memoryA && memoryA->size() != 0 && memoryA->size() != -1U)
458    {
459     gp->read(memoryA->data(), memoryA->size());
460    }
461 
462    if(memoryB && memoryB->size() != 0 && memoryB->size() != -1U)
463    {
464     gp->read(memoryB->data(), memoryB->size());
465    }
466   }
467   catch(MDFN_Error &e)
468   {
469    if(e.GetErrno() != ENOENT)
470     throw;
471   }
472  }
473  else
474  {
475   std::vector<PtrLengthPair> MemToSave;
476 
477   if(memoryA && memoryA->size() != 0 && memoryA->size() != -1U)
478    MemToSave.push_back(PtrLengthPair(memoryA->data(), memoryA->size()));
479 
480   if(memoryB && memoryB->size() != 0 && memoryB->size() != -1U)
481    MemToSave.push_back(PtrLengthPair(memoryB->data(), memoryB->size()));
482 
483   MDFN_DumpToFile(path, MemToSave, true);
484  }
485 }
486 
SaveLoadMemory(bool load)487 static void SaveLoadMemory(bool load)
488 {
489   if(bSNES_v059::cartridge.loaded() == false)
490    return;
491 
492   switch(bSNES_v059::cartridge.mode())
493   {
494     case bSNES_v059::Cartridge::ModeNormal:
495     case bSNES_v059::Cartridge::ModeBsxSlotted:
496     {
497       SaveMemorySub(load, "srm", &bSNES_v059::memory::cartram);
498       SaveMemorySub(load, "rtc", &bSNES_v059::memory::cartrtc);
499     }
500     break;
501 
502     case bSNES_v059::Cartridge::ModeBsx:
503     {
504       SaveMemorySub(load, "srm", &bSNES_v059::memory::bsxram );
505       SaveMemorySub(load, "psr", &bSNES_v059::memory::bsxpram);
506     }
507     break;
508 
509     case bSNES_v059::Cartridge::ModeSufamiTurbo:
510     {
511      SaveMemorySub(load, "srm", &bSNES_v059::memory::stAram, &bSNES_v059::memory::stBram);
512     }
513     break;
514 
515     case bSNES_v059::Cartridge::ModeSuperGameBoy:
516     {
517      SaveMemorySub(load, "sav", &bSNES_v059::memory::gbram);
518      SaveMemorySub(load, "rtc", &bSNES_v059::memory::gbrtc);
519     }
520     break;
521   }
522 }
523 
524 
TestMagic(GameFile * gf)525 static bool TestMagic(GameFile* gf)
526 {
527  if(gf->ext != "smc" && gf->ext != "swc" && gf->ext != "sfc" && gf->ext != "fig" &&
528         gf->ext != "bs" && gf->ext != "st")
529  {
530   return false;
531  }
532 
533  return true;
534 }
535 
SetupMisc(bool PAL)536 static void SetupMisc(bool PAL)
537 {
538  PrevFrameInterlaced = false;
539 
540  bSNES_v059::video.set_mode(PAL ? bSNES_v059::Video::ModePAL : bSNES_v059::Video::ModeNTSC);
541 
542  // Nominal FPS values are a bit off, FIXME(and contemplate the effect on netplay sound buffer overruns/underruns)
543  MDFNGameInfo->fps = PAL ? 838977920 : 1008307711;
544  MDFNGameInfo->MasterClock = MDFN_MASTERCLOCK_FIXED(32040.5);
545 
546  if(!snsf_loader)
547  {
548   EnableHBlend = MDFN_GetSettingB("snes.h_blend");
549   MDFNGameInfo->nominal_width = MDFN_GetSettingB("snes.correct_aspect") ? (PAL ? 344/*354*/ : 292) : 256;
550   MDFNGameInfo->nominal_height = PAL ? 239 : 224;
551   MDFNGameInfo->lcm_height = MDFNGameInfo->nominal_height * 2;
552 
553   //
554   // SuperScope coordinate translation stuff:
555   //
556   MDFNGameInfo->mouse_scale_x = 256.0;
557   MDFNGameInfo->mouse_scale_y = MDFNGameInfo->nominal_height;
558   MDFNGameInfo->mouse_offs_x = 0.0;
559   MDFNGameInfo->mouse_offs_y = 0.0;
560  }
561 
562  ResampInPos = 0;
563  SoundLastRate = 0;
564 }
565 
LoadSNSF(GameFile * gf)566 static void LoadSNSF(GameFile* gf)
567 {
568  bool PAL = false;
569 
570  bSNES_v059::system.init(&Interface);
571 
572  MultitapEnabled[0] = false;
573  MultitapEnabled[1] = false;
574 
575  std::vector<std::string> SongNames;
576 
577  snsf_loader = new SNSFLoader(gf->vfs, gf->dir, gf->stream);
578  {
579   uint8 *export_ptr;
580 
581   export_ptr = new uint8[8192 * 1024];
582   memset(export_ptr, 0x00, 8192 * 1024);
583   assert(snsf_loader->ROM_Data.size() <= 8192 * 1024);
584   snsf_loader->ROM_Data.read(export_ptr, snsf_loader->ROM_Data.size());
585   bSNES_v059::memory::cartrom.map(export_ptr, snsf_loader->ROM_Data.size());
586   snsf_loader->ROM_Data.close();
587 
588   bSNES_v059::cartridge.load(bSNES_v059::Cartridge::ModeNormal);
589  }
590  SongNames.push_back(snsf_loader->tags.GetTag("title"));
591 
592  Player_Init(1, snsf_loader->tags.GetTag("game"), snsf_loader->tags.GetTag("artist"), snsf_loader->tags.GetTag("copyright"), SongNames);
593 
594  bSNES_v059::system.power();
595  PAL = (bSNES_v059::system.region() == bSNES_v059::System::PAL);
596 
597  SetupMisc(PAL);
598 }
599 
Cleanup(void)600 static void Cleanup(void)
601 {
602  bSNES_v059::memory::cartrom.map(NULL, 0); // So it delete[]s the pointer it took ownership of.
603 
604  if(snsf_loader)
605  {
606   delete snsf_loader;
607   snsf_loader = NULL;
608  }
609 
610  ColorMap.resize(0);
611 
612  if(resampler)
613  {
614   speex_resampler_destroy(resampler);
615   resampler = NULL;
616  }
617 }
618 
619 static const unsigned cheat_page_size = 1024;
620 template<typename T>
CheatMap(bool uics,uint32 addr,T & mr,uint32 offset)621 static void CheatMap(bool uics, uint32 addr, T& mr, uint32 offset)
622 {
623  assert((offset + cheat_page_size) <= mr.size());
624  MDFNMP_AddRAM(cheat_page_size, addr, mr.data() + offset, uics);
625 }
626 
627 // Intended only for the MapLinear type.
628 template<typename T>
CheatMap(bool uics,uint8 bank_lo,uint8 bank_hi,uint16 addr_lo,uint16 addr_hi,T & mr,uint32 offset=0,uint32 size=0)629 static void CheatMap(bool uics, uint8 bank_lo, uint8 bank_hi, uint16 addr_lo, uint16 addr_hi, T& mr, uint32 offset = 0, uint32 size = 0)
630 {
631  assert(bank_lo <= bank_hi);
632  assert(addr_lo <= addr_hi);
633  if((int)mr.size() < cheat_page_size)
634  {
635   if((int)mr.size() > 0)
636    printf("Boop: %d\n", mr.size());
637   return;
638  }
639 
640  uint8 page_lo = addr_lo / cheat_page_size;
641  uint8 page_hi = addr_hi / cheat_page_size;
642  unsigned index = 0;
643 
644  for(unsigned bank = bank_lo; bank <= bank_hi; bank++)
645  {
646   for(unsigned page = page_lo; page <= page_hi; page++)
647   {
648    if(size)
649    {
650     if(index >= size)
651      uics = false;
652     index %= size;
653    }
654 
655    if((offset + index) >= mr.size())
656     uics = false;
657 
658    CheatMap(uics, (bank << 16) + (page * cheat_page_size), mr, bSNES_v059::bus.mirror(offset + index, mr.size()));
659    index += cheat_page_size;
660   }
661  }
662 }
663 
Load(GameFile * gf)664 static void Load(GameFile* gf)
665 {
666  bool PAL = false;
667 
668  CycleCounter = 0;
669 
670  try
671  {
672   if(PSFLoader::TestMagic(0x23, gf->stream))
673   {
674    LoadSNSF(gf);
675    return;
676   }
677 
678   bSNES_v059::system.init(&Interface);
679 
680   // Allocate 8MiB of space regardless of actual ROM image size, to prevent malformed or corrupted ROM images
681   // from crashing the bsnes cart loading code.
682   {
683    static const uint64 max_rom_size = 8192 * 1024;
684    const uint64 raw_size = gf->stream->size();
685    const unsigned header_adjust = (((raw_size & 0x7FFF) == 512) ? 512 : 0);
686    const uint64 size = raw_size - header_adjust;
687    md5_context md5;
688    md5.starts();
689 
690    if(size > max_rom_size)
691     throw MDFN_Error(0, _("SNES ROM image is too large."));
692 
693    if(header_adjust)
694    {
695     uint8 header_tmp[512];
696     gf->stream->read(header_tmp, 512);
697     md5.update(header_tmp, 512);	// For Mednafen backwards compat
698    }
699 
700    std::unique_ptr<uint8[]> export_ptr(new uint8[max_rom_size]);
701    memset(export_ptr.get(), 0x00, max_rom_size);
702    gf->stream->read(export_ptr.get(), size);
703 
704    md5.update(export_ptr.get(), size);
705    md5.finish(MDFNGameInfo->MD5);
706 
707    //
708    // Mirror up to an 8MB boundary so we can implement HAPPY FUNTIME YAAAAAAAY optimizations(like with SuperFX).
709    //
710    for(uint32 a = (size + 255) &~255; a < max_rom_size; a += 256)
711    {
712     const uint32 oa = bSNES_v059::bus.mirror(a, size);
713     //printf("%08x->%08x\n",a, oa);
714     memcpy(&export_ptr[a], &export_ptr[oa], 256);
715    }
716    bSNES_v059::memory::cartrom.map(export_ptr.release(), size);
717    bSNES_v059::cartridge.load(bSNES_v059::Cartridge::ModeNormal);
718   }
719 
720   bSNES_v059::system.power();
721 
722   PAL = (bSNES_v059::system.region() == bSNES_v059::System::PAL);
723 
724   SetupMisc(PAL);
725 
726   MultitapEnabled[0] = MDFN_GetSettingB("snes.input.port1.multitap");
727   MultitapEnabled[1] = MDFN_GetSettingB("snes.input.port2.multitap");
728 
729   SaveLoadMemory(true);
730 
731   //printf(" %d %d\n", FSettings.SndRate, resampler.max_write());
732 
733   MDFNMP_Init(cheat_page_size, (1U << 24) / cheat_page_size);
734 
735   //
736   // Should more-or-less match what's in: src/memory/smemory/generic.cpp
737   //
738   if((int)bSNES_v059::memory::cartram.size() > 0 && (bSNES_v059::memory::cartram.size() % cheat_page_size) == 0)
739   {
740    if(bSNES_v059::cartridge.mapper() == bSNES_v059::Cartridge::SuperFXROM)
741    {
742 
743    }
744    else if(bSNES_v059::cartridge.mapper() == bSNES_v059::Cartridge::SA1ROM)
745    {
746     CheatMap(true,  0x00, 0x3f, 0x3000, 0x37ff, bSNES_v059::memory::iram);    //cpuiram);
747     CheatMap(false, 0x00, 0x3f, 0x6000, 0x7fff, bSNES_v059::memory::cartram); //cc1bwram);
748     CheatMap(true,  0x40, 0x4f, 0x0000, 0xffff, bSNES_v059::memory::cartram); //cc1bwram);
749     CheatMap(false, 0x80, 0xbf, 0x3000, 0x37ff, bSNES_v059::memory::iram);    //cpuiram);
750     CheatMap(false, 0x80, 0xbf, 0x6000, 0x7fff, bSNES_v059::memory::cartram); //cc1bwram);
751    }
752    else if(bSNES_v059::cartridge.mapper() == bSNES_v059::Cartridge::SPC7110ROM)
753    {
754 
755    }
756    else if(bSNES_v059::cartridge.mapper() == bSNES_v059::Cartridge::BSXROM)
757    {
758 
759    }
760    else if(bSNES_v059::cartridge.mapper() == bSNES_v059::Cartridge::BSCLoROM)
761    {
762 
763    }
764    else if(bSNES_v059::cartridge.mapper() == bSNES_v059::Cartridge::BSCHiROM)
765    {
766 
767    }
768    else if(bSNES_v059::cartridge.mapper() == bSNES_v059::Cartridge::STROM)
769    {
770 
771    }
772    else
773    {
774     if((int)bSNES_v059::memory::cartram.size() > 0)
775     {
776      CheatMap(false, 0x20, 0x3f, 0x6000, 0x7fff, bSNES_v059::memory::cartram);
777      CheatMap(false, 0xa0, 0xbf, 0x6000, 0x7fff, bSNES_v059::memory::cartram);
778 
779      //research shows only games with very large ROM/RAM sizes require MAD-1 memory mapping of RAM
780      //otherwise, default to safer, larger RAM address window
781      uint16 addr_hi = (bSNES_v059::memory::cartrom.size() > 0x200000 || bSNES_v059::memory::cartram.size() > 32 * 1024) ? 0x7fff : 0xffff;
782      const bool meowmeowmoocow = bSNES_v059::memory::cartram.size() <= ((addr_hi + 1) * 14) || bSNES_v059::cartridge.mapper() != bSNES_v059::Cartridge::LoROM;
783 
784      CheatMap(meowmeowmoocow, 0x70, 0x7f, 0x0000, addr_hi, bSNES_v059::memory::cartram);
785 
786      if(bSNES_v059::cartridge.mapper() == bSNES_v059::Cartridge::LoROM)
787       CheatMap(!meowmeowmoocow, 0xf0, 0xff, 0x0000, addr_hi, bSNES_v059::memory::cartram);
788     }
789    }
790   }
791 
792   //
793   // System(WRAM) mappings should be done last, as they'll partially overwrite some of the cart mappings above, matching bsnes' internal semantics.
794   //
795 
796   CheatMap(false, 0x00, 0x3f, 0x0000, 0x1fff, bSNES_v059::memory::wram, 0x000000, 0x002000);
797   CheatMap(false, 0x80, 0xbf, 0x0000, 0x1fff, bSNES_v059::memory::wram, 0x000000, 0x002000);
798   CheatMap(true,  0x7e, 0x7f, 0x0000, 0xffff, bSNES_v059::memory::wram);
799 
800   ColorMap.resize(32768);
801  }
802  catch(std::exception &e)
803  {
804   Cleanup();
805   throw;
806  }
807 }
808 
CloseGame(void)809 static void CloseGame(void)
810 {
811  if(!snsf_loader)
812  {
813   try
814   {
815    SaveLoadMemory(false);
816   }
817   catch(std::exception &e)
818   {
819    MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
820   }
821  }
822  Cleanup();
823 }
824 
Emulate(EmulateSpecStruct * espec)825 static void Emulate(EmulateSpecStruct *espec)
826 {
827  tsurf = espec->surface;
828  tlw = espec->LineWidths;
829  tdr = &espec->DisplayRect;
830  es = espec;
831 
832  for(unsigned i = 0; i < 2; i++)
833   ScopeOSCounter[i] -= (bool)ScopeOSCounter[i];
834 
835  PrevLine = -1;
836 
837  if(!snsf_loader)
838  {
839   if(!espec->skip && tsurf && tlw)
840   {
841    tdr->x = 0;
842    tdr->y = 0;
843    tdr->w = 1;
844    tdr->h = 1;
845    tlw[0] = 0;	// Mark line widths as valid(ie != ~0; since field == 1 would skip it).
846   }
847 
848   if(espec->VideoFormatChanged)
849    BuildColorMap(espec->surface->format, espec->CustomPalette);
850  }
851 
852  if(SoundLastRate != espec->SoundRate)
853  {
854   if(resampler)
855   {
856    speex_resampler_destroy(resampler);
857    resampler = NULL;
858   }
859   int err = 0;
860   int quality = MDFN_GetSettingUI("snes.apu.resamp_quality");
861 
862   resampler = speex_resampler_init_frac(2, 64081, 2 * (int)(espec->SoundRate ? espec->SoundRate : 48000),
863 					   32040.5, (int)(espec->SoundRate ? espec->SoundRate : 48000), quality, &err);
864   SoundLastRate = espec->SoundRate;
865 
866   //printf("%f ms\n", 1000.0 * speex_resampler_get_input_latency(resampler) / 32040.5);
867  }
868 
869  if(!snsf_loader)
870  {
871   MDFNMP_ApplyPeriodicCheats();
872  }
873 
874  // Make sure to trash any leftover samples, generated from system.runtosave() in save state saving, if sound is now disabled.
875  if(SoundOn && !espec->SoundBuf)
876  {
877   ResampInPos = 0;
878  }
879 
880  SoundOn = espec->SoundBuf ? true : false;
881 
882  HasPolledThisFrame = false;
883  InProperEmu = true;
884 
885  // More aggressive frameskipping disabled until we can rule out undesirable side-effects and interactions.
886  //bSNES_v059::ppu.enable_renderer(!espec->skip || PrevFrameInterlaced);
887  bSNES_v059::system.run_mednafen_custom();
888  bSNES_v059::ppu.enable_renderer(true);
889 
890 
891  //
892  // Blank out any missed lines(for e.g. display height change with PAL emulation)
893  //
894  if(!snsf_loader && !es->skip && tsurf && tlw)
895  {
896   //printf("%d\n", PrevLine + 1);
897   BlankMissingLines(PrevLine + 1, tdr->h >> es->InterlaceOn, es->InterlaceOn, es->InterlaceField);
898  }
899 
900  tsurf = NULL;
901  tlw = NULL;
902  tdr = NULL;
903  es = NULL;
904  InProperEmu = false;
905 
906  espec->MasterCycles = CycleCounter;
907  CycleCounter = 0;
908 
909  //if(!espec->MasterCycles)
910  //{
911  // puts("BOGUS GNOMES");
912  // espec->MasterCycles = 1;
913  //}
914  //printf("%d\n", espec->MasterCycles);
915 
916  if(espec->SoundBuf)
917  {
918   spx_uint32_t in_len; // "Number of input samples in the input buffer. Returns the number of samples processed. This is all per-channel."
919   spx_uint32_t out_len; // "Size of the output buffer. Returns the number of samples written. This is all per-channel."
920 
921   // Hrm, still crackly with some games(like MMX) when rewinding...
922   if(espec->NeedSoundReverse)
923   {
924    for(unsigned lr = 0; lr < 2; lr++)
925    {
926     int16* p0 = &ResampInBuffer[0][lr];
927     int16* p1 = &ResampInBuffer[ResampInPos - 1][lr];
928     unsigned count = ResampInPos >> 1;
929 
930     while(MDFN_LIKELY(count--))
931     {
932      int16 tmp;
933 
934      tmp = *p0;
935      *p0 = *p1;
936      *p1 = tmp;
937      p0 += 2;
938      p1 -= 2;
939     }
940    }
941    espec->NeedSoundReverse = false;
942   }
943 
944   //printf("%d\n", ResampInPos);
945   in_len = ResampInPos;
946   out_len = 524288; //8192;     // FIXME, real size.
947 
948   speex_resampler_process_interleaved_int(resampler, (const spx_int16_t *)ResampInBuffer, &in_len, (spx_int16_t *)espec->SoundBuf, &out_len);
949 
950   assert(in_len <= ResampInPos);
951 
952   if((ResampInPos - in_len) > 0)
953    memmove(ResampInBuffer, ResampInBuffer + in_len, (ResampInPos - in_len) * sizeof(int16) * 2);
954 
955   ResampInPos -= in_len;
956 
957   espec->SoundBufSize = out_len;
958  }
959 
960  MDFNGameInfo->mouse_sensitivity = MDFN_GetSettingF("snes.mouse_sensitivity");
961 
962  if(snsf_loader)
963  {
964   if(!espec->skip)
965   {
966    espec->LineWidths[0] = ~0;
967    Player_Draw(espec->surface, &espec->DisplayRect, 0, espec->SoundBuf, espec->SoundBufSize);
968   }
969  }
970 
971 
972 #if 0
973  {
974   static int skipframe = 3;
975 
976   if(skipframe)
977    skipframe--;
978   else
979   {
980    static unsigned fc = 0;
981    static uint64 cc = 0;
982    static uint64 cc2 = 0;
983 
984    fc++;
985    cc += espec->MasterCycles;
986    cc2 += espec->SoundBufSize;
987 
988    printf("%f %f\n", (double)fc / ((double)cc / 32040.5), (double)fc / ((double)cc2 / espec->SoundRate));
989   }
990  }
991 #endif
992 }
993 
StateAction(StateMem * sm,const unsigned load,const bool data_only)994 static void StateAction(StateMem *sm, const unsigned load, const bool data_only)
995 {
996  const uint32 length = bSNES_v059::system.serialize_size();
997  bSNES_v059::Input* inpp = &bSNES_v059::input;
998 
999  SFORMAT ExtraStateRegs[] =
1000  {
1001    SFVAR(PadLatch),
1002 
1003    SFVAR(MouseXLatch),
1004    SFVAR(MouseYLatch),
1005    SFVAR(MouseBLatch),
1006 
1007    SFVAR(ScopeXLatch),
1008    SFVAR(ScopeYLatch),
1009    SFVAR(ScopeBLatch),
1010    SFVAR(ScopeOSCounter),
1011 
1012    SFVAR(inpp->latchx),
1013    SFVAR(inpp->latchy),
1014 #define INPP_HELPER(n)				\
1015    SFVAR(inpp->port[n].counter0),		\
1016    SFVAR(inpp->port[n].counter1),		\
1017    SFVAR(inpp->port[n].superscope.x),		\
1018    SFVAR(inpp->port[n].superscope.y),		\
1019    SFVAR(inpp->port[n].superscope.trigger),	\
1020    SFVAR(inpp->port[n].superscope.cursor),	\
1021    SFVAR(inpp->port[n].superscope.turbo),	\
1022    SFVAR(inpp->port[n].superscope.pause),	\
1023    SFVAR(inpp->port[n].superscope.offscreen),	\
1024    SFVAR(inpp->port[n].superscope.turbolock),	\
1025    SFVAR(inpp->port[n].superscope.triggerlock),	\
1026    SFVAR(inpp->port[n].superscope.pauselock)
1027 
1028    INPP_HELPER(0),
1029    INPP_HELPER(1),
1030 
1031 #undef INPP_HELPER
1032 
1033    SFEND
1034  };
1035 
1036  if(load)
1037  {
1038   std::unique_ptr<uint8[]> ptr(new uint8[length]);
1039 
1040   SFORMAT StateRegs[] =
1041   {
1042    SFPTR8N(ptr.get(), length, "OmniCat"),
1043    SFLINK(ExtraStateRegs),
1044    SFEND
1045   };
1046 
1047   MDFNSS_StateAction(sm, 1, data_only, StateRegs, "DATA");
1048 
1049   //srand(99);
1050   //for(int i = 16; i < length; i++)
1051   // ptr[i] = rand() & 0x3;
1052 
1053   serializer state(ptr.get(), length);
1054 
1055   if(!bSNES_v059::system.unserialize(state))
1056    throw MDFN_Error(0, _("bSNES core unserializer error."));
1057  }
1058  else // save:
1059  {
1060   if(bSNES_v059::scheduler.sync != bSNES_v059::Scheduler::SyncAll)
1061    bSNES_v059::system.runtosave();
1062 
1063   serializer state(length);
1064 
1065   bSNES_v059::system.serialize(state);
1066 
1067   assert(state.size() == length);
1068 
1069   uint8 *ptr = const_cast<uint8 *>(state.data());
1070 
1071   SFORMAT StateRegs[] =
1072   {
1073    SFPTR8N(ptr, length, "OmniCat"),
1074    SFLINK(ExtraStateRegs),
1075    SFEND
1076   };
1077 
1078   MDFNSS_StateAction(sm, 0, data_only, StateRegs, "DATA");
1079  }
1080 }
1081 
1082 struct StrToBSIT_t
1083 {
1084  const char *str;
1085  const int id;
1086 };
1087 
1088 static const StrToBSIT_t StrToBSIT[] =
1089 {
1090  { "none",   	bSNES_v059::Input::DeviceNone },
1091  { "gamepad",   bSNES_v059::Input::DeviceJoypad },
1092  { "multitap",  bSNES_v059::Input::DeviceMultitap },
1093  { "mouse",   	bSNES_v059::Input::DeviceMouse },
1094  { "superscope",   bSNES_v059::Input::DeviceSuperScope },
1095  { "justifier",   bSNES_v059::Input::DeviceJustifier },
1096  { "justifiers",   bSNES_v059::Input::DeviceJustifiers },
1097  { NULL,	-1	},
1098 };
1099 
1100 
SetInput(unsigned port,const char * type,uint8 * ptr)1101 static void SetInput(unsigned port, const char *type, uint8 *ptr)
1102 {
1103  assert(port < 8);
1104 
1105  if(port < 2)
1106  {
1107   const StrToBSIT_t *sb = StrToBSIT;
1108   int id = -1;
1109 
1110   if(MultitapEnabled[port] && !strcmp(type, "gamepad"))
1111    type = "multitap";
1112 
1113   while(sb->str && id == -1)
1114   {
1115    if(!strcmp(type, sb->str))
1116     id = sb->id;
1117    sb++;
1118   }
1119   assert(id != -1);
1120 
1121   InputType[port] = id;
1122 
1123   if(port)
1124    bSNES_v059::config.controller_port2 = id;
1125   else
1126    bSNES_v059::config.controller_port1 = id;
1127 
1128   bSNES_v059::input.port_set_device(port, id);
1129 
1130 #if 0
1131   switch(config().input.port1) { default:
1132     case ControllerPort1::None: mapper().port1 = 0; break;
1133     case ControllerPort1::Gamepad: mapper().port1 = &Controllers::gamepad1; break;
1134     case ControllerPort1::Asciipad: mapper().port1 = &Controllers::asciipad1; break;
1135     case ControllerPort1::Multitap: mapper().port1 = &Controllers::multitap1; break;
1136     case ControllerPort1::Mouse: mapper().port1 = &Controllers::mouse1; break;
1137   }
1138 
1139   switch(config().input.port2) { default:
1140     case ControllerPort2::None: mapper().port2 = 0; break;
1141     case ControllerPort2::Gamepad: mapper().port2 = &Controllers::gamepad2; break;
1142     case ControllerPort2::Asciipad: mapper().port2 = &Controllers::asciipad2; break;
1143     case ControllerPort2::Multitap: mapper().port2 = &Controllers::multitap2; break;
1144     case ControllerPort2::Mouse: mapper().port2 = &Controllers::mouse2; break;
1145     case ControllerPort2::SuperScope: mapper().port2 = &Controllers::superscope; break;
1146     case ControllerPort2::Justifier: mapper().port2 = &Controllers::justifier1; break;
1147     case ControllerPort2::Justifiers: mapper().port2 = &Controllers::justifiers; break;
1148   }
1149 #endif
1150 
1151  }
1152 
1153 
1154  InputPtr[port] = (uint8 *)ptr;
1155 }
1156 
SetLayerEnableMask(uint64 mask)1157 static void SetLayerEnableMask(uint64 mask)
1158 {
1159 
1160 }
1161 
1162 
DoSimpleCommand(int cmd)1163 static void DoSimpleCommand(int cmd)
1164 {
1165  switch(cmd)
1166  {
1167   case MDFN_MSC_RESET: bSNES_v059::system.reset(); break;
1168   case MDFN_MSC_POWER: bSNES_v059::system.power(); break;
1169  }
1170 }
1171 
1172 static const IDIISG GamepadIDII =
1173 {
1174  IDIIS_ButtonCR("b", "B (center, lower)", 7),
1175  IDIIS_ButtonCR("y", "Y (left)", 6),
1176  IDIIS_Button("select", "SELECT", 4),
1177  IDIIS_Button("start", "START", 5),
1178  IDIIS_Button("up", "UP ↑", 0, "down"),
1179  IDIIS_Button("down", "DOWN ↓", 1, "up"),
1180  IDIIS_Button("left", "LEFT ←", 2, "right"),
1181  IDIIS_Button("right", "RIGHT →", 3, "left"),
1182  IDIIS_ButtonCR("a", "A (right)", 9),
1183  IDIIS_ButtonCR("x", "X (center, upper)", 8),
1184  IDIIS_Button("l", "Left Shoulder", 10),
1185  IDIIS_Button("r", "Right Shoulder", 11),
1186 };
1187 
1188 static const IDIISG MouseIDII =
1189 {
1190  IDIIS_AxisRel("motion", "Motion",/**/ "left", "Left",/**/ "right", "Right", 0),
1191  IDIIS_AxisRel("motion", "Motion",/**/ "up", "Up",/**/ "down", "Down", 1),
1192  IDIIS_Button("left", "Left Button", 2),
1193  IDIIS_Button("right", "Right Button", 3),
1194 };
1195 
1196 static const IDIISG SuperScopeIDII =
1197 {
1198  { "x_axis", "X Axis", -1, IDIT_POINTER_X },
1199  { "y_axis", "Y Axis", -1, IDIT_POINTER_Y },
1200 
1201  IDIIS_Button("trigger", "Trigger", 0),
1202  IDIIS_Button("offscreen_shot", "Offscreen Shot(Simulated)", 1),
1203  IDIIS_Button("pause", "Pause", 2),
1204  IDIIS_Button("turbo", "Turbo", 3),
1205  IDIIS_Button("cursor", "Cursor", 4),
1206 };
1207 
1208 static const std::vector<InputDeviceInfoStruct> InputDeviceInfoSNESPort1 =
1209 {
1210  // None
1211  {
1212   "none",
1213   "none",
1214   NULL,
1215   IDII_Empty
1216  },
1217 
1218  // Gamepad
1219  {
1220   "gamepad",
1221   "Gamepad",
1222   NULL,
1223   GamepadIDII
1224  },
1225 
1226  // Mouse
1227  {
1228   "mouse",
1229   "Mouse",
1230   NULL,
1231   MouseIDII
1232  },
1233 };
1234 
1235 static const std::vector<InputDeviceInfoStruct> InputDeviceInfoSNESPort2 =
1236 {
1237  // None
1238  {
1239   "none",
1240   "none",
1241   NULL,
1242   IDII_Empty
1243  },
1244 
1245  // Gamepad
1246  {
1247   "gamepad",
1248   "Gamepad",
1249   NULL,
1250   GamepadIDII
1251  },
1252 
1253  // Mouse
1254  {
1255   "mouse",
1256   "Mouse",
1257   NULL,
1258   MouseIDII
1259  },
1260 
1261  // Super Scope
1262  {
1263   "superscope",
1264   "Super Scope",
1265   gettext_noop("Monkey!"),
1266   SuperScopeIDII
1267  },
1268 };
1269 
1270 
1271 static const std::vector<InputDeviceInfoStruct> InputDeviceInfoTapPort =
1272 {
1273  // Gamepad
1274  {
1275   "gamepad",
1276   "Gamepad",
1277   NULL,
1278   GamepadIDII,
1279  },
1280 };
1281 
1282 
1283 static const std::vector<InputPortInfoStruct> PortInfo =
1284 {
1285  { "port1", "Port 1/1A", InputDeviceInfoSNESPort1, "gamepad" },
1286  { "port2", "Port 2/2A", InputDeviceInfoSNESPort2, "gamepad" },
1287  { "port3", "Port 2B", InputDeviceInfoTapPort, "gamepad" },
1288  { "port4", "Port 2C", InputDeviceInfoTapPort, "gamepad" },
1289  { "port5", "Port 2D", InputDeviceInfoTapPort, "gamepad" },
1290  { "port6", "Port 1B", InputDeviceInfoTapPort, "gamepad" },
1291  { "port7", "Port 1C", InputDeviceInfoTapPort, "gamepad" },
1292  { "port8", "Port 1D", InputDeviceInfoTapPort, "gamepad" },
1293 };
1294 
InstallReadPatch(uint32 address,uint8 value,int compare)1295 static void InstallReadPatch(uint32 address, uint8 value, int compare)
1296 {
1297  bSNES_v059::CheatCode tc;
1298 
1299  tc.addr = address;
1300  tc.data = value;
1301  tc.compare = compare;
1302 
1303  //printf("%08x %02x %d\n", address, value, compare);
1304 
1305  bSNES_v059::cheat.enable(true);
1306  bSNES_v059::cheat.install_read_patch(tc);
1307 }
1308 
RemoveReadPatches(void)1309 static void RemoveReadPatches(void)
1310 {
1311  bSNES_v059::cheat.enable(false);
1312  bSNES_v059::cheat.remove_read_patches();
1313 }
1314 
1315 
1316 static const MDFNSetting SNESSettings[] =
1317 {
1318  { "snes.input.port1.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Enable multitap on SNES port 1."), NULL, MDFNST_BOOL, "0", NULL, NULL },
1319  { "snes.input.port2.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Enable multitap on SNES port 2."), NULL, MDFNST_BOOL, "0", NULL, NULL },
1320 
1321  { "snes.mouse_sensitivity", MDFNSF_NOFLAGS, gettext_noop("Emulated mouse sensitivity."), NULL, MDFNST_FLOAT, "0.50", NULL, NULL, NULL },
1322 
1323  { "snes.correct_aspect", MDFNSF_CAT_VIDEO, gettext_noop("Correct the aspect ratio."), gettext_noop("Note that regardless of this setting's value, \"512\" and \"256\" width modes will be scaled to the same dimensions for display."), MDFNST_BOOL, "0" },
1324 
1325  { "snes.h_blend", MDFNSF_NOFLAGS, gettext_noop("Enable horizontal blend(blur) filter."), gettext_noop("Intended for use in combination with the \"goat\" OpenGL shader, or with bilinear interpolation or linear interpolation on the X axis enabled."), MDFNST_BOOL, "0" },
1326 
1327  { "snes.apu.resamp_quality", MDFNSF_NOFLAGS, gettext_noop("APU output resampler quality."), gettext_noop("0 is lowest quality and latency and CPU usage, 10 is highest quality and latency and CPU usage.\n\nWith a Mednafen sound output rate of about 32041Hz or higher: Quality \"0\" resampler has approximately 0.125ms of latency, quality \"5\" resampler has approximately 1.25ms of latency, and quality \"10\" resampler has approximately 3.99ms of latency."), MDFNST_UINT, "5", "0", "10" },
1328 
1329  { NULL }
1330 };
1331 
1332 static const CheatInfoStruct CheatInfo =
1333 {
1334  InstallReadPatch,
1335  RemoveReadPatches,
1336 
1337  NULL, //MemRead,
1338  NULL,
1339 
1340  CheatFormats_SNES
1341 };
1342 
1343 static const FileExtensionSpecStruct KnownExtensions[] =
1344 {
1345  { ".smc", 0, "Super Magicom ROM Image" },
1346  { ".swc", 0, "Super Wildcard ROM Image" },
1347  { ".sfc", 0, "Cartridge ROM Image" },
1348  { ".fig", -10, "Cartridge ROM Image" },
1349 
1350  { ".bs", -10, "BS-X EEPROM Image" },
1351  { ".st", -10, "Sufami Turbo Cartridge ROM Image" },
1352 
1353  { NULL, 0, NULL }
1354 };
1355 
1356 MDFNGI EmulatedSNES =
1357 {
1358  "snes",
1359  "Super Nintendo Entertainment System/Super Famicom",
1360  KnownExtensions,
1361  MODPRIO_INTERNAL_HIGH,
1362  NULL,						// Debugger
1363  PortInfo,
1364  NULL,
1365  Load,
1366  TestMagic,
1367  NULL,
1368  NULL,
1369  CloseGame,
1370  SetLayerEnableMask,
1371  NULL,	// Layer names, null-delimited
1372  NULL,
1373  NULL,
1374 
1375  CPInfo,
1376  1 << 0,
1377 
1378  CheatInfo,
1379 
1380  true,
1381  StateAction,
1382  Emulate,
1383  NULL,
1384  SetInput,
1385  NULL,
1386  DoSimpleCommand,
1387  NULL,
1388  SNESSettings,
1389  0,
1390  0,
1391  false, // Multires
1392 
1393  512,   // lcm_width
1394  480,   // lcm_height           (replaced in game load)
1395  NULL,  // Dummy
1396 
1397  256,   // Nominal width	(replaced in game load)
1398  240,   // Nominal height	(replaced in game load)
1399 
1400  512,	// Framebuffer width
1401  512,	// Framebuffer height
1402 
1403  2,     // Number of output sound channels
1404 };
1405 
1406 
1407