static void _reloadSettings(void) { struct mCoreOptions opts = { .useBios = true, .volume = 0x100, }; struct retro_variable var; var.key = "mgba_use_bios"; var.value = 0; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { opts.useBios = strcmp(var.value, "ON") == 0; } var.key = "mgba_skip_bios"; var.value = 0; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { opts.skipBios = strcmp(var.value, "ON") == 0; } var.key = "mgba_idle_optimization"; var.value = 0; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { if (strcmp(var.value, "Don't Remove") == 0) { mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "ignore"); } else if (strcmp(var.value, "Remove Known") == 0) { mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "remove"); } else if (strcmp(var.value, "Detect and Remove") == 0) { mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "detect"); } } mCoreConfigLoadDefaults(&core->config, &opts); mCoreLoadConfig(core); } unsigned retro_api_version(void) { return RETRO_API_VERSION; } void retro_set_environment(retro_environment_t env) { environCallback = env; struct retro_variable vars[] = { { "mgba_solar_sensor_level", "Solar sensor level; 0|1|2|3|4|5|6|7|8|9|10" }, { "mgba_allow_opposing_directions", "Allow opposing directional input; OFF|ON" }, { "mgba_use_bios", "Use BIOS file if found; ON|OFF" }, { "mgba_skip_bios", "Skip BIOS intro; OFF|ON" }, { "mgba_idle_optimization", "Idle loop removal; Remove Known|Detect and Remove|Don't Remove" }, { 0, 0 } }; environCallback(RETRO_ENVIRONMENT_SET_VARIABLES, vars); }
bool _mPerfRunCore(const char* fname, const struct mArguments* args, const struct PerfOpts* perfOpts) { struct mCore* core = mCoreFind(fname); if (!core) { return false; } // TODO: Put back debugger char gameCode[9] = { 0 }; core->init(core); if (!perfOpts->noVideo) { core->setVideoBuffer(core, _outputBuffer, 256); } mCoreLoadFile(core, fname); mCoreConfigInit(&core->config, "perf"); mCoreConfigLoad(&core->config); if (perfOpts->threadedVideo) { mCoreConfigSetOverrideIntValue(&core->config, "threadedVideo", 1); } else { mCoreConfigSetOverrideIntValue(&core->config, "threadedVideo", 0); } struct mCoreOptions opts = {}; mCoreConfigMap(&core->config, &opts); opts.audioSync = false; opts.videoSync = false; applyArguments(args, NULL, &core->config); mCoreConfigLoadDefaults(&core->config, &opts); mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "detect"); mCoreLoadConfig(core); core->reset(core); if (_savestate) { mCoreLoadStateNamed(core, _savestate, 0); } core->getGameCode(core, gameCode); int frames = perfOpts->frames; if (!frames) { frames = perfOpts->duration * 60; } struct timeval tv; gettimeofday(&tv, 0); uint64_t start = 1000000LL * tv.tv_sec + tv.tv_usec; _mPerfRunloop(core, &frames, perfOpts->csv); gettimeofday(&tv, 0); uint64_t end = 1000000LL * tv.tv_sec + tv.tv_usec; uint64_t duration = end - start; mCoreConfigFreeOpts(&opts); mCoreConfigDeinit(&core->config); core->deinit(core); float scaledFrames = frames * 1000000.f; if (perfOpts->csv) { char buffer[256]; const char* rendererName; if (perfOpts->noVideo) { rendererName = "none"; } else if (perfOpts->threadedVideo) { rendererName = "threaded-software"; } else { rendererName = "software"; } snprintf(buffer, sizeof(buffer), "%s,%i,%" PRIu64 ",%s\n", gameCode, frames, duration, rendererName); printf("%s", buffer); if (_socket != INVALID_SOCKET) { SocketSend(_socket, buffer, strlen(buffer)); } } else { printf("%u frames in %" PRIu64 " microseconds: %g fps (%gx)\n", frames, duration, scaledFrames / duration, scaledFrames / (duration * 60.f)); } return true; }
static void _reloadSettings(void) { struct mCoreOptions opts = { .useBios = true, .volume = 0x100, }; struct retro_variable var; enum GBModel model; const char* modelName; var.key = "mgba_gb_model"; var.value = 0; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { if (strcmp(var.value, "Game Boy") == 0) { model = GB_MODEL_DMG; } else if (strcmp(var.value, "Super Game Boy") == 0) { model = GB_MODEL_SGB; } else if (strcmp(var.value, "Game Boy Color") == 0) { model = GB_MODEL_CGB; } else if (strcmp(var.value, "Game Boy Advance") == 0) { model = GB_MODEL_AGB; } else { model = GB_MODEL_AUTODETECT; } modelName = GBModelToName(model); mCoreConfigSetDefaultValue(&core->config, "gb.model", modelName); mCoreConfigSetDefaultValue(&core->config, "sgb.model", modelName); mCoreConfigSetDefaultValue(&core->config, "cgb.model", modelName); } var.key = "mgba_use_bios"; var.value = 0; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { opts.useBios = strcmp(var.value, "ON") == 0; } var.key = "mgba_skip_bios"; var.value = 0; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { opts.skipBios = strcmp(var.value, "ON") == 0; } var.key = "mgba_sgb_borders"; var.value = 0; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { if (strcmp(var.value, "ON") == 0) { mCoreConfigSetDefaultIntValue(&core->config, "sgb.borders", true); } else { mCoreConfigSetDefaultIntValue(&core->config, "sgb.borders", false); } } var.key = "mgba_idle_optimization"; var.value = 0; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { if (strcmp(var.value, "Don't Remove") == 0) { mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "ignore"); } else if (strcmp(var.value, "Remove Known") == 0) { mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "remove"); } else if (strcmp(var.value, "Detect and Remove") == 0) { mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "detect"); } } var.key = "mgba_frameskip"; var.value = 0; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { opts.frameskip = strtol(var.value, NULL, 10); } mCoreConfigLoadDefaults(&core->config, &opts); mCoreLoadConfig(core); } unsigned retro_api_version(void) { return RETRO_API_VERSION; } void retro_set_environment(retro_environment_t env) { environCallback = env; struct retro_variable vars[] = { { "mgba_solar_sensor_level", "Solar sensor level; 0|1|2|3|4|5|6|7|8|9|10" }, { "mgba_allow_opposing_directions", "Allow opposing directional input; OFF|ON" }, { "mgba_gb_model", "Game Boy model (requires restart); Autodetect|Game Boy|Super Game Boy|Game Boy Color|Game Boy Advance" }, { "mgba_use_bios", "Use BIOS file if found (requires restart); ON|OFF" }, { "mgba_skip_bios", "Skip BIOS intro (requires restart); OFF|ON" }, { "mgba_sgb_borders", "Use Super Game Boy borders (requires restart); ON|OFF" }, { "mgba_idle_optimization", "Idle loop removal; Remove Known|Detect and Remove|Don't Remove" }, { "mgba_frameskip", "Frameskip; 0|1|2|3|4|5|6|7|8|9|10" }, { 0, 0 } }; environCallback(RETRO_ENVIRONMENT_SET_VARIABLES, vars); }
int main(int argc, char** argv) { struct mSDLRenderer renderer = {}; struct mCoreOptions opts = { .useBios = true, .rewindEnable = true, .audioBuffers = 512, .videoSync = false, .audioSync = true, .volume = 0x100, }; struct mArguments args; struct mGraphicsOpts graphicsOpts; struct mSubParser subparser; initParserForGraphics(&subparser, &graphicsOpts); bool parsed = parseArguments(&args, argc, argv, &subparser); if (!parsed || args.showHelp) { usage(argv[0], subparser.usage); freeArguments(&args); return !parsed; } if (args.showVersion) { version(argv[0]); freeArguments(&args); return 0; } renderer.core = mCoreFind(args.fname); if (!renderer.core) { printf("Could not run game. Are you sure the file exists and is a compatible game?\n"); freeArguments(&args); return 1; } renderer.core->desiredVideoDimensions(renderer.core, &renderer.width, &renderer.height); #ifdef BUILD_GL mSDLGLCreate(&renderer); #elif defined(BUILD_GLES2) || defined(USE_EPOXY) mSDLGLES2Create(&renderer); #else mSDLSWCreate(&renderer); #endif renderer.ratio = graphicsOpts.multiplier; if (renderer.ratio == 0) { renderer.ratio = 1; } opts.width = renderer.width * renderer.ratio; opts.height = renderer.height * renderer.ratio; if (!renderer.core->init(renderer.core)) { freeArguments(&args); return 1; } mInputMapInit(&renderer.core->inputMap, &GBAInputInfo); mCoreInitConfig(renderer.core, PORT); applyArguments(&args, &subparser, &renderer.core->config); mCoreConfigLoadDefaults(&renderer.core->config, &opts); mCoreLoadConfig(renderer.core); renderer.viewportWidth = renderer.core->opts.width; renderer.viewportHeight = renderer.core->opts.height; #if SDL_VERSION_ATLEAST(2, 0, 0) renderer.player.fullscreen = renderer.core->opts.fullscreen; renderer.player.windowUpdated = 0; #else renderer.fullscreen = renderer.core->opts.fullscreen; #endif renderer.lockAspectRatio = renderer.core->opts.lockAspectRatio; renderer.filter = renderer.core->opts.resampleVideo; if (!mSDLInit(&renderer)) { freeArguments(&args); renderer.core->deinit(renderer.core); return 1; } renderer.player.bindings = &renderer.core->inputMap; mSDLInitBindingsGBA(&renderer.core->inputMap); mSDLInitEvents(&renderer.events); mSDLEventsLoadConfig(&renderer.events, mCoreConfigGetInput(&renderer.core->config)); mSDLAttachPlayer(&renderer.events, &renderer.player); mSDLPlayerLoadConfig(&renderer.player, mCoreConfigGetInput(&renderer.core->config)); int ret; // TODO: Use opts and config ret = mSDLRun(&renderer, &args); mSDLDetachPlayer(&renderer.events, &renderer.player); mInputMapDeinit(&renderer.core->inputMap); mSDLDeinit(&renderer); freeArguments(&args); mCoreConfigFreeOpts(&opts); mCoreConfigDeinit(&renderer.core->config); renderer.core->deinit(renderer.core); return ret; } int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { struct mCoreThread thread = { .core = renderer->core }; if (!mCoreLoadFile(renderer->core, args->fname)) { return 1; } mCoreAutoloadSave(renderer->core); struct mDebugger* debugger = mDebuggerCreate(args->debuggerType, renderer->core); if (debugger) { mDebuggerAttach(debugger, renderer->core); mDebuggerEnter(debugger, DEBUGGER_ENTER_MANUAL, NULL); } if (args->patch) { struct VFile* patch = VFileOpen(args->patch, O_RDONLY); if (patch) { renderer->core->loadPatch(renderer->core, patch); } } else { mCoreAutoloadPatch(renderer->core); } renderer->audio.samples = renderer->core->opts.audioBuffers; renderer->audio.sampleRate = 44100; bool didFail = !mSDLInitAudio(&renderer->audio, &thread); if (!didFail) { #if SDL_VERSION_ATLEAST(2, 0, 0) mSDLSetScreensaverSuspendable(&renderer->events, renderer->core->opts.suspendScreensaver); mSDLSuspendScreensaver(&renderer->events); #endif if (mCoreThreadStart(&thread)) { renderer->runloop(renderer, &thread); mSDLPauseAudio(&renderer->audio); mCoreThreadJoin(&thread); } else { didFail = true; printf("Could not run game. Are you sure the file exists and is a compatible game?\n"); } #if SDL_VERSION_ATLEAST(2, 0, 0) mSDLResumeScreensaver(&renderer->events); mSDLSetScreensaverSuspendable(&renderer->events, false); #endif if (mCoreThreadHasCrashed(&thread)) { didFail = true; printf("The game crashed!\n"); } } renderer->core->unloadROM(renderer->core); return didFail; } static bool mSDLInit(struct mSDLRenderer* renderer) { if (SDL_Init(SDL_INIT_VIDEO) < 0) { printf("Could not initialize video: %s\n", SDL_GetError()); return false; } return renderer->init(renderer); } static void mSDLDeinit(struct mSDLRenderer* renderer) { mSDLDeinitEvents(&renderer->events); mSDLDeinitAudio(&renderer->audio); #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_DestroyWindow(renderer->window); #endif renderer->deinit(renderer); SDL_Quit(); }
void retro_run(void) { uint16_t keys; inputPollCallback(); bool updated = false; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) { struct retro_variable var = { .key = "mgba_allow_opposing_directions", .value = 0 }; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { ((struct GBA*) core->board)->allowOpposingDirections = strcmp(var.value, "yes") == 0; } var.key = "mgba_frameskip"; var.value = 0; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { mCoreConfigSetUIntValue(&core->config, "frameskip", strtol(var.value, NULL, 10)); mCoreLoadConfig(core); } } keys = 0; keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A)) << 0; keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) << 1; keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) << 2; keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) << 3; keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT)) << 4; keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT)) << 5; keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP)) << 6; keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)) << 7; keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R)) << 8; keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L)) << 9; core->setKeys(core, keys); static bool wasAdjustingLux = false; if (wasAdjustingLux) { wasAdjustingLux = inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3) || inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3); } else { if (inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3)) { ++luxLevel; if (luxLevel > 10) { luxLevel = 10; } wasAdjustingLux = true; } else if (inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3)) { --luxLevel; if (luxLevel < 0) { luxLevel = 0; } wasAdjustingLux = true; } } core->runFrame(core); unsigned width, height; core->desiredVideoDimensions(core, &width, &height); videoCallback(outputBuffer, width, height, BYTES_PER_PIXEL * 256); } static void _setupMaps(struct mCore* core) { #ifdef M_CORE_GBA if (core->platform(core) == PLATFORM_GBA) { struct GBA* gba = core->board; struct retro_memory_descriptor descs[11]; struct retro_memory_map mmaps; size_t romSize = gba->memory.romSize + (gba->memory.romSize & 1); memset(descs, 0, sizeof(descs)); size_t savedataSize = retro_get_memory_size(RETRO_MEMORY_SAVE_RAM); /* Map internal working RAM */ descs[0].ptr = gba->memory.iwram; descs[0].start = BASE_WORKING_IRAM; descs[0].len = SIZE_WORKING_IRAM; descs[0].select = 0xFF000000; /* Map working RAM */ descs[1].ptr = gba->memory.wram; descs[1].start = BASE_WORKING_RAM; descs[1].len = SIZE_WORKING_RAM; descs[1].select = 0xFF000000; /* Map save RAM */ /* TODO: if SRAM is flash, use start=0 addrspace="S" instead */ descs[2].ptr = savedataSize ? savedata : NULL; descs[2].start = BASE_CART_SRAM; descs[2].len = savedataSize; /* Map ROM */ descs[3].ptr = gba->memory.rom; descs[3].start = BASE_CART0; descs[3].len = romSize; descs[3].flags = RETRO_MEMDESC_CONST; descs[4].ptr = gba->memory.rom; descs[4].start = BASE_CART1; descs[4].len = romSize; descs[4].flags = RETRO_MEMDESC_CONST; descs[5].ptr = gba->memory.rom; descs[5].start = BASE_CART2; descs[5].len = romSize; descs[5].flags = RETRO_MEMDESC_CONST; /* Map BIOS */ descs[6].ptr = gba->memory.bios; descs[6].start = BASE_BIOS; descs[6].len = SIZE_BIOS; descs[6].flags = RETRO_MEMDESC_CONST; /* Map VRAM */ descs[7].ptr = gba->video.vram; descs[7].start = BASE_VRAM; descs[7].len = SIZE_VRAM; descs[7].select = 0xFF000000; /* Map palette RAM */ descs[8].ptr = gba->video.palette; descs[8].start = BASE_PALETTE_RAM; descs[8].len = SIZE_PALETTE_RAM; descs[8].select = 0xFF000000; /* Map OAM */ descs[9].ptr = &gba->video.oam; /* video.oam is a structure */ descs[9].start = BASE_OAM; descs[9].len = SIZE_OAM; descs[9].select = 0xFF000000; /* Map mmapped I/O */ descs[10].ptr = gba->memory.io; descs[10].start = BASE_IO; descs[10].len = SIZE_IO; mmaps.descriptors = descs; mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]); bool yes = true; environCallback(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmaps); environCallback(RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS, &yes); } #endif } void retro_reset(void) { core->reset(core); _setupMaps(core); if (rumbleCallback) { CircleBufferClear(&rumbleHistory); } }