1 #include <stdarg.h>
2 #include <string.h>
3 #include <errno.h>
4 
5 #include "mednafen/mednafen.h"
6 #include "mednafen/mempatcher.h"
7 #include "mednafen/git.h"
8 #include "mednafen/general.h"
9 #include "mednafen/md5.h"
10 #include "mednafen/gba/GBA.h"
11 #include "mednafen/gba/Globals.h"
12 #include "libretro.h"
13 
14 static MDFNGI *game;
15 MDFNGI *MDFNGameInfo = NULL;
16 
17 struct retro_perf_callback perf_cb;
18 retro_get_cpu_features_t perf_get_cpu_features_cb = NULL;
19 retro_log_printf_t log_cb;
20 static retro_video_refresh_t video_cb;
21 static retro_audio_sample_t audio_cb;
22 static retro_audio_sample_batch_t audio_batch_cb;
23 static retro_environment_t environ_cb;
24 static retro_input_poll_t input_poll_cb;
25 static retro_input_state_t input_state_cb;
26 static retro_set_rumble_state_t rumble_cb;
27 
28 static bool rumble_state = false;
29 static bool rumble_isrunning = false;
30 static int rumble = 0;
31 static const int rumble_frames = 4; // delays turning off rumble for N frames;
32 
33 static uint8 sensorDarkness = 0xE8;
34 static uint8 sensorDarknessLevel = 0;
35 
36 static double last_sound_rate;
37 
38 static MDFN_PixelFormat last_pixel_format;
39 
40 static MDFN_Surface *surf;
41 
42 static bool failed_init;
43 
44 static void hookup_ports(bool force);
45 
46 static bool initial_ports_hookup = false;
47 
48 std::string retro_base_directory;
49 std::string retro_base_name;
50 std::string retro_save_directory;
51 
52 /* Workaround for broken-by-design GBA save semantics. */
53 uint8_t libretro_save_buf[0x20000 + 0x2000];
54 static unsigned libretro_save_size = sizeof(libretro_save_buf);
55 bool use_mednafen_save_method = false;
56 
set_basename(const char * path)57 static void set_basename(const char *path)
58 {
59    const char *base = strrchr(path, '/');
60    if (!base)
61       base = strrchr(path, '\\');
62 
63    if (base)
64       retro_base_name = base + 1;
65    else
66       retro_base_name = path;
67 
68    retro_base_name = retro_base_name.substr(0, retro_base_name.find_last_of('.'));
69 }
70 
71 #define MEDNAFEN_CORE_NAME_MODULE "gba"
72 #define MEDNAFEN_CORE_NAME "Beetle GBA"
73 #define MEDNAFEN_CORE_VERSION "v0.9.36"
74 #define MEDNAFEN_CORE_EXTENSIONS "gba|agb|bin"
75 #define MEDNAFEN_CORE_TIMING_FPS 59.73
76 #define MEDNAFEN_CORE_GEOMETRY_BASE_W (game->nominal_width)
77 #define MEDNAFEN_CORE_GEOMETRY_BASE_H (game->nominal_height)
78 #define MEDNAFEN_CORE_GEOMETRY_MAX_W 240
79 #define MEDNAFEN_CORE_GEOMETRY_MAX_H 160
80 #define MEDNAFEN_CORE_GEOMETRY_ASPECT_RATIO (3.0 / 2.0)
81 #define FB_WIDTH 240
82 #define FB_HEIGHT 160
83 
84 #define FB_MAX_HEIGHT FB_HEIGHT
85 
scan_area(const uint8_t * data,unsigned size)86 static bool scan_area(const uint8_t *data, unsigned size)
87 {
88    for (unsigned i = 0; i < size; i++)
89       if (data[i] != 0xff)
90          return true;
91 
92    return false;
93 }
94 
adjust_save_ram()95 static void adjust_save_ram()
96 {
97    if (scan_area(libretro_save_buf, 512) &&
98          !scan_area(libretro_save_buf + 512, sizeof(libretro_save_buf) - 512))
99    {
100       libretro_save_size = 512;
101       if (log_cb)
102          log_cb(RETRO_LOG_INFO, "Detecting EEprom 8kbit\n");
103    }
104    else if (scan_area(libretro_save_buf, 0x2000) &&
105          !scan_area(libretro_save_buf + 0x2000, sizeof(libretro_save_buf) - 0x2000))
106    {
107       libretro_save_size = 0x2000;
108       if (log_cb)
109          log_cb(RETRO_LOG_INFO, "Detecting EEprom 64kbit\n");
110    }
111 
112    else if (scan_area(libretro_save_buf, 0x10000) &&
113          !scan_area(libretro_save_buf + 0x10000, sizeof(libretro_save_buf) - 0x10000))
114    {
115       libretro_save_size = 0x10000;
116       if (log_cb)
117          log_cb(RETRO_LOG_INFO, "Detecting Flash 512kbit\n");
118    }
119    else if (scan_area(libretro_save_buf, 0x20000) &&
120          !scan_area(libretro_save_buf + 0x20000, sizeof(libretro_save_buf) - 0x20000))
121    {
122       libretro_save_size = 0x20000;
123       if (log_cb)
124          log_cb(RETRO_LOG_INFO, "Detecting Flash 1Mbit\n");
125    }
126    else if (log_cb)
127       log_cb(RETRO_LOG_INFO, "Did not detect any particular SRAM type.\n");
128 
129    /*if (libretro_save_size == 512 || libretro_save_size == 0x2000)
130       eepromData = libretro_save_buf;
131    else if (libretro_save_size == 0x10000 || libretro_save_size == 0x20000)
132       flashSaveMemory = libretro_save_buf;*/
133 }
134 
check_system_specs(void)135 static void check_system_specs(void)
136 {
137    unsigned level = 0;
138    environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
139 }
140 
retro_init(void)141 void retro_init(void)
142 {
143    memset(libretro_save_buf, 0xFF, sizeof(libretro_save_buf));
144    struct retro_log_callback log;
145    if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
146       log_cb = log.log;
147    else
148       log_cb = NULL;
149 
150    const char *dir = NULL;
151 
152    if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir)
153    {
154       retro_base_directory = dir;
155       // Make sure that we don't have any lingering slashes, etc, as they break Windows.
156       size_t last = retro_base_directory.find_last_not_of("/\\");
157       if (last != std::string::npos)
158          last++;
159 
160       retro_base_directory = retro_base_directory.substr(0, last);
161    }
162    else
163    {
164       /* TODO: Add proper fallback */
165       if (log_cb)
166          log_cb(RETRO_LOG_WARN, "System directory is not defined. Fallback on using same dir as ROM for system directory later ...\n");
167       failed_init = true;
168    }
169 
170    if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir) && dir)
171    {
172       // If save directory is defined use it, otherwise use system directory
173       retro_save_directory = *dir ? dir : retro_base_directory;
174       // Make sure that we don't have any lingering slashes, etc, as they break Windows.
175       size_t last = retro_save_directory.find_last_not_of("/\\");
176       if (last != std::string::npos)
177          last++;
178 
179       retro_save_directory = retro_save_directory.substr(0, last);
180    }
181    else
182    {
183       /* TODO: Add proper fallback */
184       if (log_cb)
185          log_cb(RETRO_LOG_WARN, "Save directory is not defined. Fallback on using SYSTEM directory ...\n");
186       retro_save_directory = retro_base_directory;
187    }
188 
189 #if defined(WANT_16BPP) && defined(FRONTEND_SUPPORTS_RGB565)
190    enum retro_pixel_format rgb565 = RETRO_PIXEL_FORMAT_RGB565;
191    if (environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb565) && log_cb)
192       log_cb(RETRO_LOG_INFO, "Frontend supports RGB565 - will use that instead of XRGB1555.\n");
193 #endif
194 
195    if (environ_cb(RETRO_ENVIRONMENT_GET_PERF_INTERFACE, &perf_cb))
196       perf_get_cpu_features_cb = perf_cb.get_cpu_features;
197    else
198       perf_get_cpu_features_cb = NULL;
199 
200    retro_rumble_interface rumble;
201    if (environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble))
202       rumble_cb = rumble.set_rumble_state;
203    else
204       rumble_cb = NULL;
205 
206    check_system_specs();
207 }
208 
retro_reset(void)209 void retro_reset(void)
210 {
211    DoSimpleCommand(MDFN_MSC_RESET);
212 }
213 
retro_load_game_special(unsigned,const struct retro_game_info *,size_t)214 bool retro_load_game_special(unsigned, const struct retro_game_info *, size_t)
215 {
216    return false;
217 }
218 
check_variables(bool startup)219 static void check_variables(bool startup)
220 {
221    struct retro_variable var = {0};
222 
223    var.key = "gba_hle";
224 
225    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && startup)
226    {
227       if (strcmp(var.value, "enabled") == 0)
228          setting_gba_hle = 1;
229       else if (strcmp(var.value, "disabled") == 0)
230          setting_gba_hle = 0;
231    }
232 
233    var.key = "gba_use_mednafen_save_method";
234 
235    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && startup)
236    {
237       if (strcmp(var.value, "mednafen") == 0)
238          use_mednafen_save_method = true;
239       else if (strcmp(var.value, "libretro") == 0)
240          use_mednafen_save_method = false;
241    }
242 }
243 
244 #define MAX_PLAYERS 1
245 #define MAX_BUTTONS 11
246 static uint16_t input_buf;
247 
hookup_ports(bool force)248 static void hookup_ports(bool force)
249 {
250    if (initial_ports_hookup && !force)
251       return;
252 
253    // Possible endian bug ...
254    SetInput(0, "gamepad", &input_buf);
255 
256    initial_ports_hookup = true;
257 }
258 
retro_load_game(const struct retro_game_info * info)259 bool retro_load_game(const struct retro_game_info *info)
260 {
261    if (!info || failed_init)
262       return false;
263 
264    struct retro_input_descriptor desc[] = {
265       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
266       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
267       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
268       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
269       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "B" },
270       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "A" },
271       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L" },
272       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R" },
273       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
274       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
275       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,     "Solar Level Decrease" },
276       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,     "Solar Level Increase" },
277 
278       { 0 },
279    };
280 
281    environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
282 
283 #ifdef WANT_32BPP
284    enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
285    if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
286    {
287       if (log_cb)
288          log_cb(RETRO_LOG_ERROR, "Pixel format XRGB8888 not supported by platform, cannot use %s.\n", MEDNAFEN_CORE_NAME);
289       return false;
290    }
291 #endif
292 
293    set_basename(info->path);
294 
295    check_variables(true);
296 
297    game = MDFNI_LoadGame(MEDNAFEN_CORE_NAME_MODULE, (const uint8_t *)info->data, info->size);
298    if (!game)
299       return false;
300 
301    MDFN_PixelFormat pix_fmt(MDFN_COLORSPACE_RGB, 16, 8, 0, 24);
302    last_pixel_format = MDFN_PixelFormat();
303 
304    surf = new MDFN_Surface(NULL, FB_WIDTH, FB_HEIGHT, FB_WIDTH, pix_fmt);
305 
306    hookup_ports(true);
307 
308    struct retro_memory_descriptor descs[7];
309    struct retro_memory_map retromap;
310 
311    memset(descs, 0, sizeof(descs));
312 
313    descs[0].ptr    = internalRAM;    // Internal working RAM
314    descs[0].start  = 0x03000000;
315    descs[0].len    = 0x8000;
316    descs[0].select = 0xFF000000;
317 
318    descs[1].ptr    = workRAM;        // Working RAM
319    descs[1].start  = 0x02000000;
320    descs[1].len    = 0x40000;
321    descs[1].select = 0xFF000000;
322 
323    // TODO: if SRAM is flash, use start=0 addrspace="S" instead
324    descs[2].ptr    = flashSaveMemory;  // Save RAM
325    descs[2].start  = 0x0E000000;
326    descs[2].len    = flashSize;
327    descs[2].select = 0;
328 
329    descs[3].ptr    = vram;           // VRAM
330    descs[3].start  = 0x06000000;
331    descs[3].len    = 0x20000;
332    descs[3].select = 0xFF000000;
333 
334    descs[4].ptr    = paletteRAM;     // Palettes
335    descs[4].start  = 0x05000000;
336    descs[4].len    = 0x400;
337    descs[4].select = 0xFF000000;
338 
339    descs[5].ptr    = oam;            // OAM
340    descs[5].start  = 0x07000000;
341    descs[5].len    = 0x400;
342    descs[5].select = 0xFF000000;
343 
344    descs[6].ptr    = ioMem;          // I/O
345    descs[6].start  = 0x04000000;
346    descs[6].len    = 0x400;
347    descs[6].select = 0;
348 
349    retromap.descriptors       = descs;
350    retromap.num_descriptors   = sizeof(descs) / sizeof(*descs);
351 
352    environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &retromap);
353 
354    bool retroarchievement = true;
355    environ_cb(RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS, &retroarchievement);
356 
357    return game;
358 }
359 
retro_unload_game()360 void retro_unload_game()
361 {
362    if (!game)
363       return;
364 
365    MDFNI_CloseGame();
366 }
367 
systemUpdateSolarSensor(int v)368 static void systemUpdateSolarSensor(int v)
369 {
370     int value = 0;
371     switch (v) {
372     case 1:  value = 0x06; break;
373     case 2:  value = 0x0E; break;
374     case 3:  value = 0x18; break;
375     case 4:  value = 0x20; break;
376     case 5:  value = 0x28; break;
377     case 6:  value = 0x38; break;
378     case 7:  value = 0x48; break;
379     case 8:  value = 0x60; break;
380     case 9:  value = 0x78; break;
381     case 10: value = 0x98; break;
382     default: break;
383     }
384 
385     sensorDarkness = 0xE8 - value;
386 }
387 
update_input(void)388 static void update_input(void)
389 {
390    input_buf = 0;
391    size_t map_size = 10;
392    static unsigned map[] = {
393       RETRO_DEVICE_ID_JOYPAD_A, //A button
394       RETRO_DEVICE_ID_JOYPAD_B, //B button
395       RETRO_DEVICE_ID_JOYPAD_SELECT,
396       RETRO_DEVICE_ID_JOYPAD_START,
397       RETRO_DEVICE_ID_JOYPAD_RIGHT,
398       RETRO_DEVICE_ID_JOYPAD_LEFT,
399       RETRO_DEVICE_ID_JOYPAD_UP,
400       RETRO_DEVICE_ID_JOYPAD_DOWN,
401       RETRO_DEVICE_ID_JOYPAD_R,
402       RETRO_DEVICE_ID_JOYPAD_L,
403    };
404 
405    for (unsigned i = 0; i < map_size; i++)
406       input_buf |= map[i] != -1u &&
407          input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, map[i]) ? (1 << i) : 0;
408 
409 #ifdef MSB_FIRST
410    union {
411       uint8_t b[2];
412       uint16_t s;
413    } u;
414    u.s = input_buf;
415    input_buf = u.b[0] | u.b[1] << 8;
416 #endif
417 
418    if ((hardware & SENSOR_RUMBLE) && rumble_cb)
419    {
420       // Do rumble frames
421       if (rumble_state == true && rumble_isrunning == false)
422       {
423          // Only do rumble callback if not running already
424          rumble_cb(0, RETRO_RUMBLE_WEAK, 0xffff);
425          rumble_cb(0, RETRO_RUMBLE_STRONG, 0xffff);
426          rumble_isrunning = true;
427          rumble = rumble_frames;
428       }
429       else if (rumble && rumble_isrunning)
430       {
431          // Make sure we disable rumble after Nth frames
432          rumble--;
433          if (!rumble)
434          {
435             rumble_cb(0, RETRO_RUMBLE_WEAK, 0);
436             rumble_cb(0, RETRO_RUMBLE_STRONG, 0);
437             rumble_isrunning = false;
438             rumble_state = false;
439          }
440       }
441    }
442 
443    if (hardware & SENSOR_SOLAR) {
444       static bool buttonpressed = false;
445       if (buttonpressed) {
446          buttonpressed = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2) ||
447             input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2);
448       } else {
449          if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2)) {
450             sensorDarknessLevel++;
451             if (sensorDarknessLevel > 10)
452                sensorDarknessLevel = 10;
453             systemUpdateSolarSensor(sensorDarknessLevel);
454             buttonpressed = true;
455          } else if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2)) {
456             if (sensorDarknessLevel)
457                sensorDarknessLevel--;
458             systemUpdateSolarSensor(sensorDarknessLevel);
459             buttonpressed = true;
460          }
461       }
462    }
463 }
464 
retro_run()465 void retro_run()
466 {
467    input_poll_cb();
468 
469    update_input();
470 
471    static int16_t sound_buf[0x10000];
472    static MDFN_Rect rects[FB_MAX_HEIGHT];
473    rects[0].w = ~0;
474 
475    EmulateSpecStruct spec = {0};
476    spec.surface = surf;
477    spec.SoundRate = 44100;
478    spec.SoundBuf = sound_buf;
479    spec.LineWidths = rects;
480    spec.SoundBufMaxSize = sizeof(sound_buf) / 2;
481    spec.SoundVolume = 1.0;
482    spec.soundmultiplier = 1.0;
483    spec.SoundBufSize = 0;
484    spec.VideoFormatChanged = false;
485    spec.SoundFormatChanged = false;
486 
487    if (memcmp(&last_pixel_format, &spec.surface->format, sizeof(MDFN_PixelFormat)))
488    {
489       spec.VideoFormatChanged = true;
490 
491       last_pixel_format = spec.surface->format;
492    }
493 
494    if (spec.SoundRate != last_sound_rate)
495    {
496       spec.SoundFormatChanged = true;
497       last_sound_rate = spec.SoundRate;
498    }
499 
500    Emulate(&spec);
501 
502    unsigned width  = spec.DisplayRect.w;
503    unsigned height = spec.DisplayRect.h;
504 
505 #if defined(WANT_32BPP)
506    const uint32_t *pix = surf->pixels;
507    video_cb(pix, width, height, FB_WIDTH << 2);
508 #elif defined(WANT_16BPP)
509    const uint16_t *pix = surf->pixels16;
510    video_cb(pix, width, height, FB_WIDTH << 1);
511 #endif
512 
513    audio_batch_cb(spec.SoundBuf, spec.SoundBufSize);
514 
515    bool updated = false;
516    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
517      check_variables(false);
518 }
519 
retro_get_system_info(struct retro_system_info * info)520 void retro_get_system_info(struct retro_system_info *info)
521 {
522    memset(info, 0, sizeof(*info));
523    info->library_name     = MEDNAFEN_CORE_NAME;
524 #ifndef GIT_VERSION
525 #define GIT_VERSION ""
526 #endif
527    info->library_version  = MEDNAFEN_CORE_VERSION GIT_VERSION;
528    info->need_fullpath    = false;
529    info->valid_extensions = MEDNAFEN_CORE_EXTENSIONS;
530    info->block_extract    = false;
531 }
532 
retro_get_system_av_info(struct retro_system_av_info * info)533 void retro_get_system_av_info(struct retro_system_av_info *info)
534 {
535    memset(info, 0, sizeof(*info));
536    info->timing.fps            = MEDNAFEN_CORE_TIMING_FPS;
537    info->timing.sample_rate    = 44100;
538    info->geometry.base_width   = MEDNAFEN_CORE_GEOMETRY_BASE_W;
539    info->geometry.base_height  = MEDNAFEN_CORE_GEOMETRY_BASE_H;
540    info->geometry.max_width    = MEDNAFEN_CORE_GEOMETRY_MAX_W;
541    info->geometry.max_height   = MEDNAFEN_CORE_GEOMETRY_MAX_H;
542    info->geometry.aspect_ratio = MEDNAFEN_CORE_GEOMETRY_ASPECT_RATIO;
543 }
544 
retro_deinit()545 void retro_deinit()
546 {
547    delete surf;
548    surf = NULL;
549 }
550 
retro_get_region(void)551 unsigned retro_get_region(void)
552 {
553    return RETRO_REGION_NTSC; // FIXME: Regions for other cores.
554 }
555 
retro_api_version(void)556 unsigned retro_api_version(void)
557 {
558    return RETRO_API_VERSION;
559 }
560 
retro_set_controller_port_device(unsigned in_port,unsigned device)561 void retro_set_controller_port_device(unsigned in_port, unsigned device)
562 {
563 }
564 
retro_set_environment(retro_environment_t cb)565 void retro_set_environment(retro_environment_t cb)
566 {
567    environ_cb = cb;
568 
569    static const struct retro_variable vars[] = {
570       { "gba_hle", "HLE bios emulation (Restart); enabled|disabled" },
571       { "gba_use_mednafen_save_method", "Save method (Restart); mednafen|libretro" },
572       { NULL, NULL },
573    };
574    cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars);
575 }
576 
retro_set_audio_sample(retro_audio_sample_t cb)577 void retro_set_audio_sample(retro_audio_sample_t cb)
578 {
579    audio_cb = cb;
580 }
581 
retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)582 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
583 {
584    audio_batch_cb = cb;
585 }
586 
retro_set_input_poll(retro_input_poll_t cb)587 void retro_set_input_poll(retro_input_poll_t cb)
588 {
589    input_poll_cb = cb;
590 }
591 
retro_set_input_state(retro_input_state_t cb)592 void retro_set_input_state(retro_input_state_t cb)
593 {
594    input_state_cb = cb;
595 }
596 
retro_set_video_refresh(retro_video_refresh_t cb)597 void retro_set_video_refresh(retro_video_refresh_t cb)
598 {
599    video_cb = cb;
600 }
601 
602 static size_t serialize_size;
603 
retro_serialize_size(void)604 size_t retro_serialize_size(void)
605 {
606    StateMem st;
607 
608    st.data           = NULL;
609    st.loc            = 0;
610    st.len            = 0;
611    st.malloced       = 0;
612    st.initial_malloc = 0;
613 
614    if (!MDFNSS_SaveSM(&st, 0, 0, NULL, NULL, NULL))
615       return 0;
616 
617    free(st.data);
618 
619    return serialize_size = st.len;
620 }
621 
retro_serialize(void * data,size_t size)622 bool retro_serialize(void *data, size_t size)
623 {
624    StateMem st;
625    bool ret          = false;
626    uint8_t *_dat     = (uint8_t*)malloc(size);
627 
628    if (!_dat)
629       return false;
630 
631    /* Mednafen can realloc the buffer so we need to ensure this is safe. */
632    st.data           = _dat;
633    st.loc            = 0;
634    st.len            = 0;
635    st.malloced       = size;
636    st.initial_malloc = 0;
637 
638    ret = MDFNSS_SaveSM(&st, 0, 0, NULL, NULL, NULL);
639 
640    memcpy(data, st.data, size);
641    free(st.data);
642 
643    return ret;
644 }
645 
retro_unserialize(const void * data,size_t size)646 bool retro_unserialize(const void *data, size_t size)
647 {
648    StateMem st;
649    memset(&st, 0, sizeof(st));
650    st.data = (uint8_t*)data;
651    st.len  = size;
652 
653    return MDFNSS_LoadSM(&st, 0, 0);
654 }
655 
retro_get_memory_data(unsigned type)656 void *retro_get_memory_data(unsigned type)
657 {
658    if (type == RETRO_MEMORY_SAVE_RAM)
659    {
660       if (!use_mednafen_save_method)
661          return libretro_save_buf;
662    }
663    else if ( type == RETRO_MEMORY_SYSTEM_RAM )
664    {
665       return workRAM ;
666    }
667 
668    return NULL;
669 }
670 
retro_get_memory_size(unsigned type)671 size_t retro_get_memory_size(unsigned type)
672 {
673    if (type == RETRO_MEMORY_SAVE_RAM)
674    {
675       if (!use_mednafen_save_method)
676          return libretro_save_size;
677    }
678    else if ( type == RETRO_MEMORY_SYSTEM_RAM )
679    {
680       return 0x40000 ;
681    }
682 
683    return 0;
684 }
685 
retro_cheat_reset(void)686 void retro_cheat_reset(void)
687 {}
688 
retro_cheat_set(unsigned,bool,const char *)689 void retro_cheat_set(unsigned, bool, const char *)
690 {}
691 
692 #ifdef _WIN32
sanitize_path(std::string & path)693 static void sanitize_path(std::string &path)
694 {
695    size_t size = path.size();
696    for (size_t i = 0; i < size; i++)
697       if (path[i] == '/')
698          path[i] = '\\';
699 }
700 #endif
701 
702 // Use a simpler approach to make sure that things go right for libretro.
MDFN_MakeFName(MakeFName_Type type,int id1,const char * cd1)703 std::string MDFN_MakeFName(MakeFName_Type type, int id1, const char *cd1)
704 {
705    char slash;
706 #ifdef _WIN32
707    slash = '\\';
708 #else
709    slash = '/';
710 #endif
711    std::string ret;
712    switch (type)
713    {
714       case MDFNMKF_SAV:
715          ret = retro_save_directory +slash + retro_base_name +
716             std::string(".") +
717 #ifndef _XBOX
718          md5_context::asciistr(MDFNGameInfo->MD5, 0) + std::string(".") +
719 #endif
720             std::string(cd1);
721          break;
722       case MDFNMKF_FIRMWARE:
723          ret = retro_base_directory + slash + std::string(cd1);
724 #ifdef _WIN32
725          sanitize_path(ret); // Because Windows path handling is mongoloid.
726 #endif
727          break;
728       default:
729          break;
730    }
731 
732    if (log_cb)
733       log_cb(RETRO_LOG_INFO, "MDFN_MakeFName: %s\n\n", ret.c_str());
734    return ret;
735 }
736 
MDFND_DispMessage(unsigned char * str)737 void MDFND_DispMessage(unsigned char *str)
738 {
739    if (log_cb)
740       log_cb(RETRO_LOG_INFO, "%s\n", str);
741 }
742 
MDFND_Message(const char * str)743 void MDFND_Message(const char *str)
744 {
745    if (log_cb)
746       log_cb(RETRO_LOG_INFO, "%s", str);
747 }
748 
MDFND_PrintError(const char * err)749 void MDFND_PrintError(const char* err)
750 {
751    if (log_cb)
752       log_cb(RETRO_LOG_ERROR, "%s\n", err);
753 }
754 
755 /* forward declarations */
756 extern void MDFND_DispMessage(unsigned char *str);
757 
MDFN_DispMessage(const char * format,...)758 void MDFN_DispMessage(const char *format, ...)
759 {
760  va_list ap;
761  va_start(ap,format);
762  char *msg = new char[4096];
763 
764  vsnprintf(msg, 4096, format,ap);
765  va_end(ap);
766 
767  MDFND_DispMessage((UTF8*)msg);
768 }
769 
MDFN_ResetMessages(void)770 void MDFN_ResetMessages(void)
771 {
772  MDFND_DispMessage(NULL);
773 }
774 
MDFNI_LoadGame(const char * force_module,const uint8_t * data,size_t size)775 MDFNGI *MDFNI_LoadGame(const char *force_module, const uint8_t *data, size_t size)
776 {
777    MDFNGameInfo = &EmulatedGBA;
778    MDFN_indent(2);
779 
780    if(Load(data, size) <= 0)
781    {
782       MDFN_indent(-2);
783       MDFNGameInfo = NULL;
784       return(0);
785    }
786 
787    MDFN_LoadGameCheats(NULL);
788    MDFNMP_InstallReadPatches();
789 
790    //MDFN_ResetMessages();   // Save state, status messages, etc.
791 
792    MDFN_indent(-2);
793 
794    return(MDFNGameInfo);
795 }
796 
MDFNI_CloseGame(void)797 void MDFNI_CloseGame(void)
798 {
799    if(!MDFNGameInfo)
800       return;
801 
802    MDFN_FlushGameCheats(0);
803 
804    CloseGame();
805 
806    MDFNMP_Kill();
807 
808    MDFNGameInfo = NULL;
809 }
810 
811 static int curindent = 0;
812 
MDFN_indent(int indent)813 void MDFN_indent(int indent)
814 {
815  curindent += indent;
816 }
817 
818 static uint8 lastchar = 0;
819 
MDFN_printf(const char * format,...)820 void MDFN_printf(const char *format, ...)
821 {
822    char *format_temp;
823    char *temp;
824    unsigned int x, newlen;
825 
826    va_list ap;
827    va_start(ap,format);
828 
829 
830    // First, determine how large our format_temp buffer needs to be.
831    uint8 lastchar_backup = lastchar; // Save lastchar!
832    for(newlen=x=0;x<strlen(format);x++)
833    {
834       if(lastchar == '\n' && format[x] != '\n')
835       {
836          int y;
837          for(y=0;y<curindent;y++)
838             newlen++;
839       }
840       newlen++;
841       lastchar = format[x];
842    }
843 
844    format_temp = (char *)malloc(newlen + 1); // Length + NULL character, duh
845 
846    // Now, construct our format_temp string
847    lastchar = lastchar_backup; // Restore lastchar
848    for(newlen=x=0;x<strlen(format);x++)
849    {
850       if(lastchar == '\n' && format[x] != '\n')
851       {
852          int y;
853          for(y=0;y<curindent;y++)
854             format_temp[newlen++] = ' ';
855       }
856       format_temp[newlen++] = format[x];
857       lastchar = format[x];
858    }
859 
860    format_temp[newlen] = 0;
861 
862    temp = new char[4096];
863    vsnprintf(temp, 4096, format_temp, ap);
864    free(format_temp);
865 
866    MDFND_Message(temp);
867    free(temp);
868 
869    va_end(ap);
870 }
871 
MDFN_PrintError(const char * format,...)872 void MDFN_PrintError(const char *format, ...)
873 {
874  char *temp;
875 
876  va_list ap;
877 
878  va_start(ap, format);
879 
880  temp = new char[4096];
881  vsnprintf(temp, 4096, format, ap);
882  MDFND_PrintError(temp);
883  free(temp);
884 
885  va_end(ap);
886 }
887 
MDFN_DebugPrintReal(const char * file,const int line,const char * format,...)888 void MDFN_DebugPrintReal(const char *file, const int line, const char *format, ...)
889 {
890  char *temp;
891 
892  va_list ap;
893 
894  va_start(ap, format);
895 
896  temp = new char[4096];
897  vsnprintf(temp, 4096, format, ap);
898  fprintf(stderr, "%s:%d  %s\n", file, line, temp);
899  free(temp);
900 
901  va_end(ap);
902 }
903 
systemCartridgeRumble(bool e)904 void systemCartridgeRumble(bool e)
905 {
906    rumble_state = e;
907 }
908 
systemGetSensorDarkness()909 uint8 systemGetSensorDarkness()
910 {
911    return sensorDarkness;
912 }
913 
systemGetSensorZ()914 int systemGetSensorZ()
915 {
916    return 0;
917 }
918