all repos — mgba @ 983a4825278af11c1a35726edd95c28bbd037a1b

mGBA Game Boy Advance Emulator

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}