libretro.cpp (17679B)
1/* 2 * Gearboy - Nintendo Game Boy Emulator 3 * Copyright (C) 2012 Ignacio Sanchez 4 * Copyright (C) 2017 Andrés Suárez 5 * Copyright (C) 2017 Brad Parker 6 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * any later version. 11 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see http://www.gnu.org/licenses/ 19 * 20 */ 21 22#include <stdio.h> 23#include <stdint.h> 24#include <stdlib.h> 25#include <stdarg.h> 26#include <string.h> 27#include <math.h> 28 29#include <stdio.h> 30#include "libretro.h" 31 32#include "../../src/gearboy.h" 33 34#define VIDEO_WIDTH 160 35#define VIDEO_HEIGHT 144 36 37#ifdef _WIN32 38static const char slash = '\\'; 39#else 40static const char slash = '/'; 41#endif 42 43static u16* gearboy_frame_buf; 44 45static struct retro_log_callback logging; 46static retro_log_printf_t log_cb; 47static char retro_system_directory[4096]; 48static char retro_game_path[4096]; 49 50static s16 audio_buf[AUDIO_BUFFER_SIZE]; 51static int audio_sample_count; 52 53static bool force_dmg = false; 54static bool force_gba = false; 55static bool allow_up_down = false; 56static bool bootrom_dmg = false; 57static bool bootrom_gbc = false; 58static bool libretro_supports_bitmasks; 59 60static void fallback_log(enum retro_log_level level, const char *fmt, ...) 61{ 62 (void)level; 63 va_list va; 64 va_start(va, fmt); 65 vfprintf(stderr, fmt, va); 66 va_end(va); 67} 68 69static GearboyCore* core; 70static Cartridge::CartridgeTypes mapper = Cartridge::CartridgeNotSupported; 71 72static retro_environment_t environ_cb; 73 74static const struct retro_variable vars[] = { 75 { "gearboy_model", "Game Boy Model (restart); Auto|Game Boy DMG|Game Boy Advance" }, 76 { "gearboy_mapper", "Mapper (restart); Auto|ROM Only|MBC 1|MBC 2|MBC 3|MBC 5|MBC 1 Multicart" }, 77 { "gearboy_palette", "DMG Palette; Original|Sharp|B/W|Autumn|Soft|Slime" }, 78 { "gearboy_bootrom_dmg", "DMG Bootrom (restart); Disabled|Enabled" }, 79 { "gearboy_bootrom_gbc", "Game Boy Color Bootrom (restart); Disabled|Enabled" }, 80 { "gearboy_up_down_allowed", "Allow Up+Down / Left+Right; Disabled|Enabled" }, 81 82 { NULL } 83}; 84 85// red, green, blue 86static GB_Color original_palette[4] = {{0x87, 0x96, 0x03},{0x4D, 0x6B, 0x03},{0x2B, 0x55, 0x03},{0x14, 0x44, 0x03}}; 87static GB_Color sharp_palette[4] = {{0xF5, 0xFA, 0xEF},{0x86, 0xC2, 0x70},{0x2F, 0x69, 0x57},{0x0B, 0x19, 0x20}}; 88static GB_Color bw_palette[4] = {{0xFF, 0xFF, 0xFF},{0xAA, 0xAA, 0xAA},{0x55, 0x55, 0x55},{0x00, 0x00, 0x00}}; 89static GB_Color autumn_palette[4] = {{0xF8, 0xE8, 0xC8},{0xD8, 0x90, 0x48},{0xA8, 0x34, 0x20},{0x30, 0x18, 0x50}}; 90static GB_Color soft_palette[4] = {{0xE0, 0xE0, 0xAA},{0xB0, 0xB8, 0x7C},{0x72, 0x82, 0x5B},{0x39, 0x34, 0x17}}; 91static GB_Color slime_palette[4] = {{0xD4, 0xEB, 0xA5},{0x62, 0xB8, 0x7C},{0x27, 0x76, 0x5D},{0x1D, 0x39, 0x39}}; 92 93static GB_Color* current_palette = original_palette; 94 95void retro_init(void) 96{ 97 const char *dir = NULL; 98 99 if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir) { 100 snprintf(retro_system_directory, sizeof(retro_system_directory), "%s", dir); 101 } 102 else { 103 snprintf(retro_system_directory, sizeof(retro_system_directory), "%s", "."); 104 } 105 106 core = new GearboyCore(); 107 108#ifdef PS2 109 core->Init(GB_PIXEL_BGR555); 110#else 111 core->Init(GB_PIXEL_RGB565); 112#endif 113 114 gearboy_frame_buf = new u16[VIDEO_WIDTH * VIDEO_HEIGHT]; 115 116 audio_sample_count = 0; 117 libretro_supports_bitmasks = environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL); 118} 119 120void retro_deinit(void) 121{ 122 SafeDeleteArray(gearboy_frame_buf); 123 SafeDelete(core); 124} 125 126unsigned retro_api_version(void) 127{ 128 return RETRO_API_VERSION; 129} 130 131void retro_set_controller_port_device(unsigned port, unsigned device) 132{ 133 log_cb(RETRO_LOG_INFO, "Plugging device %u into port %u.\n", device, port); 134} 135 136void retro_get_system_info(struct retro_system_info *info) 137{ 138 memset(info, 0, sizeof(*info)); 139 info->library_name = GEARBOY_TITLE; 140 info->library_version = GEARBOY_VERSION; 141 info->need_fullpath = false; 142 info->valid_extensions = "gb|dmg|gbc|cgb|sgb"; 143} 144 145static retro_video_refresh_t video_cb; 146static retro_audio_sample_t audio_cb; 147static retro_audio_sample_batch_t audio_batch_cb; 148static retro_input_poll_t input_poll_cb; 149static retro_input_state_t input_state_cb; 150 151void retro_get_system_av_info(struct retro_system_av_info *info) 152{ 153 float aspect = (float)VIDEO_WIDTH/VIDEO_HEIGHT; 154 info->geometry.base_width = VIDEO_WIDTH; 155 info->geometry.base_height = VIDEO_HEIGHT; 156 info->geometry.max_width = VIDEO_WIDTH; 157 info->geometry.max_height = VIDEO_HEIGHT; 158 info->geometry.aspect_ratio = aspect; 159 info->timing.fps = 4194304.0 / 70224.0; 160 info->timing.sample_rate = 44100.0f; 161} 162 163void retro_set_environment(retro_environment_t cb) 164{ 165 environ_cb = cb; 166 167 if (cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging)) 168 log_cb = logging.log; 169 else 170 log_cb = fallback_log; 171 172 static const struct retro_controller_description controllers[] = { 173 { "Nintendo Gameboy", RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0) }, 174 }; 175 176 static const struct retro_controller_info ports[] = { 177 { controllers, 1 }, 178 { NULL, 0 }, 179 }; 180 181 cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports); 182 183 environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars); 184} 185 186void retro_set_audio_sample(retro_audio_sample_t cb) 187{ 188 audio_cb = cb; 189} 190 191void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) 192{ 193 audio_batch_cb = cb; 194} 195 196void retro_set_input_poll(retro_input_poll_t cb) 197{ 198 input_poll_cb = cb; 199} 200 201void retro_set_input_state(retro_input_state_t cb) 202{ 203 input_state_cb = cb; 204} 205 206void retro_set_video_refresh(retro_video_refresh_t cb) 207{ 208 video_cb = cb; 209} 210 211static void load_bootroms(void) 212{ 213 char bootrom_dmg_path[4112]; 214 char bootrom_gbc_path[4112]; 215 216 sprintf(bootrom_dmg_path, "%s%cdmg_boot.bin", retro_system_directory, slash); 217 sprintf(bootrom_gbc_path, "%s%ccgb_boot.bin", retro_system_directory, slash); 218 219 core->GetMemory()->LoadBootromDMG(bootrom_dmg_path); 220 core->GetMemory()->LoadBootromGBC(bootrom_gbc_path); 221 core->GetMemory()->EnableBootromDMG(bootrom_dmg); 222 core->GetMemory()->EnableBootromGBC(bootrom_gbc); 223} 224 225static void update_input(void) 226{ 227 input_poll_cb(); 228 229 int16_t ib; 230 if (libretro_supports_bitmasks) 231 ib = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); 232 else 233 { 234 unsigned int i; 235 ib = 0; 236 for (i = 0; i <= RETRO_DEVICE_ID_JOYPAD_R3; i++) 237 ib |= input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, i) ? (1 << i) : 0; 238 } 239 240 if (ib & (1 << RETRO_DEVICE_ID_JOYPAD_UP)) 241 { 242 if (allow_up_down || !(ib & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN))) 243 core->KeyPressed(Up_Key); 244 } 245 else 246 core->KeyReleased(Up_Key); 247 248 if (ib & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN)) 249 { 250 if (allow_up_down || !(ib & (1 << RETRO_DEVICE_ID_JOYPAD_UP))) 251 core->KeyPressed(Down_Key); 252 } 253 else 254 core->KeyReleased(Down_Key); 255 256 if (ib & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT)) 257 { 258 if (allow_up_down || !(ib & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT))) 259 core->KeyPressed(Left_Key); 260 } 261 else 262 core->KeyReleased(Left_Key); 263 264 if (ib & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT)) 265 { 266 if (allow_up_down || !(ib & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT))) 267 core->KeyPressed(Right_Key); 268 } 269 else 270 core->KeyReleased(Right_Key); 271 272 if (ib & (1 << RETRO_DEVICE_ID_JOYPAD_B)) 273 core->KeyPressed(B_Key); 274 else 275 core->KeyReleased(B_Key); 276 if (ib & (1 << RETRO_DEVICE_ID_JOYPAD_A)) 277 core->KeyPressed(A_Key); 278 else 279 core->KeyReleased(A_Key); 280 if (ib & (1 << RETRO_DEVICE_ID_JOYPAD_START)) 281 core->KeyPressed(Start_Key); 282 else 283 core->KeyReleased(Start_Key); 284 if (ib & (1 << RETRO_DEVICE_ID_JOYPAD_SELECT)) 285 core->KeyPressed(Select_Key); 286 else 287 core->KeyReleased(Select_Key); 288} 289 290static void check_variables(void) 291{ 292 struct retro_variable var = {0}; 293 294 var.key = "gearboy_model"; 295 var.value = NULL; 296 297 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) 298 { 299 if (strcmp(var.value, "Game Boy DMG") == 0) 300 { 301 force_dmg = true; 302 force_gba = false; 303 } 304 else if (strcmp(var.value, "Game Boy Advance") == 0) 305 { 306 force_gba = true; 307 force_dmg = false; 308 } 309 else 310 { 311 force_dmg = false; 312 force_gba = false; 313 } 314 } 315 316 var.key = "gearboy_mapper"; 317 var.value = NULL; 318 319 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) 320 { 321 if (strcmp(var.value, "Auto") == 0) 322 mapper = Cartridge::CartridgeNotSupported; 323 else if (strcmp(var.value, "ROM Only") == 0) 324 mapper = Cartridge::CartridgeNoMBC; 325 else if (strcmp(var.value, "MBC 1") == 0) 326 mapper = Cartridge::CartridgeMBC1; 327 else if (strcmp(var.value, "MBC 2") == 0) 328 mapper = Cartridge::CartridgeMBC2; 329 else if (strcmp(var.value, "MBC 3") == 0) 330 mapper = Cartridge::CartridgeMBC3; 331 else if (strcmp(var.value, "MBC 5") == 0) 332 mapper = Cartridge::CartridgeMBC5; 333 else if (strcmp(var.value, "MBC 1 Multicart") == 0) 334 mapper = Cartridge::CartridgeMBC1Multi; 335 else 336 mapper = Cartridge::CartridgeNotSupported; 337 } 338 339 var.key = "gearboy_palette"; 340 var.value = NULL; 341 342 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) 343 { 344 if (strcmp(var.value, "Original") == 0) 345 current_palette = original_palette; 346 else if (strcmp(var.value, "Sharp") == 0) 347 current_palette = sharp_palette; 348 else if (strcmp(var.value, "B/W") == 0) 349 current_palette = bw_palette; 350 else if (strcmp(var.value, "Autumn") == 0) 351 current_palette = autumn_palette; 352 else if (strcmp(var.value, "Soft") == 0) 353 current_palette = soft_palette; 354 else if (strcmp(var.value, "Slime") == 0) 355 current_palette = slime_palette; 356 else 357 current_palette = original_palette; 358 } 359 360 var.key = "gearboy_bootrom_dmg"; 361 var.value = NULL; 362 363 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) 364 { 365 if (strcmp(var.value, "Enabled") == 0) 366 bootrom_dmg = true; 367 else 368 bootrom_dmg = false; 369 } 370 371 var.key = "gearboy_bootrom_gbc"; 372 var.value = NULL; 373 374 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) 375 { 376 if (strcmp(var.value, "Enabled") == 0) 377 bootrom_gbc = true; 378 else 379 bootrom_gbc = false; 380 } 381 382 var.key = "gearboy_up_down_allowed"; 383 var.value = NULL; 384 385 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) 386 { 387 if (strcmp(var.value, "Enabled") == 0) 388 allow_up_down = true; 389 else 390 allow_up_down = false; 391 } 392} 393 394void retro_run(void) 395{ 396 bool updated = false; 397 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) 398 { 399 check_variables(); 400 core->SetDMGPalette(current_palette[0], current_palette[1], current_palette[2], current_palette[3]); 401 } 402 403 update_input(); 404 405 core->RunToVBlank(gearboy_frame_buf, audio_buf, &audio_sample_count); 406 407 video_cb((uint8_t*)gearboy_frame_buf, VIDEO_WIDTH, VIDEO_HEIGHT, VIDEO_WIDTH * sizeof(u16)); 408 409 if (audio_sample_count > 0) 410 audio_batch_cb(audio_buf, audio_sample_count / 2); 411 412 audio_sample_count = 0; 413} 414 415void retro_reset(void) 416{ 417 check_variables(); 418 load_bootroms(); 419 420 core->SetDMGPalette(current_palette[0], current_palette[1], current_palette[2], current_palette[3]); 421 422 core->ResetROMPreservingRAM(force_dmg, mapper, force_gba); 423} 424 425 426bool retro_load_game(const struct retro_game_info *info) 427{ 428 check_variables(); 429 load_bootroms(); 430 431 core->SetDMGPalette(current_palette[0], current_palette[1], current_palette[2], current_palette[3]); 432 433 if (!core->LoadROMFromBuffer(reinterpret_cast<const u8*>(info->data), info->size, force_dmg, mapper, force_gba)) 434 { 435 log_cb(RETRO_LOG_ERROR, "Invalid or corrupted ROM.\n"); 436 return false; 437 } 438 439 struct retro_input_descriptor desc[] = { 440 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" }, 441 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" }, 442 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" }, 443 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" }, 444 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, 445 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" }, 446 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" }, 447 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" }, 448 { 0 }, 449 }; 450 451 environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc); 452 453 enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565; 454 455 if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) 456 { 457 log_cb(RETRO_LOG_INFO, "RETRO_PIXEL_FORMAT_RGB565 is not supported.\n"); 458 return false; 459 } 460 461 snprintf(retro_game_path, sizeof(retro_game_path), "%s", info->path); 462 463 struct retro_memory_descriptor descs[11]; 464 465 memset(descs, 0, sizeof(descs)); 466 467 // IE 468 descs[0].ptr = core->GetMemory()->GetMemoryMap() + 0xFFFF; 469 descs[0].start = 0xFFFF; 470 descs[0].len = 1; 471 // HRAM 472 descs[1].ptr = core->GetMemory()->GetMemoryMap() + 0xFF80; 473 descs[1].start = 0xFF80; 474 descs[1].len = 0x0080; 475 // RAM bank 0 476 descs[2].ptr = core->IsCGB() ? core->GetMemory()->GetCGBRAM() : (core->GetMemory()->GetMemoryMap() + 0xC000); 477 descs[2].start = 0xC000; 478 descs[2].len = 0x1000; 479 // RAM bank 1 480 descs[3].ptr = core->IsCGB() ? (core->GetMemory()->GetCGBRAM() + (0x1000)) : (core->GetMemory()->GetMemoryMap() + 0xD000); 481 descs[3].start = 0xD000; 482 descs[3].len = 0x1000; 483 // CART RAM 484 descs[4].ptr = core->GetMemory()->GetCurrentRule()->GetCurrentRamBank(); 485 descs[4].start = 0xA000; 486 descs[4].len = 0x2000; 487 // VRAM 488 descs[5].ptr = core->GetMemory()->GetMemoryMap() + 0x8000; // todo: fix GBC 489 descs[5].start = 0x8000; 490 descs[5].len = 0x2000; 491 // ROM bank 0 492 descs[6].ptr = core->GetMemory()->GetCurrentRule()->GetRomBank0(); 493 descs[6].start = 0x0000; 494 descs[6].len = 0x4000; 495 // ROM bank x 496 descs[7].ptr = core->GetMemory()->GetCurrentRule()->GetCurrentRomBank1(); 497 descs[7].start = 0x4000; 498 descs[7].len = 0x4000; 499 // OAM 500 descs[8].ptr = core->GetMemory()->GetMemoryMap() + 0xFE00; 501 descs[8].start = 0xFE00; 502 descs[8].len = 0x00A0; 503 descs[8].select= 0xFFFFFF00; 504 // CGB RAM banks 2-7 505 descs[9].ptr = core->IsCGB() ? (core->GetMemory()->GetCGBRAM() + 0x2000) : (core->GetMemory()->GetMemoryMap() + 0xD000); 506 descs[9].start = 0x10000; 507 descs[9].len = core->IsCGB() ? 0x6000 : 0; 508 descs[9].select= 0xFFFF0000; 509 // IO PORTS 510 descs[10].ptr = core->GetMemory()->GetMemoryMap() + 0xFF00; 511 descs[10].start = 0xFF00; 512 descs[10].len = 0x0080; 513 descs[10].select= 0xFFFFFF00; 514 515 struct retro_memory_map mmaps; 516 mmaps.descriptors = descs; 517 mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]); 518 environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmaps); 519 520 bool achievements = true; 521 environ_cb(RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS, &achievements); 522 523 return true; 524} 525 526void retro_unload_game(void) 527{ 528} 529 530unsigned retro_get_region(void) 531{ 532 return RETRO_REGION_NTSC; 533} 534 535bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num) 536{ 537 return false; 538} 539 540size_t retro_serialize_size(void) 541{ 542 size_t size; 543 core->SaveState(NULL, size); 544 return size; 545} 546 547bool retro_serialize(void *data, size_t size) 548{ 549 return core->SaveState(reinterpret_cast<u8*>(data), size); 550} 551 552bool retro_unserialize(const void *data, size_t size) 553{ 554 return core->LoadState(reinterpret_cast<const u8*>(data), size); 555} 556 557void *retro_get_memory_data(unsigned id) 558{ 559 switch (id) 560 { 561 case RETRO_MEMORY_SAVE_RAM: 562 return core->GetMemory()->GetCurrentRule()->GetRamBanks(); 563 case RETRO_MEMORY_RTC: 564 return core->GetMemory()->GetCurrentRule()->GetRTCMemory(); 565 case RETRO_MEMORY_SYSTEM_RAM: 566 return core->IsCGB() ? core->GetMemory()->GetCGBRAM() : core->GetMemory()->GetMemoryMap() + 0xC000; 567 } 568 569 return NULL; 570} 571 572size_t retro_get_memory_size(unsigned id) 573{ 574 switch (id) 575 { 576 case RETRO_MEMORY_SAVE_RAM: 577 return core->GetMemory()->GetCurrentRule()->GetRamSize(); 578 case RETRO_MEMORY_RTC: 579 return core->GetMemory()->GetCurrentRule()->GetRTCSize(); 580 case RETRO_MEMORY_SYSTEM_RAM: 581 return core->IsCGB() ? 0x8000 : 0x2000; 582 } 583 584 return 0; 585} 586 587void retro_cheat_reset(void) 588{ 589 core->ClearCheats(); 590} 591 592void retro_cheat_set(unsigned index, bool enabled, const char *code) 593{ 594 core->SetCheat(code); 595}