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 #include "../general.h"
20 #include "src/base.hpp"
21 #include "../mempatcher.h"
22 #include "Fir_Resampler.h"
23 #include <vector>
24 
25 static void Cleanup(void);
26 
27 static Fir_Resampler<24> resampler;
28 class MeowFace : public SNES::Interface
29 {
30   virtual void video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height);
31   virtual void audio_sample(uint16_t l_sample, uint16_t r_sample);
32   virtual void input_poll();
33   virtual int16_t input_poll(bool port, unsigned device, unsigned index, unsigned id);
34 //  virtual int16_t input_poll(unsigned deviceid, unsigned id);
35 };
36 
37 static bool InProperEmu;
38 static bool SoundOn;
39 static double SoundLastRate = 0;
40 static MeowFace meowface;
41 
42 static int32 CycleCounter;
43 static MDFN_Surface *tsurf = NULL;
44 static MDFN_Rect *tlw = NULL;
45 static MDFN_Rect *tdr = NULL;
46 
47 static int InputType[2];
48 static uint8 *InputPtr[8] = { NULL };
49 static uint16 PadLatch[8];
50 static bool MultitapEnabled[2];
51 static bool HasPolledThisFrame;
52 
53 static int16 MouseXLatch[2];
54 static int16 MouseYLatch[2];
55 static uint8 MouseBLatch[2];
56 
57 static uint8 *CustomColorMap = NULL;
58 //static uint32 ColorMap[32768];
59 static std::vector<uint32> ColorMap;
60 
LoadCPalette(const char * syspalname,uint8 ** ptr,uint32 num_entries)61 static bool LoadCPalette(const char *syspalname, uint8 **ptr, uint32 num_entries)
62 {
63  std::string colormap_fn = MDFN_MakeFName(MDFNMKF_PALETTE, 0, syspalname).c_str();
64  FILE *fp;
65 
66  MDFN_printf(_("Loading custom palette from \"%s\"...\n"),  colormap_fn.c_str());
67  MDFN_indent(1);
68 
69  if(!(fp = fopen(colormap_fn.c_str(), "rb")))
70  {
71   ErrnoHolder ene(errno);
72 
73   MDFN_printf(_("Error opening file: %s\n"), ene.StrError());
74 
75   MDFN_indent(-1);
76 
77   return(ene.Errno() == ENOENT);        // Return fatal error if it's an error other than the file not being found.
78  }
79 
80  if(!(*ptr = (uint8 *)malloc(num_entries * 3)))
81  {
82   MDFN_indent(-1);
83 
84   fclose(fp);
85   return(false);
86  }
87 
88  if(fread(*ptr, 1, num_entries * 3, fp) != (num_entries * 3))
89  {
90   ErrnoHolder ene(errno);
91 
92   MDFN_printf(_("Error reading file: %s\n"), feof(fp) ? "EOF" : ene.StrError());
93   MDFN_indent(-1);
94 
95   free(*ptr);
96   *ptr = NULL;
97   fclose(fp);
98 
99   return(false);
100  }
101 
102  fclose(fp);
103 
104  MDFN_indent(-1);
105 
106  return(true);
107 }
108 
109 
BuildColorMap(MDFN_PixelFormat & format)110 static void BuildColorMap(MDFN_PixelFormat &format)
111 {
112  for(int x = 0; x < 32768; x++)
113  {
114   int r, g, b;
115 
116   r = (x & (0x1F <<  0)) << 3;
117   g = (x & (0x1F <<  5)) >> (5 - 3);
118   b = (x & (0x1F << 10)) >> (5 * 2 - 3);
119 
120   //r = ((((x >> 0) & 0x1F) * 255 + 15) / 31);
121   //g = ((((x >> 5) & 0x1F) * 255 + 15) / 31);
122   //b = ((((x >> 10) & 0x1F) * 255 + 15) / 31);
123 
124   if(CustomColorMap)
125   {
126    r = CustomColorMap[x * 3 + 0];
127    g = CustomColorMap[x * 3 + 1];
128    b = CustomColorMap[x * 3 + 2];
129   }
130 
131   ColorMap[x] = format.MakeColor(r, g, b);
132  }
133 }
134 
video_refresh(uint16_t * data,unsigned pitch,unsigned * line,unsigned width,unsigned height)135 void MeowFace::video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height)
136 {
137  if(!tsurf || !tlw || !tdr)
138   return;
139 
140  const uint16 *source_line = data;
141  uint32 *dest_line = tsurf->pixels;
142 
143  assert(!(pitch & 1));
144 
145  //if(height != 224)
146  // printf("%d\n", height);
147 
148  //if(tsurf->format.bpp == 32)
149  //{
150  //
151  //}
152  //else
153  {
154   for(int y = 0; y < height; y++, source_line += pitch >> 1, dest_line += tsurf->pitch32)
155    for(int x = 0; x < width; tlw[y].x = 0, tlw[y].w = (width == 512) ? line[y] : 256, x++)
156     dest_line[x] = ColorMap[source_line[x] & 0x7FFF];
157  }
158 
159  tdr->w = width;
160  tdr->h = height;
161 }
162 
audio_sample(uint16_t l_sample,uint16_t r_sample)163 void MeowFace::audio_sample(uint16_t l_sample, uint16_t r_sample)
164 {
165  CycleCounter++;
166 
167  if(!SoundOn)
168   return;
169 
170  if(resampler.max_write() >= 2)
171  {
172   resampler.buffer()[0] = l_sample;
173   resampler.buffer()[1] = r_sample;
174   resampler.write(2);
175  }
176  else
177  {
178   MDFN_DispMessage("Buffer overflow?");
179  }
180 }
181 
182 #if 0
183 class Input {
184 public:
185   enum Device {
186     DeviceNone,
187     DeviceJoypad,
188     DeviceMultitap,
189     DeviceMouse,
190     DeviceSuperScope,
191     DeviceJustifier,
192     DeviceJustifiers,
193   };
194 
195   enum JoypadID {
196     JoypadB      =  0, JoypadY     =  1,
197     JoypadSelect =  2, JoypadStart =  3,
198     JoypadUp     =  4, JoypadDown  =  5,
199     JoypadLeft   =  6, JoypadRight =  7,
200     JoypadA      =  8, JoypadX     =  9,
201     JoypadL      = 10, JoypadR     = 11,
202   };
203 #endif
204 
input_poll()205 void MeowFace::input_poll()
206 {
207  if(!InProperEmu)
208   return;
209 
210  HasPolledThisFrame = true;
211 
212  for(int port = 0; port < 2; port++)
213  {
214   switch(InputType[port])
215   {
216    case SNES::Input::DeviceJoypad:
217 	PadLatch[port] = MDFN_de16lsb(InputPtr[port]);
218 	break;
219 
220    case SNES::Input::DeviceMultitap:
221 	for(int index = 0; index < 4; index++)
222         {
223          if(!index)
224           PadLatch[port] = MDFN_de16lsb(InputPtr[port]);
225          else
226 	 {
227 	  int pi = 2 + 3 * (port ^ 1) + (index - 1);
228           PadLatch[pi] = MDFN_de16lsb(InputPtr[pi]);
229 	 }
230         }
231         break;
232 
233    case SNES::Input::DeviceMouse:
234 	MouseXLatch[port] = (int32)MDFN_de32lsb(InputPtr[port] + 0);
235 	MouseYLatch[port] = (int32)MDFN_de32lsb(InputPtr[port] + 4);
236 	MouseBLatch[port] = *(uint8 *)(InputPtr[port] + 8);
237 	break;
238   }
239  }
240 }
241 
sats32tos16(int32 val)242 static INLINE int16 sats32tos16(int32 val)
243 {
244  if(val > 32767)
245   val = 32767;
246  if(val < -32768)
247   val = -32768;
248 
249  return(val);
250 }
251 
input_poll(bool port,unsigned device,unsigned index,unsigned id)252 int16_t MeowFace::input_poll(bool port, unsigned device, unsigned index, unsigned id)
253 {
254  if(!HasPolledThisFrame)
255   printf("input_poll(...) before input_poll() for frame, %d %d %d %d\n", port, device, index, id);
256 
257  switch(device)
258  {
259  	case SNES::Input::DeviceJoypad:
260 	{
261 	  return((PadLatch[port] >> id) & 1);
262 	}
263 	break;
264 
265 	case SNES::Input::DeviceMultitap:
266 	{
267 	 if(!index)
268           return((PadLatch[port] >> id) & 1);
269          else
270 	  return((PadLatch[2 + 3 * (port ^ 1) + (index - 1)] >> id) & 1);
271 	}
272 	break;
273 
274 	case SNES::Input::DeviceMouse:
275 	{
276 	 int port_type = port;
277 	 assert(port_type < 2);
278 	 switch(id)
279 	 {
280 	  case SNES::Input::MouseX:
281 		return(sats32tos16(MouseXLatch[port]));
282 		break;
283 
284 	  case SNES::Input::MouseY:
285 		return(sats32tos16(MouseYLatch[port]));
286 		break;
287 
288 	  case SNES::Input::MouseLeft:
289 		return((int)(bool)(MouseBLatch[port] & 1));
290 		break;
291 
292 	  case SNES::Input::MouseRight:
293 		return((int)(bool)(MouseBLatch[port] & 2));
294 		break;
295 	 }
296 	}
297 	break;
298  }
299 
300  return(0);
301 }
302 
303 #if 0
304 void MeowFace::init()
305 {
306 
307 
308 }
309 
310 void MeowFace::term()
311 {
312 
313 
314 }
315 #endif
316 
317 #if 0
318 
319 namespace memory {
320   extern MappedRAM cartrom, cartram, cartrtc;
321   extern MappedRAM bsxflash, bsxram, bsxpram;
322   extern MappedRAM stArom, stAram;
323   extern MappedRAM stBrom, stBram;
324   extern MappedRAM gbrom, gbram;
325 };
326 
327 #endif
328 
329 // For loading: Return false on fatal error during loading, or true on success(or file not found)
SaveMemorySub(bool load,const char * extension,SNES::MappedRAM * memoryA,SNES::MappedRAM * memoryB=NULL)330 static bool SaveMemorySub(bool load, const char *extension, SNES::MappedRAM *memoryA, SNES::MappedRAM *memoryB = NULL)
331 {
332  const std::string path = MDFN_MakeFName(MDFNMKF_SAV, 0, extension);
333  std::vector<PtrLengthPair> MemToSave;
334 
335  if(load)
336  {
337   FILE *gp;
338 
339   errno = 0;
340   gp = fopen(path.c_str(), "rb");
341   if(!gp)
342   {
343    ErrnoHolder ene(errno);
344    if(ene.Errno() == ENOENT)
345     return(true);
346 
347    MDFN_PrintError(_("Error opening save file \"%s\": %s"), path.c_str(), ene.StrError());
348    return(false);
349   }
350 
351   if(memoryA && memoryA->size() != 0 && memoryA->size() != -1U)
352   {
353    errno = 0;
354    fread(memoryA->data(), memoryA->size(), 1, gp);
355   }
356 
357   if(memoryB && memoryB->size() != 0 && memoryB->size() != -1U)
358   {
359    errno = 0;
360    fread(memoryB->data(), memoryB->size(), 1, gp);
361   }
362 
363   fclose(gp);
364 
365   return(true);
366  }
367  else
368  {
369   if(memoryA && memoryA->size() != 0 && memoryA->size() != -1U)
370    MemToSave.push_back(PtrLengthPair(memoryA->data(), memoryA->size()));
371 
372   if(memoryB && memoryB->size() != 0 && memoryB->size() != -1U)
373    MemToSave.push_back(PtrLengthPair(memoryB->data(), memoryB->size()));
374 
375   return(MDFN_DumpToFile(path.c_str(), 6, MemToSave));
376  }
377 }
378 
SaveLoadMemory(bool load)379 static bool SaveLoadMemory(bool load)
380 {
381   if(SNES::cartridge.loaded() == false)
382    return(FALSE);
383 
384   bool ret = true;
385 
386   switch(SNES::cartridge.mode())
387   {
388     case SNES::Cartridge::ModeNormal:
389     case SNES::Cartridge::ModeBsxSlotted:
390     {
391       ret &= SaveMemorySub(load, "srm", &SNES::memory::cartram);
392       ret &= SaveMemorySub(load, "rtc", &SNES::memory::cartrtc);
393     }
394     break;
395 
396     case SNES::Cartridge::ModeBsx:
397     {
398       ret &= SaveMemorySub(load, "srm", &SNES::memory::bsxram );
399       ret &= SaveMemorySub(load, "psr", &SNES::memory::bsxpram);
400     }
401     break;
402 
403     case SNES::Cartridge::ModeSufamiTurbo:
404     {
405      ret &= SaveMemorySub(load, "srm", &SNES::memory::stAram, &SNES::memory::stBram);
406     }
407     break;
408 
409     case SNES::Cartridge::ModeSuperGameBoy:
410     {
411      ret &= SaveMemorySub(load, "sav", &SNES::memory::gbram);
412      ret &= SaveMemorySub(load, "rtc", &SNES::memory::gbrtc);
413     }
414     break;
415   }
416 
417  return(ret);
418 }
419 
420 
TestMagic(const char * name,MDFNFILE * fp)421 static bool TestMagic(const char *name, MDFNFILE *fp)
422 {
423  if(strcasecmp(fp->f_ext, "smc") && strcasecmp(fp->f_ext, "swc") && strcasecmp(fp->f_ext, "sfc") && strcasecmp(fp->f_ext, "fig") &&
424         strcasecmp(fp->f_ext, "bs") && strcasecmp(fp->f_ext, "st"))
425  {
426   return(false);
427  }
428 
429  return(true);
430 }
431 
SetupMisc(bool PAL)432 static void SetupMisc(bool PAL)
433 {
434  MDFNGameInfo->fps = PAL ? 838977920 : 1008307711;
435  MDFNGameInfo->MasterClock = MDFN_MASTERCLOCK_FIXED(32040.40);  //MDFN_MASTERCLOCK_FIXED(21477272);     //PAL ? PAL_CPU : NTSC_CPU);
436 
437  {
438   MDFNGameInfo->nominal_width = MDFN_GetSettingB("snes.correct_aspect") ? 292 : 256;
439   MDFNGameInfo->nominal_height = PAL ? 239 : 224;
440   MDFNGameInfo->lcm_height = MDFNGameInfo->nominal_height * 2;
441  }
442 
443  resampler.buffer_size(32040 / 10);
444  //resampler.time_ratio((double)32040.40 / 48000, 0.9965);
445  SoundLastRate = 0;
446 }
447 
LoadSNSF(MDFNFILE * fp)448 static bool LoadSNSF(MDFNFILE *fp)
449 {
450  return(false);
451 }
452 
Cleanup(void)453 static void Cleanup(void)
454 {
455  SNES::memory::cartrom.map(NULL, 0); // So it delete[]s the pointer it took ownership of.
456 
457  if(CustomColorMap)
458  {
459   free(CustomColorMap);
460   CustomColorMap = NULL;
461  }
462 
463  ColorMap.resize(0);
464 }
465 
Load(const char * name,MDFNFILE * fp)466 static int Load(const char *name, MDFNFILE *fp)
467 {
468  bool PAL = FALSE;
469 
470  CycleCounter = 0;
471 
472  try
473  {
474   // Allocate 8MiB of space regardless of actual ROM image size, to prevent malformed or corrupted ROM images
475   // from crashing the bsnes cart loading code.
476 
477   const uint32 header_adjust = (((fp->f_size & 0x7FFF) == 512) ? 512 : 0);
478   uint8 *export_ptr;
479 
480   if((fp->f_size - header_adjust) > (8192 * 1024))
481    throw MDFN_Error(0, _("SNES ROM image is too large."));
482 
483   SNES::system.init(&meowface);
484 
485   //const SNES::Cartridge::Type rom_type = SNES::cartridge.detect_image_type((uint8 *)fp->f_data, fp->size);
486 
487   export_ptr = new uint8[8192 * 1024];
488   memset(export_ptr, 0x00, 8192 * 1024);
489   memcpy(export_ptr, fp->f_data + header_adjust, fp->f_size - header_adjust);
490 
491   SNES::memory::cartrom.map(export_ptr, fp->f_size - header_adjust);
492 
493   SNES::cartridge.load(SNES::Cartridge::ModeNormal);
494 
495   SNES::system.power();
496 
497   PAL = (SNES::system.region() == SNES::System::PAL);
498 
499   SetupMisc(PAL);
500 
501   MultitapEnabled[0] = MDFN_GetSettingB("snes.input.port1.multitap");
502   MultitapEnabled[1] = MDFN_GetSettingB("snes.input.port2.multitap");
503 
504   if(!SaveLoadMemory(true))
505   {
506    Cleanup();
507    return(0);
508   }
509 
510   //printf(" %d %d\n", FSettings.SndRate, resampler.max_write());
511 
512   MDFNMP_Init(1024, (1 << 24) / 1024);
513 
514   MDFNMP_AddRAM(131072, 0x7E << 16, SNES::memory::wram.data());
515 
516   ColorMap.resize(32768);
517 
518   //if(!LoadCPalette(NULL, &CustomColorMap, 32768))
519   //{
520    //Cleanup();
521    //return(0);
522   //}
523  }
524  catch(std::exception &e)
525  {
526   MDFND_PrintError(e.what());
527   Cleanup();
528   return 0;
529  }
530 
531  return(1);
532 }
533 
CloseGame(void)534 static void CloseGame(void)
535 {
536  {
537   SaveLoadMemory(false);
538  }
539  Cleanup();
540 }
541 
Emulate(EmulateSpecStruct * espec)542 static void Emulate(EmulateSpecStruct *espec)
543 {
544  tsurf = espec->surface;
545  tlw = espec->LineWidths;
546  tdr = &espec->DisplayRect;
547 
548  {
549   if(espec->VideoFormatChanged)
550    BuildColorMap(espec->surface->format);
551  }
552 
553  if(SoundLastRate != espec->SoundRate)
554  {
555   double ratio = (double)32040.40 / (espec->SoundRate ? espec->SoundRate : 48000);
556   resampler.time_ratio(ratio, 0.9965);
557   printf("%f, %f\n", ratio, resampler.ratio());
558   SoundLastRate = espec->SoundRate;
559  }
560 
561   MDFNMP_ApplyPeriodicCheats();
562 
563  // Make sure to trash any leftover samples, generated from system.runtosave() in save state saving, if sound is now disabled.
564  if(SoundOn && !espec->SoundBuf)
565  {
566   resampler.clear();
567  }
568 
569  SoundOn = espec->SoundBuf ? true : false;
570 
571  HasPolledThisFrame = false;
572  InProperEmu = TRUE;
573  SNES::system.run_mednafen_custom();
574  tsurf = NULL;
575  tlw = NULL;
576  tdr = NULL;
577  InProperEmu = FALSE;
578 
579  espec->MasterCycles = CycleCounter;
580  CycleCounter = 0;
581 
582  //printf("%d\n", espec->MasterCycles);
583 
584  if(espec->SoundBuf)
585   espec->SoundBufSize = resampler.read(espec->SoundBuf, resampler.avail()) >> 1;
586 
587  MDFNGameInfo->mouse_sensitivity = MDFN_GetSettingF("snes.mouse_sensitivity");
588 }
589 
StateAction(StateMem * sm,int load,int data_only)590 static int StateAction(StateMem *sm, int load, int data_only)
591 {
592  //if(!SNES::Cartridge::saveStatesSupported())
593   //return(0);
594 
595  if(load)
596  {
597   uint32 length;
598   uint8 *ptr;
599 
600   SFORMAT StateLengthCat[] =
601   {
602    SFVARN(length, "length"),
603    SFEND
604   };
605 
606   if(!MDFNSS_StateAction(sm, 1, data_only, StateLengthCat, "LEN"))
607    return(0);
608 
609   ptr = (uint8 *)calloc(1, length);
610 
611   SFORMAT StateRegs[] =
612   {
613    SFARRAYN(ptr, length, "OmniCat"),
614    SFARRAY16(PadLatch, 8),
615    SFARRAY16(MouseXLatch, 2),
616    SFARRAY16(MouseYLatch, 2),
617    SFARRAY(MouseBLatch, 2),
618    SFEND
619   };
620 
621   if(!MDFNSS_StateAction(sm, 1, data_only, StateRegs, "DATA"))
622   {
623    free(ptr);
624    return(0);
625   }
626 
627   serializer state(ptr, length);
628   int result;
629 
630   result = SNES::system.unserialize(state);
631 
632   free(ptr);
633   return(result);
634  }
635  else // save:
636  {
637   uint32 length;
638 
639   if(SNES::scheduler.sync != SNES::Scheduler::SyncAll)
640    SNES::system.runtosave();
641 
642   serializer state = SNES::system.serialize();
643 
644   length = state.size();
645 
646   SFORMAT StateLengthCat[] =
647   {
648    SFVARN(length, "length"),
649    SFEND
650   };
651 
652   if(!MDFNSS_StateAction(sm, 0, data_only, StateLengthCat, "LEN"))
653    return(0);
654 
655   uint8 *ptr = const_cast<uint8 *>(state.data());
656 
657   SFORMAT StateRegs[] =
658   {
659    SFARRAYN(ptr, length, "OmniCat"),
660    SFARRAY16(PadLatch, 8),
661    SFARRAY16(MouseXLatch, 2),
662    SFARRAY16(MouseYLatch, 2),
663    SFARRAY(MouseBLatch, 2),
664    SFEND
665   };
666 
667   if(!MDFNSS_StateAction(sm, 0, data_only, StateRegs, "DATA"))
668    return(0);
669 
670   return(1);
671  }
672 
673 }
674 
675 struct StrToBSIT_t
676 {
677  const char *str;
678  const int id;
679 };
680 
681 static const StrToBSIT_t StrToBSIT[] =
682 {
683  { "none",   	SNES::Input::DeviceNone },
684  { "gamepad",   SNES::Input::DeviceJoypad },
685  { "multitap",  SNES::Input::DeviceMultitap },
686  { "mouse",   	SNES::Input::DeviceMouse },
687  { "superscope",   SNES::Input::DeviceSuperScope },
688  { "justifier",   SNES::Input::DeviceJustifier },
689  { "justifiers",   SNES::Input::DeviceJustifiers },
690  { NULL,	-1	},
691 };
692 
693 
SetInput(int port,const char * type,void * ptr)694 static void SetInput(int port, const char *type, void *ptr)
695 {
696  assert(port >= 0 && port < 8);
697 
698  if(port < 2)
699  {
700   const StrToBSIT_t *sb = StrToBSIT;
701   int id = -1;
702 
703   if(MultitapEnabled[port] && !strcmp(type, "gamepad"))
704    type = "multitap";
705 
706   while(sb->str && id == -1)
707   {
708    if(!strcmp(type, sb->str))
709     id = sb->id;
710    sb++;
711   }
712   assert(id != -1);
713 
714   InputType[port] = id;
715 
716   SNES::input.port_set_device(port, id);
717 
718 #if 0
719   switch(config().input.port1) { default:
720     case ControllerPort1::None: mapper().port1 = 0; break;
721     case ControllerPort1::Gamepad: mapper().port1 = &Controllers::gamepad1; break;
722     case ControllerPort1::Asciipad: mapper().port1 = &Controllers::asciipad1; break;
723     case ControllerPort1::Multitap: mapper().port1 = &Controllers::multitap1; break;
724     case ControllerPort1::Mouse: mapper().port1 = &Controllers::mouse1; break;
725   }
726 
727   switch(config().input.port2) { default:
728     case ControllerPort2::None: mapper().port2 = 0; break;
729     case ControllerPort2::Gamepad: mapper().port2 = &Controllers::gamepad2; break;
730     case ControllerPort2::Asciipad: mapper().port2 = &Controllers::asciipad2; break;
731     case ControllerPort2::Multitap: mapper().port2 = &Controllers::multitap2; break;
732     case ControllerPort2::Mouse: mapper().port2 = &Controllers::mouse2; break;
733     case ControllerPort2::SuperScope: mapper().port2 = &Controllers::superscope; break;
734     case ControllerPort2::Justifier: mapper().port2 = &Controllers::justifier1; break;
735     case ControllerPort2::Justifiers: mapper().port2 = &Controllers::justifiers; break;
736   }
737 #endif
738 
739  }
740 
741 
742  InputPtr[port] = (uint8 *)ptr;
743 }
744 
SetLayerEnableMask(uint64 mask)745 static void SetLayerEnableMask(uint64 mask)
746 {
747 
748 }
749 
750 
DoSimpleCommand(int cmd)751 static void DoSimpleCommand(int cmd)
752 {
753  switch(cmd)
754  {
755   case MDFN_MSC_RESET: SNES::system.reset(); break;
756   case MDFN_MSC_POWER: SNES::system.power(); break;
757  }
758 }
759 
760 static const InputDeviceInputInfoStruct GamepadIDII[] =
761 {
762  { "b", "B (center, lower)", 7, IDIT_BUTTON_CAN_RAPID, NULL },
763  { "y", "Y (left)", 6, IDIT_BUTTON_CAN_RAPID, NULL },
764  { "select", "SELECT", 4, IDIT_BUTTON, NULL },
765  { "start", "START", 5, IDIT_BUTTON, NULL },
766  { "up", "UP ↑", 0, IDIT_BUTTON, "down" },
767  { "down", "DOWN ↓", 1, IDIT_BUTTON, "up" },
768  { "left", "LEFT ←", 2, IDIT_BUTTON, "right" },
769  { "right", "RIGHT →", 3, IDIT_BUTTON, "left" },
770  { "a", "A (right)", 9, IDIT_BUTTON_CAN_RAPID, NULL },
771  { "x", "X (center, upper)", 8, IDIT_BUTTON_CAN_RAPID, NULL },
772  { "l", "Left Shoulder", 10, IDIT_BUTTON, NULL },
773  { "r", "Right Shoulder", 11, IDIT_BUTTON, NULL },
774 };
775 
776 static const InputDeviceInputInfoStruct MouseIDII[0x4] =
777 {
778  { "x_axis", "X Axis", -1, IDIT_X_AXIS_REL },
779  { "y_axis", "Y Axis", -1, IDIT_Y_AXIS_REL },
780  { "left", "Left Button", 0, IDIT_BUTTON, NULL },
781  { "right", "Right Button", 1, IDIT_BUTTON, NULL },
782 };
783 
784 
785 static InputDeviceInfoStruct InputDeviceInfoSNESPort[] =
786 {
787  // None
788  {
789   "none",
790   "none",
791   NULL,
792   NULL,
793   0,
794   NULL
795  },
796 
797  // Gamepad
798  {
799   "gamepad",
800   "Gamepad",
801   NULL,
802   NULL,
803   sizeof(GamepadIDII) / sizeof(InputDeviceInputInfoStruct),
804   GamepadIDII,
805  },
806 
807  // Mouse
808  {
809   "mouse",
810   "Mouse",
811   NULL,
812   NULL,
813   sizeof(MouseIDII) / sizeof(InputDeviceInputInfoStruct),
814   MouseIDII,
815  },
816 
817 };
818 
819 
820 static InputDeviceInfoStruct InputDeviceInfoTapPort[] =
821 {
822  // Gamepad
823  {
824   "gamepad",
825   "Gamepad",
826   NULL,
827   NULL,
828   sizeof(GamepadIDII) / sizeof(InputDeviceInputInfoStruct),
829   GamepadIDII,
830  },
831 };
832 
833 
834 static const InputPortInfoStruct PortInfo[] =
835 {
836  { "port1", "Port 1/1A", sizeof(InputDeviceInfoSNESPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoSNESPort, "gamepad" },
837  { "port2", "Port 2/2A", sizeof(InputDeviceInfoSNESPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoSNESPort, "gamepad" },
838  { "port3", "Port 2B", sizeof(InputDeviceInfoTapPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoTapPort, "gamepad" },
839  { "port4", "Port 2C", sizeof(InputDeviceInfoTapPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoTapPort, "gamepad" },
840  { "port5", "Port 2D", sizeof(InputDeviceInfoTapPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoTapPort, "gamepad" },
841  { "port6", "Port 1B", sizeof(InputDeviceInfoTapPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoTapPort, "gamepad" },
842  { "port7", "Port 1C", sizeof(InputDeviceInfoTapPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoTapPort, "gamepad" },
843  { "port8", "Port 1D", sizeof(InputDeviceInfoTapPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoTapPort, "gamepad" },
844 };
845 
846 static InputInfoStruct SNESInputInfo =
847 {
848  sizeof(PortInfo) / sizeof(InputPortInfoStruct),
849  PortInfo
850 };
851 
852 static const MDFNSetting SNESSettings[] =
853 {
854  { "snes.input.port1.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, "Enable multitap on SNES port 1.", NULL, MDFNST_BOOL, "0", NULL, NULL },
855  { "snes.input.port2.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, "Enable multitap on SNES port 2.", NULL, MDFNST_BOOL, "0", NULL, NULL },
856 
857  { "snes.mouse_sensitivity", MDFNSF_NOFLAGS, "Emulated mouse sensitivity.", NULL, MDFNST_FLOAT, "0.50", NULL, NULL, NULL },
858 
859  { "snes.correct_aspect", MDFNSF_CAT_VIDEO, "Correct the aspect ratio.", "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" },
860 
861  { NULL }
862 };
863 
864 static const FileExtensionSpecStruct KnownExtensions[] =
865 {
866  { ".smc", "Super Magicom ROM Image" },
867  { ".swc", "Super Wildcard ROM Image" },
868  { ".sfc", "Cartridge ROM Image" },
869  { ".fig", "Cartridge ROM Image" },
870 
871  { ".bs", "BS-X EEPROM Image" },
872  { ".st", "Sufami Turbo Cartridge ROM Image" },
873 
874  { NULL, NULL }
875 };
876 
877 MDFNGI EmulatedSNES =
878 {
879  "snes",
880  "Super Nintendo Entertainment System/Super Famicom",
881  KnownExtensions,
882  MODPRIO_INTERNAL_HIGH,
883  NULL,						// Debugger
884  &SNESInputInfo,
885  Load,
886  TestMagic,
887  NULL,
888  NULL,
889  CloseGame,
890  SetLayerEnableMask,
891  NULL,	// Layer names, null-delimited
892  NULL,
893  NULL,
894  NULL, //InstallReadPatch,
895  NULL, //RemoveReadPatches,
896  NULL, //MemRead,
897  true,
898  StateAction,
899  Emulate,
900  SetInput,
901  DoSimpleCommand,
902  SNESSettings,
903  0,
904  0,
905  FALSE, // Multires
906 
907  512,   // lcm_width
908  480,   // lcm_height           (replaced in game load)
909  NULL,  // Dummy
910 
911  256,   // Nominal width	(replaced in game load)
912  240,   // Nominal height	(replaced in game load)
913 
914  512,	// Framebuffer width
915  512,	// Framebuffer height
916 
917  2,     // Number of output sound channels
918 };
919 
920 
921