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