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