src/platform/libretro/libretro.c (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "libretro.h"
7
8#include <mgba-util/common.h>
9
10#include <mgba/core/blip_buf.h>
11#include <mgba/core/cheats.h>
12#include <mgba/core/core.h>
13#include <mgba/core/log.h>
14#include <mgba/core/serialize.h>
15#include <mgba/core/version.h>
16#ifdef M_CORE_GB
17#include <mgba/gb/core.h>
18#include <mgba/internal/gb/gb.h>
19#include <mgba/internal/gb/mbc.h>
20#endif
21#ifdef M_CORE_GBA
22#include <mgba/gba/core.h>
23#include <mgba/gba/interface.h>
24#include <mgba/internal/gba/gba.h>
25#endif
26#include <mgba-util/memory.h>
27#include <mgba-util/vfs.h>
28
29#include "libretro_core_options.h"
30
31#define SAMPLES 1024
32#define RUMBLE_PWM 35
33#define EVENT_RATE 60
34
35static retro_environment_t environCallback;
36static retro_video_refresh_t videoCallback;
37static retro_audio_sample_batch_t audioCallback;
38static retro_input_poll_t inputPollCallback;
39static retro_input_state_t inputCallback;
40static retro_log_printf_t logCallback;
41static retro_set_rumble_state_t rumbleCallback;
42static retro_sensor_get_input_t sensorGetCallback;
43static retro_set_sensor_state_t sensorStateCallback;
44
45static void GBARetroLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args);
46
47static void _postAudioBuffer(struct mAVStream*, blip_t* left, blip_t* right);
48static void _setRumble(struct mRumble* rumble, int enable);
49static uint8_t _readLux(struct GBALuminanceSource* lux);
50static void _updateLux(struct GBALuminanceSource* lux);
51static void _updateCamera(const uint32_t* buffer, unsigned width, unsigned height, size_t pitch);
52static void _startImage(struct mImageSource*, unsigned w, unsigned h, int colorFormats);
53static void _stopImage(struct mImageSource*);
54static void _requestImage(struct mImageSource*, const void** buffer, size_t* stride, enum mColorFormat* colorFormat);
55
56static struct mCore* core;
57static color_t* outputBuffer = NULL;
58static void* data;
59static size_t dataSize;
60static void* savedata;
61static struct mAVStream stream;
62static bool sensorsInitDone;
63static int rumbleUp;
64static int rumbleDown;
65static struct mRumble rumble;
66static struct GBALuminanceSource lux;
67static struct mRotationSource rotation;
68static bool rotationEnabled;
69static int luxLevelIndex;
70static uint8_t luxLevel;
71static bool luxSensorEnabled;
72static bool luxSensorUsed;
73static struct mLogger logger;
74static struct retro_camera_callback cam;
75static struct mImageSource imageSource;
76static uint32_t* camData = NULL;
77static unsigned camWidth;
78static unsigned camHeight;
79static unsigned imcapWidth;
80static unsigned imcapHeight;
81static size_t camStride;
82static bool deferredSetup = false;
83static bool envVarsUpdated;
84
85static void _initSensors(void) {
86 if(sensorsInitDone) {
87 return;
88 }
89
90 struct retro_sensor_interface sensorInterface;
91 if (environCallback(RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE, &sensorInterface)) {
92 sensorGetCallback = sensorInterface.get_sensor_input;
93 sensorStateCallback = sensorInterface.set_sensor_state;
94
95 if(sensorStateCallback(0, RETRO_SENSOR_ACCELEROMETER_ENABLE, EVENT_RATE)
96 && sensorStateCallback(0, RETRO_SENSOR_GYROSCOPE_ENABLE, EVENT_RATE)) {
97 rotationEnabled = true;
98 }
99
100 if(sensorStateCallback(0, RETRO_SENSOR_ILLUMINANCE_ENABLE, EVENT_RATE)) {
101 luxSensorEnabled = true;
102 }
103 }
104
105 sensorsInitDone = true;
106}
107
108static void _reloadSettings(void) {
109 struct mCoreOptions opts = {
110 .useBios = true,
111 .volume = 0x100,
112 };
113
114 struct retro_variable var;
115#ifdef M_CORE_GB
116 enum GBModel model;
117 const char* modelName;
118
119 var.key = "mgba_gb_model";
120 var.value = 0;
121 if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
122 if (strcmp(var.value, "Game Boy") == 0) {
123 model = GB_MODEL_DMG;
124 } else if (strcmp(var.value, "Super Game Boy") == 0) {
125 model = GB_MODEL_SGB;
126 } else if (strcmp(var.value, "Game Boy Color") == 0) {
127 model = GB_MODEL_CGB;
128 } else if (strcmp(var.value, "Game Boy Advance") == 0) {
129 model = GB_MODEL_AGB;
130 } else {
131 model = GB_MODEL_AUTODETECT;
132 }
133
134 modelName = GBModelToName(model);
135 mCoreConfigSetDefaultValue(&core->config, "gb.model", modelName);
136 mCoreConfigSetDefaultValue(&core->config, "sgb.model", modelName);
137 mCoreConfigSetDefaultValue(&core->config, "cgb.model", modelName);
138 }
139#endif
140
141 var.key = "mgba_use_bios";
142 var.value = 0;
143 if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
144 opts.useBios = strcmp(var.value, "ON") == 0;
145 }
146
147 var.key = "mgba_skip_bios";
148 var.value = 0;
149 if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
150 opts.skipBios = strcmp(var.value, "ON") == 0;
151 }
152
153#ifdef M_CORE_GB
154 var.key = "mgba_sgb_borders";
155 var.value = 0;
156 if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
157 mCoreConfigSetDefaultIntValue(&core->config, "sgb.borders", strcmp(var.value, "ON") == 0);
158 }
159#endif
160
161 var.key = "mgba_frameskip";
162 var.value = 0;
163 if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
164 opts.frameskip = strtol(var.value, NULL, 10);
165 }
166
167 var.key = "mgba_idle_optimization";
168 var.value = 0;
169 if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
170 if (strcmp(var.value, "Don't Remove") == 0) {
171 mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "ignore");
172 } else if (strcmp(var.value, "Remove Known") == 0) {
173 mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "remove");
174 } else if (strcmp(var.value, "Detect and Remove") == 0) {
175 mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "detect");
176 }
177 }
178
179#ifdef M_CORE_GBA
180 var.key = "mgba_force_gbp";
181 var.value = 0;
182 if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
183 mCoreConfigSetDefaultIntValue(&core->config, "gba.forceGbp", strcmp(var.value, "ON") == 0);
184 }
185#endif
186
187 mCoreConfigLoadDefaults(&core->config, &opts);
188 mCoreLoadConfig(core);
189}
190
191static void _doDeferredSetup(void) {
192 // Libretro API doesn't let you know when it's done copying data into the save buffers.
193 // On the off-hand chance that a core actually expects its buffers to be populated when
194 // you actually first get them, you're out of luck without workarounds. Yup, seriously.
195 // Here's that workaround, but really the API needs to be thrown out and rewritten.
196 struct VFile* save = VFileFromMemory(savedata, SIZE_CART_FLASH1M);
197 if (!core->loadSave(core, save)) {
198 save->close(save);
199 }
200 deferredSetup = false;
201}
202
203unsigned retro_api_version(void) {
204 return RETRO_API_VERSION;
205}
206
207void retro_set_environment(retro_environment_t env) {
208 environCallback = env;
209
210 libretro_set_core_options(environCallback);
211}
212
213void retro_set_video_refresh(retro_video_refresh_t video) {
214 videoCallback = video;
215}
216
217void retro_set_audio_sample(retro_audio_sample_t audio) {
218 UNUSED(audio);
219}
220
221void retro_set_audio_sample_batch(retro_audio_sample_batch_t audioBatch) {
222 audioCallback = audioBatch;
223}
224
225void retro_set_input_poll(retro_input_poll_t inputPoll) {
226 inputPollCallback = inputPoll;
227}
228
229void retro_set_input_state(retro_input_state_t input) {
230 inputCallback = input;
231}
232
233void retro_get_system_info(struct retro_system_info* info) {
234 info->need_fullpath = false;
235#ifdef M_CORE_GB
236 info->valid_extensions = "gba|gb|gbc|sgb";
237#else
238 info->valid_extensions = "gba";
239#endif
240 info->library_version = projectVersion;
241 info->library_name = projectName;
242 info->block_extract = false;
243}
244
245void retro_get_system_av_info(struct retro_system_av_info* info) {
246 unsigned width, height;
247 core->desiredVideoDimensions(core, &width, &height);
248 info->geometry.base_width = width;
249 info->geometry.base_height = height;
250#ifdef M_CORE_GB
251 if (core->platform(core) == PLATFORM_GB) {
252 info->geometry.max_width = 256;
253 info->geometry.max_height = 224;
254 } else
255#endif
256 {
257 info->geometry.max_width = width;
258 info->geometry.max_height = height;
259 }
260
261 info->geometry.aspect_ratio = width / (double) height;
262 info->timing.fps = core->frequency(core) / (float) core->frameCycles(core);
263 info->timing.sample_rate = 32768;
264}
265
266void retro_init(void) {
267 enum retro_pixel_format fmt;
268#ifdef COLOR_16_BIT
269#ifdef COLOR_5_6_5
270 fmt = RETRO_PIXEL_FORMAT_RGB565;
271#else
272#warning This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
273 fmt = RETRO_PIXEL_FORMAT_0RGB1555;
274#endif
275#else
276#warning This pixel format is unsupported. Please use -DCOLOR_16-BIT -DCOLOR_5_6_5
277 fmt = RETRO_PIXEL_FORMAT_XRGB8888;
278#endif
279 environCallback(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt);
280
281 struct retro_input_descriptor inputDescriptors[] = {
282 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
283 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
284 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
285 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
286 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
287 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
288 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
289 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
290 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
291 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" },
292 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "Brighten Solar Sensor" },
293 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "Darken Solar Sensor" },
294 { 0 }
295 };
296 environCallback(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &inputDescriptors);
297
298 // TODO: RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME when BIOS booting is supported
299
300 struct retro_rumble_interface rumbleInterface;
301 if (environCallback(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumbleInterface)) {
302 rumbleCallback = rumbleInterface.set_rumble_state;
303 rumble.setRumble = _setRumble;
304 } else {
305 rumbleCallback = 0;
306 }
307
308 sensorsInitDone = false;
309 sensorGetCallback = 0;
310 sensorStateCallback = 0;
311
312 rotationEnabled = false;
313 rotation.readTiltX = _readTiltX;
314 rotation.readTiltY = _readTiltY;
315 rotation.readGyroZ = _readGyroZ;
316
317 envVarsUpdated = true;
318 luxSensorUsed = false;
319 luxSensorEnabled = false;
320 luxLevelIndex = 0;
321 luxLevel = 0;
322 lux.readLuminance = _readLux;
323 lux.sample = _updateLux;
324 _updateLux(&lux);
325
326 struct retro_log_callback log;
327 if (environCallback(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log)) {
328 logCallback = log.log;
329 } else {
330 logCallback = 0;
331 }
332 logger.log = GBARetroLog;
333 mLogSetDefaultLogger(&logger);
334
335 stream.videoDimensionsChanged = 0;
336 stream.postAudioFrame = 0;
337 stream.postAudioBuffer = _postAudioBuffer;
338 stream.postVideoFrame = 0;
339
340 imageSource.startRequestImage = _startImage;
341 imageSource.stopRequestImage = _stopImage;
342 imageSource.requestImage = _requestImage;
343}
344
345void retro_deinit(void) {
346 free(outputBuffer);
347
348 if(sensorStateCallback) {
349 sensorStateCallback(0, RETRO_SENSOR_ACCELEROMETER_DISABLE, EVENT_RATE);
350 sensorStateCallback(0, RETRO_SENSOR_GYROSCOPE_DISABLE, EVENT_RATE);
351 sensorStateCallback(0, RETRO_SENSOR_ILLUMINANCE_DISABLE, EVENT_RATE);
352 }
353
354 rotationEnabled = false;
355 luxSensorEnabled = false;
356}
357
358void retro_run(void) {
359 if (deferredSetup) {
360 _doDeferredSetup();
361 }
362 uint16_t keys;
363
364 _initSensors();
365 inputPollCallback();
366
367 bool updated = false;
368 if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) {
369 envVarsUpdated = true;
370
371 struct retro_variable var = {
372 .key = "mgba_allow_opposing_directions",
373 .value = 0
374 };
375 if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
376 mCoreConfigSetIntValue(&core->config, "allowOpposingDirections", strcmp(var.value, "yes") == 0);
377 core->reloadConfigOption(core, "allowOpposingDirections", NULL);
378 }
379
380 var.key = "mgba_frameskip";
381 var.value = 0;
382 if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
383 mCoreConfigSetIntValue(&core->config, "frameskip", strtol(var.value, NULL, 10));
384 core->reloadConfigOption(core, "frameskip", NULL);
385 }
386 }
387
388 keys = 0;
389 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A)) << 0;
390 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) << 1;
391 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) << 2;
392 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) << 3;
393 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT)) << 4;
394 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT)) << 5;
395 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP)) << 6;
396 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)) << 7;
397 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R)) << 8;
398 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L)) << 9;
399 core->setKeys(core, keys);
400
401 if(!luxSensorUsed) {
402 static bool wasAdjustingLux = false;
403 if (wasAdjustingLux) {
404 wasAdjustingLux = inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3) ||
405 inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3);
406 } else {
407 if (inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3)) {
408 ++luxLevelIndex;
409 if (luxLevelIndex > 10) {
410 luxLevelIndex = 10;
411 }
412 wasAdjustingLux = true;
413 } else if (inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3)) {
414 --luxLevelIndex;
415 if (luxLevelIndex < 0) {
416 luxLevelIndex = 0;
417 }
418 wasAdjustingLux = true;
419 }
420 }
421 }
422
423 core->runFrame(core);
424 unsigned width, height;
425 core->desiredVideoDimensions(core, &width, &height);
426 videoCallback(outputBuffer, width, height, BYTES_PER_PIXEL * 256);
427
428 if (rumbleCallback) {
429 if (rumbleUp) {
430 rumbleCallback(0, RETRO_RUMBLE_STRONG, rumbleUp * 0xFFFF / (rumbleUp + rumbleDown));
431 rumbleCallback(0, RETRO_RUMBLE_WEAK, rumbleUp * 0xFFFF / (rumbleUp + rumbleDown));
432 } else {
433 rumbleCallback(0, RETRO_RUMBLE_STRONG, 0);
434 rumbleCallback(0, RETRO_RUMBLE_WEAK, 0);
435 }
436 rumbleUp = 0;
437 rumbleDown = 0;
438 }
439}
440
441static void _setupMaps(struct mCore* core) {
442#ifdef M_CORE_GBA
443 if (core->platform(core) == PLATFORM_GBA) {
444 struct GBA* gba = core->board;
445 struct retro_memory_descriptor descs[11];
446 struct retro_memory_map mmaps;
447 size_t romSize = gba->memory.romSize + (gba->memory.romSize & 1);
448
449 memset(descs, 0, sizeof(descs));
450 size_t savedataSize = retro_get_memory_size(RETRO_MEMORY_SAVE_RAM);
451
452 /* Map internal working RAM */
453 descs[0].ptr = gba->memory.iwram;
454 descs[0].start = BASE_WORKING_IRAM;
455 descs[0].len = SIZE_WORKING_IRAM;
456 descs[0].select = 0xFF000000;
457
458 /* Map working RAM */
459 descs[1].ptr = gba->memory.wram;
460 descs[1].start = BASE_WORKING_RAM;
461 descs[1].len = SIZE_WORKING_RAM;
462 descs[1].select = 0xFF000000;
463
464 /* Map save RAM */
465 /* TODO: if SRAM is flash, use start=0 addrspace="S" instead */
466 descs[2].ptr = savedataSize ? savedata : NULL;
467 descs[2].start = BASE_CART_SRAM;
468 descs[2].len = savedataSize;
469
470 /* Map ROM */
471 descs[3].ptr = gba->memory.rom;
472 descs[3].start = BASE_CART0;
473 descs[3].len = romSize;
474 descs[3].flags = RETRO_MEMDESC_CONST;
475
476 descs[4].ptr = gba->memory.rom;
477 descs[4].start = BASE_CART1;
478 descs[4].len = romSize;
479 descs[4].flags = RETRO_MEMDESC_CONST;
480
481 descs[5].ptr = gba->memory.rom;
482 descs[5].start = BASE_CART2;
483 descs[5].len = romSize;
484 descs[5].flags = RETRO_MEMDESC_CONST;
485
486 /* Map BIOS */
487 descs[6].ptr = gba->memory.bios;
488 descs[6].start = BASE_BIOS;
489 descs[6].len = SIZE_BIOS;
490 descs[6].flags = RETRO_MEMDESC_CONST;
491
492 /* Map VRAM */
493 descs[7].ptr = gba->video.vram;
494 descs[7].start = BASE_VRAM;
495 descs[7].len = SIZE_VRAM;
496 descs[7].select = 0xFF000000;
497
498 /* Map palette RAM */
499 descs[8].ptr = gba->video.palette;
500 descs[8].start = BASE_PALETTE_RAM;
501 descs[8].len = SIZE_PALETTE_RAM;
502 descs[8].select = 0xFF000000;
503
504 /* Map OAM */
505 descs[9].ptr = &gba->video.oam; /* video.oam is a structure */
506 descs[9].start = BASE_OAM;
507 descs[9].len = SIZE_OAM;
508 descs[9].select = 0xFF000000;
509
510 /* Map mmapped I/O */
511 descs[10].ptr = gba->memory.io;
512 descs[10].start = BASE_IO;
513 descs[10].len = SIZE_IO;
514
515 mmaps.descriptors = descs;
516 mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]);
517
518 bool yes = true;
519 environCallback(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmaps);
520 environCallback(RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS, &yes);
521 }
522#endif
523#ifdef M_CORE_GB
524 if (core->platform(core) == PLATFORM_GB) {
525 struct GB* gb = core->board;
526 struct retro_memory_descriptor descs[11];
527 struct retro_memory_map mmaps;
528
529 memset(descs, 0, sizeof(descs));
530 size_t savedataSize = retro_get_memory_size(RETRO_MEMORY_SAVE_RAM);
531
532 unsigned i = 0;
533
534 /* Map ROM */
535 descs[i].ptr = gb->memory.rom;
536 descs[i].start = GB_BASE_CART_BANK0;
537 descs[i].len = GB_SIZE_CART_BANK0;
538 descs[i].flags = RETRO_MEMDESC_CONST;
539 i++;
540
541 descs[i].ptr = gb->memory.rom;
542 descs[i].offset = GB_SIZE_CART_BANK0;
543 descs[i].start = GB_BASE_CART_BANK1;
544 descs[i].len = GB_SIZE_CART_BANK0;
545 descs[i].flags = RETRO_MEMDESC_CONST;
546 i++;
547
548 /* Map VRAM */
549 descs[i].ptr = gb->video.vram;
550 descs[i].start = GB_BASE_VRAM;
551 descs[i].len = GB_SIZE_VRAM_BANK0;
552 i++;
553
554 /* Map working RAM */
555 descs[i].ptr = gb->memory.wram;
556 descs[i].start = GB_BASE_WORKING_RAM_BANK0;
557 descs[i].len = GB_SIZE_WORKING_RAM_BANK0;
558 i++;
559
560 descs[i].ptr = gb->memory.wram;
561 descs[i].offset = GB_SIZE_WORKING_RAM_BANK0;
562 descs[i].start = GB_BASE_WORKING_RAM_BANK1;
563 descs[i].len = GB_SIZE_WORKING_RAM_BANK0;
564 i++;
565
566 /* Map OAM */
567 descs[i].ptr = &gb->video.oam; /* video.oam is a structure */
568 descs[i].start = GB_BASE_OAM;
569 descs[i].len = GB_SIZE_OAM;
570 descs[i].select = 0xFFFFFF60;
571 i++;
572
573 /* Map mmapped I/O */
574 descs[i].ptr = gb->memory.io;
575 descs[i].start = GB_BASE_IO;
576 descs[i].len = GB_SIZE_IO;
577 i++;
578
579 /* Map High RAM */
580 descs[i].ptr = gb->memory.hram;
581 descs[i].start = GB_BASE_HRAM;
582 descs[i].len = GB_SIZE_HRAM;
583 descs[i].select = 0xFFFFFF80;
584 i++;
585
586 /* Map IE Register */
587 descs[i].ptr = &gb->memory.ie;
588 descs[i].start = GB_BASE_IE;
589 descs[i].len = 1;
590 i++;
591
592 /* Map External RAM */
593 if (gb->memory.sram) {
594 descs[i].ptr = gb->memory.sram;
595 descs[i].start = GB_BASE_EXTERNAL_RAM;
596 descs[i].len = savedataSize;
597 i++;
598 }
599
600 if (gb->model >= GB_MODEL_CGB) {
601 /* Map working RAM */
602 /* banks 2-7 of wram mapped in virtual address so it can be
603 * accessed without bank switching, GBC only */
604 descs[i].ptr = gb->memory.wram + 0x2000;
605 descs[i].start = 0x10000;
606 descs[i].len = GB_SIZE_WORKING_RAM - 0x2000;
607 descs[i].select = 0xFFFFA000;
608 i++;
609 }
610
611 mmaps.descriptors = descs;
612 mmaps.num_descriptors = i;
613
614 bool yes = true;
615 environCallback(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmaps);
616 environCallback(RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS, &yes);
617 }
618#endif
619}
620
621void retro_reset(void) {
622 core->reset(core);
623 _setupMaps(core);
624
625 rumbleUp = 0;
626 rumbleDown = 0;
627}
628
629bool retro_load_game(const struct retro_game_info* game) {
630 struct VFile* rom;
631 if (game->data) {
632 data = anonymousMemoryMap(game->size);
633 dataSize = game->size;
634 memcpy(data, game->data, game->size);
635 rom = VFileFromMemory(data, game->size);
636 } else {
637 data = 0;
638 rom = VFileOpen(game->path, O_RDONLY);
639 }
640 if (!rom) {
641 return false;
642 }
643
644 core = mCoreFindVF(rom);
645 if (!core) {
646 rom->close(rom);
647 mappedMemoryFree(data, game->size);
648 return false;
649 }
650 mCoreInitConfig(core, NULL);
651 core->init(core);
652 core->setAVStream(core, &stream);
653
654 size_t size = 256 * 224 * BYTES_PER_PIXEL;
655 outputBuffer = malloc(size);
656 memset(outputBuffer, 0xFF, size);
657 core->setVideoBuffer(core, outputBuffer, 256);
658
659 core->setAudioBufferSize(core, SAMPLES);
660
661 blip_set_rates(core->getAudioChannel(core, 0), core->frequency(core), 32768);
662 blip_set_rates(core->getAudioChannel(core, 1), core->frequency(core), 32768);
663
664 core->setPeripheral(core, mPERIPH_RUMBLE, &rumble);
665
666 savedata = anonymousMemoryMap(SIZE_CART_FLASH1M);
667 memset(savedata, 0xFF, SIZE_CART_FLASH1M);
668
669 _reloadSettings();
670 core->loadROM(core, rom);
671 deferredSetup = true;
672
673 const char* sysDir = 0;
674 const char* biosName = 0;
675 char biosPath[PATH_MAX];
676 environCallback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &sysDir);
677
678#ifdef M_CORE_GBA
679 if (core->platform(core) == PLATFORM_GBA) {
680 core->setPeripheral(core, mPERIPH_GBA_LUMINANCE, &lux);
681 biosName = "gba_bios.bin";
682
683 }
684#endif
685
686#ifdef M_CORE_GB
687 if (core->platform(core) == PLATFORM_GB) {
688 memset(&cam, 0, sizeof(cam));
689 cam.height = GBCAM_HEIGHT;
690 cam.width = GBCAM_WIDTH;
691 cam.caps = 1 << RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER;
692 cam.frame_raw_framebuffer = _updateCamera;
693 if (environCallback(RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE, &cam)) {
694 core->setPeripheral(core, mPERIPH_IMAGE_SOURCE, &imageSource);
695 }
696
697 const char* modelName = mCoreConfigGetValue(&core->config, "gb.model");
698 struct GB* gb = core->board;
699
700 if (modelName) {
701 gb->model = GBNameToModel(modelName);
702 } else {
703 GBDetectModel(gb);
704 }
705
706 switch (gb->model) {
707 case GB_MODEL_AGB:
708 case GB_MODEL_CGB:
709 biosName = "gbc_bios.bin";
710 break;
711 case GB_MODEL_SGB:
712 biosName = "sgb_bios.bin";
713 break;
714 case GB_MODEL_DMG:
715 default:
716 biosName = "gb_bios.bin";
717 break;
718 }
719 }
720#endif
721
722 if (core->opts.useBios && sysDir && biosName) {
723 snprintf(biosPath, sizeof(biosPath), "%s%s%s", sysDir, PATH_SEP, biosName);
724 struct VFile* bios = VFileOpen(biosPath, O_RDONLY);
725 if (bios) {
726 core->loadBIOS(core, bios, 0);
727 }
728 }
729
730 core->reset(core);
731 _setupMaps(core);
732
733 return true;
734}
735
736void retro_unload_game(void) {
737 if (!core) {
738 return;
739 }
740 mCoreConfigDeinit(&core->config);
741 core->deinit(core);
742 mappedMemoryFree(data, dataSize);
743 data = 0;
744 mappedMemoryFree(savedata, SIZE_CART_FLASH1M);
745 savedata = 0;
746}
747
748size_t retro_serialize_size(void) {
749 if (deferredSetup) {
750 _doDeferredSetup();
751 }
752 struct VFile* vfm = VFileMemChunk(NULL, 0);
753 mCoreSaveStateNamed(core, vfm, SAVESTATE_SAVEDATA | SAVESTATE_RTC);
754 size_t size = vfm->size(vfm);
755 vfm->close(vfm);
756 return size;
757}
758
759bool retro_serialize(void* data, size_t size) {
760 if (deferredSetup) {
761 _doDeferredSetup();
762 }
763 struct VFile* vfm = VFileMemChunk(NULL, 0);
764 mCoreSaveStateNamed(core, vfm, SAVESTATE_SAVEDATA | SAVESTATE_RTC);
765 if ((ssize_t) size > vfm->size(vfm)) {
766 size = vfm->size(vfm);
767 } else if ((ssize_t) size < vfm->size(vfm)) {
768 vfm->close(vfm);
769 return false;
770 }
771 vfm->seek(vfm, 0, SEEK_SET);
772 vfm->read(vfm, data, size);
773 vfm->close(vfm);
774 return true;
775}
776
777bool retro_unserialize(const void* data, size_t size) {
778 if (deferredSetup) {
779 _doDeferredSetup();
780 }
781 struct VFile* vfm = VFileFromConstMemory(data, size);
782 bool success = mCoreLoadStateNamed(core, vfm, SAVESTATE_RTC);
783 vfm->close(vfm);
784 return success;
785}
786
787void retro_cheat_reset(void) {
788 mCheatDeviceClear(core->cheatDevice(core));
789}
790
791void retro_cheat_set(unsigned index, bool enabled, const char* code) {
792 UNUSED(index);
793 UNUSED(enabled);
794 struct mCheatDevice* device = core->cheatDevice(core);
795 struct mCheatSet* cheatSet = NULL;
796 if (mCheatSetsSize(&device->cheats)) {
797 cheatSet = *mCheatSetsGetPointer(&device->cheats, 0);
798 } else {
799 cheatSet = device->createSet(device, NULL);
800 mCheatAddSet(device, cheatSet);
801 }
802// Convert the super wonky unportable libretro format to something normal
803#ifdef M_CORE_GBA
804 if (core->platform(core) == PLATFORM_GBA) {
805 char realCode[] = "XXXXXXXX XXXXXXXX";
806 size_t len = strlen(code) + 1; // Include null terminator
807 size_t i, pos;
808 for (i = 0, pos = 0; i < len; ++i) {
809 if (isspace((int) code[i]) || code[i] == '+') {
810 realCode[pos] = ' ';
811 } else {
812 realCode[pos] = code[i];
813 }
814 if ((pos == 13 && (realCode[pos] == ' ' || !realCode[pos])) || pos == 17) {
815 realCode[pos] = '\0';
816 mCheatAddLine(cheatSet, realCode, 0);
817 pos = 0;
818 continue;
819 }
820 ++pos;
821 }
822 }
823#endif
824#ifdef M_CORE_GB
825 if (core->platform(core) == PLATFORM_GB) {
826 char realCode[] = "XXX-XXX-XXX";
827 size_t len = strlen(code) + 1; // Include null terminator
828 size_t i, pos;
829 for (i = 0, pos = 0; i < len; ++i) {
830 if (isspace((int) code[i]) || code[i] == '+') {
831 realCode[pos] = '\0';
832 } else {
833 realCode[pos] = code[i];
834 }
835 if (pos == 11 || !realCode[pos]) {
836 realCode[pos] = '\0';
837 mCheatAddLine(cheatSet, realCode, 0);
838 pos = 0;
839 continue;
840 }
841 ++pos;
842 }
843 }
844#endif
845 cheatSet->refresh(cheatSet, device);
846}
847
848unsigned retro_get_region(void) {
849 return RETRO_REGION_NTSC; // TODO: This isn't strictly true
850}
851
852void retro_set_controller_port_device(unsigned port, unsigned device) {
853 UNUSED(port);
854 UNUSED(device);
855}
856
857bool retro_load_game_special(unsigned game_type, const struct retro_game_info* info, size_t num_info) {
858 UNUSED(game_type);
859 UNUSED(info);
860 UNUSED(num_info);
861 return false;
862}
863
864void* retro_get_memory_data(unsigned id) {
865 switch (id) {
866 case RETRO_MEMORY_SAVE_RAM:
867 return savedata;
868 case RETRO_MEMORY_RTC:
869 switch (core->platform(core)) {
870#ifdef M_CORE_GB
871 case PLATFORM_GB:
872 switch (((struct GB*) core->board)->memory.mbcType) {
873 case GB_MBC3_RTC:
874 return &((uint8_t*) savedata)[((struct GB*) core->board)->sramSize];
875 default:
876 return NULL;
877 }
878#endif
879 default:
880 return NULL;
881 }
882 default:
883 break;
884 }
885 return NULL;
886}
887
888size_t retro_get_memory_size(unsigned id) {
889 switch (id) {
890 case RETRO_MEMORY_SAVE_RAM:
891 switch (core->platform(core)) {
892#ifdef M_CORE_GBA
893 case PLATFORM_GBA:
894 switch (((struct GBA*) core->board)->memory.savedata.type) {
895 case SAVEDATA_AUTODETECT:
896 return SIZE_CART_FLASH1M;
897 default:
898 return GBASavedataSize(&((struct GBA*) core->board)->memory.savedata);
899 }
900#endif
901#ifdef M_CORE_GB
902 case PLATFORM_GB:
903 return ((struct GB*) core->board)->sramSize;
904#endif
905 default:
906 break;
907 }
908 break;
909 case RETRO_MEMORY_RTC:
910 switch (core->platform(core)) {
911#ifdef M_CORE_GB
912 case PLATFORM_GB:
913 switch (((struct GB*) core->board)->memory.mbcType) {
914 case GB_MBC3_RTC:
915 return sizeof(struct GBMBCRTCSaveBuffer);
916 default:
917 return 0;
918 }
919#endif
920 default:
921 break;
922 }
923 break;
924 default:
925 break;
926 }
927 return 0;
928}
929
930void GBARetroLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
931 UNUSED(logger);
932 if (!logCallback) {
933 return;
934 }
935
936 char message[128];
937 vsnprintf(message, sizeof(message), format, args);
938
939 enum retro_log_level retroLevel = RETRO_LOG_INFO;
940 switch (level) {
941 case mLOG_ERROR:
942 case mLOG_FATAL:
943 retroLevel = RETRO_LOG_ERROR;
944 break;
945 case mLOG_WARN:
946 retroLevel = RETRO_LOG_WARN;
947 break;
948 case mLOG_INFO:
949 retroLevel = RETRO_LOG_INFO;
950 break;
951 case mLOG_GAME_ERROR:
952 case mLOG_STUB:
953#ifdef NDEBUG
954 return;
955#else
956 retroLevel = RETRO_LOG_DEBUG;
957 break;
958#endif
959 case mLOG_DEBUG:
960 retroLevel = RETRO_LOG_DEBUG;
961 break;
962 }
963#ifdef NDEBUG
964 static int biosCat = -1;
965 if (biosCat < 0) {
966 biosCat = mLogCategoryById("gba.bios");
967 }
968
969 if (category == biosCat) {
970 return;
971 }
972#endif
973 logCallback(retroLevel, "%s: %s\n", mLogCategoryName(category), message);
974}
975
976static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) {
977 UNUSED(stream);
978 int16_t samples[SAMPLES * 2];
979 blip_read_samples(left, samples, SAMPLES, true);
980 blip_read_samples(right, samples + 1, SAMPLES, true);
981 audioCallback(samples, SAMPLES);
982}
983
984static void _setRumble(struct mRumble* rumble, int enable) {
985 UNUSED(rumble);
986 if (!rumbleCallback) {
987 return;
988 }
989 if (enable) {
990 ++rumbleUp;
991 } else {
992 ++rumbleDown;
993 }
994}
995
996static void _updateLux(struct GBALuminanceSource* lux) {
997 UNUSED(lux);
998 struct retro_variable var = {
999 .key = "mgba_solar_sensor_level",
1000 .value = 0
1001 };
1002 bool luxVarUpdated = envVarsUpdated;
1003
1004 if (luxVarUpdated && (!environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || !var.value)) {
1005 luxVarUpdated = false;
1006 }
1007
1008 if(luxVarUpdated) {
1009 luxSensorUsed = strcmp(var.value, "sensor") == 0;
1010 }
1011
1012 if(luxSensorUsed) {
1013 float fLux = luxSensorEnabled ? sensorGetCallback(0, RETRO_SENSOR_ILLUMINANCE) : 0.0f;
1014 luxLevel = cbrtf(fLux) * 8;
1015 } else {
1016 if(luxVarUpdated) {
1017 char* end;
1018 int newLuxLevelIndex = strtol(var.value, &end, 10);
1019
1020 if (!*end) {
1021 if (newLuxLevelIndex > 10) {
1022 luxLevelIndex = 10;
1023 } else if (newLuxLevelIndex < 0) {
1024 luxLevelIndex = 0;
1025 } else {
1026 luxLevelIndex = newLuxLevelIndex;
1027 }
1028 }
1029 }
1030
1031 luxLevel = 0x16;
1032 if (luxLevelIndex > 0) {
1033 luxLevel += GBA_LUX_LEVELS[luxLevelIndex - 1];
1034 }
1035 }
1036
1037 envVarsUpdated = false;
1038}
1039
1040static uint8_t _readLux(struct GBALuminanceSource* lux) {
1041 UNUSED(lux);
1042 return 0xFF - luxLevel;
1043}
1044
1045static void _updateCamera(const uint32_t* buffer, unsigned width, unsigned height, size_t pitch) {
1046 if (!camData || width > camWidth || height > camHeight) {
1047 if (camData) {
1048 free(camData);
1049 camData = NULL;
1050 }
1051 unsigned bufPitch = pitch / sizeof(*buffer);
1052 unsigned bufHeight = height;
1053 if (imcapWidth > bufPitch) {
1054 bufPitch = imcapWidth;
1055 }
1056 if (imcapHeight > bufHeight) {
1057 bufHeight = imcapHeight;
1058 }
1059 camData = malloc(sizeof(*buffer) * bufHeight * bufPitch);
1060 memset(camData, 0xFF, sizeof(*buffer) * bufHeight * bufPitch);
1061 camWidth = width;
1062 camHeight = bufHeight;
1063 camStride = bufPitch;
1064 }
1065 size_t i;
1066 for (i = 0; i < height; ++i) {
1067 memcpy(&camData[camStride * i], &buffer[pitch * i / sizeof(*buffer)], pitch);
1068 }
1069}
1070
1071static void _startImage(struct mImageSource* image, unsigned w, unsigned h, int colorFormats) {
1072 UNUSED(image);
1073 UNUSED(colorFormats);
1074
1075 if (camData) {
1076 free(camData);
1077 }
1078 camData = NULL;
1079 imcapWidth = w;
1080 imcapHeight = h;
1081 cam.start();
1082}
1083
1084static void _stopImage(struct mImageSource* image) {
1085 UNUSED(image);
1086 cam.stop();
1087}
1088
1089static void _requestImage(struct mImageSource* image, const void** buffer, size_t* stride, enum mColorFormat* colorFormat) {
1090 UNUSED(image);
1091 if (!camData) {
1092 cam.start();
1093 *buffer = NULL;
1094 return;
1095 }
1096 size_t offset = 0;
1097 if (imcapWidth < camWidth) {
1098 offset += (camWidth - imcapWidth) / 2;
1099 }
1100 if (imcapHeight < camHeight) {
1101 offset += (camHeight - imcapHeight) / 2 * camStride;
1102 }
1103
1104 *buffer = &camData[offset];
1105 *stride = camStride;
1106 *colorFormat = mCOLOR_XRGB8;
1107}
1108
1109static int32_t _readTiltX(struct mRotationSource* source) {
1110 UNUSED(source);
1111 int32_t tiltX = 0;
1112
1113 if(rotationEnabled) {
1114 tiltX = sensorGetCallback(0, RETRO_SENSOR_ACCELEROMETER_X) * 3e8f;
1115 }
1116
1117 return tiltX;
1118}
1119
1120static int32_t _readTiltY(struct mRotationSource* source) {
1121 UNUSED(source);
1122 int32_t tiltY = 0;
1123
1124 if(rotationEnabled) {
1125 tiltY = sensorGetCallback(0, RETRO_SENSOR_ACCELEROMETER_Y) * -3e8f;
1126 }
1127
1128 return tiltY;
1129}
1130
1131static int32_t _readGyroZ(struct mRotationSource* source) {
1132 UNUSED(source);
1133 int32_t gyroZ = 0;
1134
1135 if(rotationEnabled) {
1136 gyroZ = sensorGetCallback(0, RETRO_SENSOR_GYROSCOPE_Z) * -1.1e9f;
1137 }
1138
1139 return gyroZ;
1140}