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