diff options
Diffstat (limited to 'gearboy/platforms/desktop-shared/gui.cpp')
| -rw-r--r-- | gearboy/platforms/desktop-shared/gui.cpp | 1446 |
1 files changed, 1446 insertions, 0 deletions
diff --git a/gearboy/platforms/desktop-shared/gui.cpp b/gearboy/platforms/desktop-shared/gui.cpp new file mode 100644 index 00000000..6e5a671b --- /dev/null +++ b/gearboy/platforms/desktop-shared/gui.cpp @@ -0,0 +1,1446 @@ +/* + * Gearboy - Nintendo Game Boy Emulator + * Copyright (C) 2012 Ignacio Sanchez + + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ + * + */ + +#include "imgui/imgui.h" +#include "imgui/imgui_memory_editor.h" +#include "imgui/fonts/RobotoMedium.h" +#include "FileBrowser/ImGuiFileBrowser.h" +#include "config.h" +#include "emu.h" +#include "../../src/gearboy.h" +#include "renderer.h" +#include "application.h" +#include "license.h" +#include "backers.h" +#include "gui_debug.h" + +#define GUI_IMPORT +#include "gui.h" + +static imgui_addons::ImGuiFileBrowser file_dialog; +static int main_menu_height; +static bool dialog_in_use = false; +static SDL_Scancode* configured_key; +static int* configured_button; +static ImVec4 custom_palette[config_max_custom_palettes][4]; +static std::list<std::string> cheat_list; +static bool shortcut_open_rom = false; +static ImFont* default_font[4]; +static char dmg_bootrom_path[4096] = ""; +static char gbc_bootrom_path[4096] = ""; +static char savefiles_path[4096] = ""; +static char savestates_path[4096] = ""; +static bool show_main_menu = true; + +static void main_menu(void); +static void main_window(void); +static void file_dialog_open_rom(void); +static void file_dialog_load_ram(void); +static void file_dialog_save_ram(void); +static void file_dialog_load_state(void); +static void file_dialog_save_state(void); +static void file_dialog_choose_save_file_path(void); +static void file_dialog_choose_savestate_path(void); +static void file_dialog_load_dmg_bootrom(void); +static void file_dialog_load_gbc_bootrom(void); +static void file_dialog_load_symbols(void); +static void keyboard_configuration_item(const char* text, SDL_Scancode* key); +static void gamepad_configuration_item(const char* text, int* button); +static void popup_modal_keyboard(void); +static void popup_modal_gamepad(void); +static void popup_modal_about(void); +static GB_Color color_float_to_int(ImVec4 color); +static ImVec4 color_int_to_float(GB_Color color); +static void update_palette(void); +static void push_recent_rom(std::string path); +static void menu_reset(void); +static void menu_pause(void); +static void menu_ffwd(void); +static void show_info(void); +static void show_fps(void); +static Cartridge::CartridgeTypes get_mbc(int index); + +void gui_init(void) +{ + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGui::StyleColorsDark(); + ImGuiIO& io = ImGui::GetIO(); + + io.IniFilename = config_imgui_file_path; + + io.FontGlobalScale /= application_display_scale; + + gui_roboto_font = io.Fonts->AddFontFromMemoryCompressedTTF(RobotoMedium_compressed_data, RobotoMedium_compressed_size, 17.0f * application_display_scale, NULL, io.Fonts->GetGlyphRangesCyrillic()); + + ImFontConfig font_cfg; + + for (int i = 0; i < 4; i++) + { + font_cfg.SizePixels = (13.0f + (i * 3)) * application_display_scale; + default_font[i] = io.Fonts->AddFontDefault(&font_cfg); + } + + gui_default_font = default_font[config_debug.font_size]; + + update_palette(); + + emu_audio_volume(config_audio.enable ? 1.0f: 0.0f); + emu_color_correction(config_video.color_correction); + + strcpy(dmg_bootrom_path, config_emulator.dmg_bootrom_path.c_str()); + strcpy(gbc_bootrom_path, config_emulator.gbc_bootrom_path.c_str()); + strcpy(savefiles_path, config_emulator.savefiles_path.c_str()); + strcpy(savestates_path, config_emulator.savestates_path.c_str()); + + if (strlen(dmg_bootrom_path) > 0) + emu_load_bootrom_dmg(dmg_bootrom_path); + if (strlen(gbc_bootrom_path) > 0) + emu_load_bootrom_gbc(gbc_bootrom_path); + + emu_enable_bootrom_dmg(config_emulator.dmg_bootrom); + emu_enable_bootrom_gbc(config_emulator.gbc_bootrom); +} + +void gui_destroy(void) +{ + ImGui::DestroyContext(); +} + +void gui_render(void) +{ + ImGui::NewFrame(); + + gui_in_use = dialog_in_use; + + main_menu(); + + if((!config_debug.debug && !emu_is_empty()) || (config_debug.debug && config_debug.show_gameboy)) + main_window(); + + gui_debug_windows(); + + ImGui::Render(); +} + +void gui_shortcut(gui_ShortCutEvent event) +{ + switch (event) + { + case gui_ShortcutOpenROM: + shortcut_open_rom = true; + break; + case gui_ShortcutReset: + menu_reset(); + break; + case gui_ShortcutPause: + menu_pause(); + break; + case gui_ShortcutFFWD: + config_emulator.ffwd = !config_emulator.ffwd; + menu_ffwd(); + break; + case gui_ShortcutSaveState: + emu_save_state_slot(config_emulator.save_slot + 1); + break; + case gui_ShortcutLoadState: + emu_load_state_slot(config_emulator.save_slot + 1); + break; + case gui_ShortcutDebugStep: + if (config_debug.debug) + emu_debug_step(); + break; + case gui_ShortcutDebugContinue: + if (config_debug.debug) + emu_debug_continue(); + break; + case gui_ShortcutDebugNextFrame: + if (config_debug.debug) + emu_debug_next_frame(); + break; + case gui_ShortcutDebugBreakpoint: + if (config_debug.debug) + gui_debug_toggle_breakpoint(); + break; + case gui_ShortcutDebugRuntocursor: + if (config_debug.debug) + gui_debug_runtocursor(); + break; + case gui_ShortcutDebugGoBack: + if (config_debug.debug) + gui_debug_go_back(); + break; + case gui_ShortcutShowMainMenu: + show_main_menu = !show_main_menu; + break; + default: + break; + } +} + +void gui_load_rom(const char* path) +{ + push_recent_rom(path); + emu_resume(); + emu_load_rom(path, config_emulator.force_dmg, get_mbc(config_emulator.mbc), config_emulator.force_gba); + cheat_list.clear(); + emu_clear_cheats(); + + gui_debug_reset(); + + std::string str(path); + str = str.substr(0, str.find_last_of(".")); + str += ".sym"; + gui_debug_load_symbols_file(str.c_str()); + + if (config_emulator.start_paused) + { + emu_pause(); + + for (int i=0; i < (GAMEBOY_WIDTH * GAMEBOY_HEIGHT); i++) + { + emu_frame_buffer[i].red = 0; + emu_frame_buffer[i].green = 0; + emu_frame_buffer[i].blue = 0; + } + } +} + +static void main_menu(void) +{ + bool open_rom = false; + bool open_ram = false; + bool save_ram = false; + bool open_state = false; + bool save_state = false; + bool open_about = false; + bool open_symbols = false; + bool choose_save_file_path = false; + bool choose_savestates_path = false; + bool open_dmg_bootrom = false; + bool open_gbc_bootrom = false; + + for (int i = 0; i < config_max_custom_palettes; i++) + for (int c = 0; c < 4; c++) + custom_palette[i][c] = color_int_to_float(config_video.color[i][c]); + + if (show_main_menu && ImGui::BeginMainMenuBar()) + { + if (ImGui::BeginMenu(GEARBOY_TITLE)) + { + gui_in_use = true; + + if (ImGui::MenuItem("Open ROM...", "Ctrl+O")) + { + open_rom = true; + } + + if (ImGui::BeginMenu("Open Recent")) + { + for (int i = 0; i < config_max_recent_roms; i++) + { + if (config_emulator.recent_roms[i].length() > 0) + { + if (ImGui::MenuItem(config_emulator.recent_roms[i].c_str())) + { + char rom_path[4096]; + strcpy(rom_path, config_emulator.recent_roms[i].c_str()); + gui_load_rom(rom_path); + } + } + } + + ImGui::EndMenu(); + } + + ImGui::Separator(); + + if (ImGui::MenuItem("Reset", "Ctrl+R")) + { + menu_reset(); + } + + if (ImGui::MenuItem("Pause", "Ctrl+P", &config_emulator.paused)) + { + menu_pause(); + } + + ImGui::Separator(); + + if (ImGui::MenuItem("Fast Forward", "Ctrl+F", &config_emulator.ffwd)) + { + menu_ffwd(); + } + + if (ImGui::BeginMenu("Fast Forward Speed")) + { + ImGui::PushItemWidth(100.0f); + ImGui::Combo("##fwd", &config_emulator.ffwd_speed, "X 1.5\0X 2\0X 2.5\0X 3\0Unlimited\0\0"); + ImGui::PopItemWidth(); + ImGui::EndMenu(); + } + + ImGui::Separator(); + + if (ImGui::MenuItem("Save RAM As...")) + { + save_ram = true; + } + + if (ImGui::MenuItem("Load RAM From...")) + { + open_ram = true; + } + + ImGui::Separator(); + + if (ImGui::MenuItem("Save State As...")) + { + save_state = true; + } + + if (ImGui::MenuItem("Load State From...")) + { + open_state = true; + } + + ImGui::Separator(); + + if (ImGui::BeginMenu("Save State Slot")) + { + ImGui::PushItemWidth(100.0f); + ImGui::Combo("##slot", &config_emulator.save_slot, "Slot 1\0Slot 2\0Slot 3\0Slot 4\0Slot 5\0\0"); + ImGui::PopItemWidth(); + ImGui::EndMenu(); + } + + if (ImGui::MenuItem("Save State", "Ctrl+S")) + { + emu_save_state_slot(config_emulator.save_slot + 1); + } + + if (ImGui::MenuItem("Load State", "Ctrl+L")) + { + emu_load_state_slot(config_emulator.save_slot + 1); + } + + ImGui::Separator(); + + if (ImGui::MenuItem("Quit", "ESC")) + { + application_trigger_quit(); + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Emulator")) + { + gui_in_use = true; + + if (ImGui::MenuItem("Force Game Boy (DMG)", "", &config_emulator.force_dmg)) + { + if (config_emulator.force_dmg) + config_emulator.force_gba = false; + } + + if (ImGui::MenuItem("Force Game Boy Advance", "", &config_emulator.force_gba)) + { + if (config_emulator.force_gba) + config_emulator.force_dmg = false; + } + + if (ImGui::BeginMenu("Memory Bank Controller")) + { + ImGui::PushItemWidth(140.0f); + ImGui::Combo("##mbc", &config_emulator.mbc, "Auto\0ROM Only\0MBC 1\0MBC 2\0MBC 3\0MBC 5\0MBC 1 Multicart\0\0"); + ImGui::PopItemWidth(); + ImGui::EndMenu(); + } + + ImGui::Separator(); + + if (ImGui::BeginMenu("DMG Bootrom")) + { + if (ImGui::MenuItem("Enable", "", &config_emulator.dmg_bootrom)) + { + emu_enable_bootrom_dmg(config_emulator.dmg_bootrom); + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("When the bootrom is enabled it will execute as in original hardware,\ncausing invalid roms to lock or forcing hardware like GB Pocket or GBA."); + if (ImGui::MenuItem("Load Bootrom...")) + { + open_dmg_bootrom = true; + } + ImGui::PushItemWidth(350); + if (ImGui::InputText("##dmg_bootrom_path", dmg_bootrom_path, IM_ARRAYSIZE(dmg_bootrom_path), ImGuiInputTextFlags_AutoSelectAll)) + { + config_emulator.dmg_bootrom_path.assign(dmg_bootrom_path); + emu_load_bootrom_dmg(dmg_bootrom_path); + } + ImGui::PopItemWidth(); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("GBC Bootrom")) + { + if (ImGui::MenuItem("Enable", "", &config_emulator.gbc_bootrom)) + { + emu_enable_bootrom_gbc(config_emulator.gbc_bootrom); + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("When the bootrom is enabled it will execute as in original hardware,\ncausing invalid roms to lock or forcing hardware like GB Pocket or GBA."); + if (ImGui::MenuItem("Load Bootrom...")) + { + open_gbc_bootrom = true; + } + ImGui::PushItemWidth(350); + if (ImGui::InputText("##gbc_bootrom_path", gbc_bootrom_path, IM_ARRAYSIZE(gbc_bootrom_path), ImGuiInputTextFlags_AutoSelectAll)) + { + config_emulator.gbc_bootrom_path.assign(gbc_bootrom_path); + emu_load_bootrom_gbc(gbc_bootrom_path); + } + ImGui::PopItemWidth(); + ImGui::EndMenu(); + } + + ImGui::Separator(); + + ImGui::MenuItem("Start Paused", "", &config_emulator.start_paused); + + ImGui::Separator(); + + if (ImGui::BeginMenu("Save File Location")) + { + ImGui::PushItemWidth(220.0f); + if (ImGui::Combo("##savefile_option", &config_emulator.savefiles_dir_option, "Save Files In Custom Folder\0Save Files In ROM Folder\0\0")) + { + emu_savefiles_dir_option = config_emulator.savefiles_dir_option; + } + + if (config_emulator.savefiles_dir_option == 0) + { + if (ImGui::MenuItem("Choose Save File Folder...")) + { + choose_save_file_path = true; + } + + ImGui::PushItemWidth(350); + if (ImGui::InputText("##savefile_path", savefiles_path, IM_ARRAYSIZE(savefiles_path), ImGuiInputTextFlags_AutoSelectAll)) + { + config_emulator.savefiles_path.assign(savefiles_path); + strcpy(emu_savefiles_path, savefiles_path); + } + ImGui::PopItemWidth(); + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Save State Location")) + { + ImGui::PushItemWidth(220.0f); + if (ImGui::Combo("##savestate_option", &config_emulator.savestates_dir_option, "Savestates In Custom Folder\0Savestates In ROM Folder\0\0")) + { + emu_savestates_dir_option = config_emulator.savestates_dir_option; + } + + if (config_emulator.savestates_dir_option == 0) + { + if (ImGui::MenuItem("Choose Savestate Folder...")) + { + choose_savestates_path = true; + } + + ImGui::PushItemWidth(350); + if (ImGui::InputText("##savestate_path", savestates_path, IM_ARRAYSIZE(savestates_path), ImGuiInputTextFlags_AutoSelectAll)) + { + config_emulator.savestates_path.assign(savestates_path); + strcpy(emu_savestates_path, savestates_path); + } + ImGui::PopItemWidth(); + } + + ImGui::EndMenu(); + } + + ImGui::Separator(); + + ImGui::MenuItem("Show ROM info", "", &config_emulator.show_info); + + ImGui::Separator(); + + ImGui::SetNextWindowSizeConstraints({300.0f, 200.0f}, {300.0f, 500.0f}); + if (ImGui::BeginMenu("Cheats")) + { + ImGui::Text("Game Genie or GameShark codes\n(one code per line):"); + + ImGui::Columns(2, "cheats", false); + + static char cheat_buffer[20*50] = ""; + ImGui::PushItemWidth(150); + ImGui::InputTextMultiline("", cheat_buffer, IM_ARRAYSIZE(cheat_buffer)); + ImGui::PopItemWidth(); + + ImGui::NextColumn(); + + if (ImGui::Button("Add Cheat Codes")) + { + std::string cheats = cheat_buffer; + std::istringstream ss(cheats); + std::string cheat; + + while (getline(ss, cheat)) + { + if ((cheat_list.size() < 50) && ((cheat.length() == 7) || (cheat.length() == 8) || (cheat.length() == 11))) + { + cheat_list.push_back(cheat); + emu_add_cheat(cheat.c_str()); + cheat_buffer[0] = 0; + } + } + } + + if (cheat_list.size() > 0) + { + if (ImGui::Button("Clear All")) + { + cheat_list.clear(); + emu_clear_cheats(); + } + } + + ImGui::Columns(1); + + std::list<std::string>::iterator it; + + for (it = cheat_list.begin(); it != cheat_list.end(); it++) + { + if ((it->length() == 7) || (it->length() == 11)) + ImGui::Text("Game Genie: %s", it->c_str()); + else + ImGui::Text("GameShark: %s", it->c_str()); + } + + ImGui::EndMenu(); + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Video")) + { + gui_in_use = true; + + if (ImGui::MenuItem("Full Screen", "F11", &application_fullscreen)) + { + application_trigger_fullscreen(application_fullscreen); + } + + ImGui::MenuItem("Show Menu", "CTRL+M", &show_main_menu); + + ImGui::Separator(); + + if (ImGui::BeginMenu("Scale")) + { + ImGui::PushItemWidth(100.0f); + ImGui::Combo("##scale", &config_video.scale, "Auto\0Zoom X1\0Zoom X2\0Zoom X3\0Zoom X4\0\0"); + ImGui::PopItemWidth(); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Aspect Ratio")) + { + ImGui::PushItemWidth(130.0f); + ImGui::Combo("##ratio", &config_video.ratio, "Game Boy\0Standard (4:3)\0Wide (16:9)\0Fit Window\0\0"); + ImGui::PopItemWidth(); + ImGui::EndMenu(); + } + + ImGui::Separator(); + + if (ImGui::MenuItem("Vertical Sync", "", &config_video.sync)) + { + SDL_GL_SetSwapInterval(config_video.sync ? 1 : 0); + + if (config_video.sync) + { + config_audio.sync = true; + emu_audio_reset(); + } + } + + ImGui::MenuItem("Show FPS", "", &config_video.fps); + + ImGui::Separator(); + + ImGui::MenuItem("Bilinear Filtering", "", &config_video.bilinear); + + if (ImGui::MenuItem("Color Correction (GBC)", "", &config_video.color_correction)) + { + emu_color_correction(config_video.color_correction); + } + + if (ImGui::BeginMenu("Screen Ghosting")) + { + ImGui::MenuItem("Enable Screen Ghosting", "", &config_video.mix_frames); + ImGui::SliderFloat("##screen_ghosting", &config_video.mix_frames_intensity, 0.0f, 1.0f, "Intensity = %.2f"); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Dot Matrix")) + { + ImGui::MenuItem("Enable Dot Matrix", "", &config_video.matrix); + ImGui::SliderFloat("##dot_matrix", &config_video.matrix_intensity, 0.0f, 1.0f, "Intensity = %.2f"); + ImGui::EndMenu(); + } + + ImGui::Separator(); + + if (ImGui::BeginMenu("Palette")) + { + ImGui::PushItemWidth(130.0f); + if (ImGui::Combo("##palette", &config_video.palette, "Original\0Sharp\0Black & White\0Autumn\0Soft\0Slime\0Custom 1\0Custom 2\0Custom 3\0Custom 4\0Custom 5\0\0", 11)) + { + update_palette(); + } + ImGui::PopItemWidth(); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Custom Palettes")) + { + + for (int i = 0; i < config_max_custom_palettes; i++) + { + char menu_label[256]; + sprintf(menu_label, "Palette %i", i + 1); + + if (ImGui::BeginMenu(menu_label)) + { + if (ImGui::ColorEdit3("Color #1", (float*)&custom_palette[i][0], ImGuiColorEditFlags_NoInputs)) + { + update_palette(); + } + if (ImGui::ColorEdit3("Color #2", (float*)&custom_palette[i][1], ImGuiColorEditFlags_NoInputs)) + { + update_palette(); + } + if (ImGui::ColorEdit3("Color #3", (float*)&custom_palette[i][2], ImGuiColorEditFlags_NoInputs)) + { + update_palette(); + } + if (ImGui::ColorEdit3("Color #4", (float*)&custom_palette[i][3], ImGuiColorEditFlags_NoInputs)) + { + update_palette(); + } + ImGui::EndMenu(); + } + } + + ImGui::EndMenu(); + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Input")) + { + gui_in_use = true; + + if (ImGui::BeginMenu("Keyboard Configuration")) + { + keyboard_configuration_item("Left:", &config_input.key_left); + keyboard_configuration_item("Right:", &config_input.key_right); + keyboard_configuration_item("Up:", &config_input.key_up); + keyboard_configuration_item("Down:", &config_input.key_down); + keyboard_configuration_item("A:", &config_input.key_a); + keyboard_configuration_item("B:", &config_input.key_b); + keyboard_configuration_item("Start:", &config_input.key_start); + keyboard_configuration_item("Select:", &config_input.key_select); + + popup_modal_keyboard(); + + ImGui::EndMenu(); + } + + ImGui::Separator(); + + if (ImGui::BeginMenu("Gamepad")) + { + ImGui::MenuItem("Enable Gamepad", "", &config_input.gamepad); + + if (ImGui::BeginMenu("Directional Controls")) + { + ImGui::PushItemWidth(150.0f); + ImGui::Combo("##directional", &config_input.gamepad_directional, "D-pad\0Left Analog Stick\0\0"); + ImGui::PopItemWidth(); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Button Configuration")) + { + gamepad_configuration_item("A:", &config_input.gamepad_a); + gamepad_configuration_item("B:", &config_input.gamepad_b); + gamepad_configuration_item("START:", &config_input.gamepad_start); + gamepad_configuration_item("SELECT:", &config_input.gamepad_select); + + popup_modal_gamepad(); + + ImGui::EndMenu(); + } + ImGui::EndMenu(); + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Audio")) + { + gui_in_use = true; + + if (ImGui::MenuItem("Enable Audio", "", &config_audio.enable)) + { + emu_audio_volume(config_audio.enable ? 1.0f: 0.0f); + } + + if (ImGui::MenuItem("Sync With Emulator", "", &config_audio.sync)) + { + config_emulator.ffwd = false; + + if (!config_audio.sync) + { + config_video.sync = false; + SDL_GL_SetSwapInterval(0); + } + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Debug")) + { + gui_in_use = true; + + if (ImGui::MenuItem("Enable", "", &config_debug.debug)) + { + if (config_debug.debug) + emu_debug_step(); + else + emu_debug_continue(); + } + + ImGui::Separator(); + + if (ImGui::MenuItem("Step Over", "CTRL + F10", (void*)0, config_debug.debug)) + { + emu_debug_step(); + } + + if (ImGui::MenuItem("Step Frame", "CTRL + F6", (void*)0, config_debug.debug)) + { + emu_debug_next_frame(); + } + + if (ImGui::MenuItem("Continue", "CTRL + F5", (void*)0, config_debug.debug)) + { + emu_debug_continue(); + } + + if (ImGui::MenuItem("Run To Cursor", "CTRL + F8", (void*)0, config_debug.debug)) + { + gui_debug_runtocursor(); + } + + ImGui::Separator(); + + if (ImGui::MenuItem("Go Back", "CTRL + BACKSPACE", (void*)0, config_debug.debug)) + { + gui_debug_go_back(); + } + + ImGui::Separator(); + + if (ImGui::MenuItem("Toggle Breakpoint", "CTRL + F9", (void*)0, config_debug.debug)) + { + gui_debug_toggle_breakpoint(); + } + + if (ImGui::MenuItem("Clear All Processor Breakpoints", 0, (void*)0, config_debug.debug)) + { + gui_debug_reset_breakpoints_cpu(); + } + + if (ImGui::MenuItem("Clear All Memory Breakpoints", 0, (void*)0, config_debug.debug)) + { + gui_debug_reset_breakpoints_mem(); + } + + ImGui::MenuItem("Disable All Processor Breakpoints", 0, &emu_debug_disable_breakpoints_cpu, config_debug.debug); + + ImGui::MenuItem("Disable All Memory Breakpoints", 0, &emu_debug_disable_breakpoints_mem, config_debug.debug); + + ImGui::Separator(); + + if (ImGui::BeginMenu("Font Size", config_debug.debug)) + { + ImGui::PushItemWidth(110.0f); + if (ImGui::Combo("##font", &config_debug.font_size, "Very Small\0Small\0Medium\0Large\0\0")) + { + gui_default_font = default_font[config_debug.font_size]; + } + ImGui::PopItemWidth(); + ImGui::EndMenu(); + } + + ImGui::Separator(); + + ImGui::MenuItem("Show Game Boy Screen", "", &config_debug.show_gameboy, config_debug.debug); + + ImGui::MenuItem("Show Disassembler", "", &config_debug.show_disassembler, config_debug.debug); + + ImGui::MenuItem("Show Processor Status", "", &config_debug.show_processor, config_debug.debug); + + ImGui::MenuItem("Show Memory Editor", "", &config_debug.show_memory, config_debug.debug); + + ImGui::MenuItem("Show IO Map", "", &config_debug.show_iomap, config_debug.debug); + + ImGui::MenuItem("Show VRAM Viewer", "", &config_debug.show_video, config_debug.debug); + + ImGui::MenuItem("Show Sound Registers", "", &config_debug.show_audio, config_debug.debug); + + ImGui::Separator(); + + if (ImGui::MenuItem("Load Symbols...", "", (void*)0, config_debug.debug)) + { + open_symbols = true; + } + + if (ImGui::MenuItem("Clear Symbols", "", (void*)0, config_debug.debug)) + { + gui_debug_reset_symbols(); + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("About")) + { + gui_in_use = true; + + if (ImGui::MenuItem("About " GEARBOY_TITLE " " GEARBOY_VERSION " ...")) + { + open_about = true; + } + ImGui::EndMenu(); + } + + main_menu_height = (int)ImGui::GetWindowSize().y; + + ImGui::EndMainMenuBar(); + } + + if (open_rom || shortcut_open_rom) + { + shortcut_open_rom = false; + ImGui::OpenPopup("Open ROM..."); + } + + if (open_ram) + ImGui::OpenPopup("Load RAM From..."); + + if (save_ram) + ImGui::OpenPopup("Save RAM As..."); + + if (open_state) + ImGui::OpenPopup("Load State From..."); + + if (save_state) + ImGui::OpenPopup("Save State As..."); + + if (choose_save_file_path) + ImGui::OpenPopup("Choose Save File Folder..."); + + if (choose_savestates_path) + ImGui::OpenPopup("Choose Savestate Folder..."); + + if (open_dmg_bootrom) + ImGui::OpenPopup("Load DMG Bootrom From..."); + + if (open_gbc_bootrom) + ImGui::OpenPopup("Load GBC Bootrom From..."); + + if (open_symbols) + ImGui::OpenPopup("Load Symbols File..."); + + if (open_about) + { + dialog_in_use = true; + ImGui::OpenPopup("About " GEARBOY_TITLE); + } + + popup_modal_about(); + file_dialog_open_rom(); + file_dialog_load_ram(); + file_dialog_save_ram(); + file_dialog_load_state(); + file_dialog_save_state(); + file_dialog_choose_save_file_path(); + file_dialog_choose_savestate_path(); + file_dialog_load_dmg_bootrom(); + file_dialog_load_gbc_bootrom(); + file_dialog_load_symbols(); + + for (int i = 0; i < config_max_custom_palettes; i++) + for (int c = 0; c < 4; c++) + config_video.color[i][c] = color_float_to_int(custom_palette[i][c]); +} + +static void main_window(void) +{ + int w = (int)ImGui::GetIO().DisplaySize.x; + int h = (int)ImGui::GetIO().DisplaySize.y - (show_main_menu ? main_menu_height : 0); + + int selected_ratio = config_debug.debug ? 0 : config_video.ratio; + float ratio = (float)GAMEBOY_WIDTH / (float)GAMEBOY_HEIGHT; + + switch (selected_ratio) + { + case 0: + ratio = (float)GAMEBOY_WIDTH / (float)GAMEBOY_HEIGHT; + break; + case 1: + ratio = 4.0f / 3.0f; + break; + case 2: + ratio = 16.0f / 9.0f; + break; + case 3: + ratio = (float)w / (float)h; + break; + default: + ratio = (float)GAMEBOY_WIDTH / (float)GAMEBOY_HEIGHT; + } + + int w_corrected = (int)(selected_ratio == 3 ? w : GAMEBOY_HEIGHT * ratio); + int h_corrected = selected_ratio == 3 ? h : GAMEBOY_HEIGHT; + + int factor = 0; + + if (config_video.scale > 0) + { + factor = config_video.scale; + } + else if (config_debug.debug) + { + factor = 1; + } + else + { + int factor_w = w / w_corrected; + int factor_h = h / h_corrected; + factor = (factor_w < factor_h) ? factor_w : factor_h; + } + + int main_window_width = w_corrected * factor; + int main_window_height = h_corrected * factor; + + int window_x = (w - (w_corrected * factor)) / 2; + int window_y = ((h - (h_corrected * factor)) / 2) + (show_main_menu ? main_menu_height : 0); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + + ImGuiWindowFlags flags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar; + + if (config_debug.debug) + { + flags |= ImGuiWindowFlags_AlwaysAutoResize; + + ImGui::SetNextWindowPos(ImVec2(7, 32), ImGuiCond_FirstUseEver); + + ImGui::Begin(emu_is_cgb() ? "Game Boy Color###debug_output" : "Game Boy###debug_output", &config_debug.show_gameboy, flags); + } + else + { + ImGui::SetNextWindowSize(ImVec2((float)main_window_width, (float)main_window_height)); + ImGui::SetNextWindowPos(ImVec2((float)window_x, (float)window_y)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + + flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoNav; + + ImGui::Begin(GEARBOY_TITLE, 0, flags); + } + + ImGui::Image((void*)(intptr_t)renderer_emu_texture, ImVec2((float)main_window_width, (float)main_window_height)); + + if (config_video.fps) + show_fps(); + + if (config_emulator.show_info) + show_info(); + + ImGui::End(); + + ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + + if (!config_debug.debug) + { + + ImGui::PopStyleVar(); + } +} + +static void file_dialog_open_rom(void) +{ + if(file_dialog.showFileDialog("Open ROM...", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(700, 400), "*.*,.gb,.gbc,.cgb,.sgb,.dmg,.rom,.zip", &dialog_in_use)) + { + gui_load_rom(file_dialog.selected_path.c_str()); + } +} + +static void file_dialog_load_ram(void) +{ + if(file_dialog.showFileDialog("Load RAM From...", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(700, 310), ".sav,*.*", &dialog_in_use)) + { + emu_load_ram(file_dialog.selected_path.c_str(), config_emulator.force_dmg, get_mbc(config_emulator.mbc), config_emulator.force_gba); + } +} + +static void file_dialog_save_ram(void) +{ + if(file_dialog.showFileDialog("Save RAM As...", imgui_addons::ImGuiFileBrowser::DialogMode::SAVE, ImVec2(700, 310), ".sav", &dialog_in_use)) + { + std::string save_path = file_dialog.selected_path; + + if (save_path.rfind(file_dialog.ext) != (save_path.size()-file_dialog.ext.size())) + { + save_path += file_dialog.ext; + } + + emu_save_ram(save_path.c_str()); + } +} + +static void file_dialog_load_state(void) +{ + if(file_dialog.showFileDialog("Load State From...", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(700, 310), ".state,*.*", &dialog_in_use)) + { + emu_load_state_file(file_dialog.selected_path.c_str()); + } +} + +static void file_dialog_save_state(void) +{ + if(file_dialog.showFileDialog("Save State As...", imgui_addons::ImGuiFileBrowser::DialogMode::SAVE, ImVec2(700, 310), ".state", &dialog_in_use)) + { + std::string state_path = file_dialog.selected_path; + + if (state_path.rfind(file_dialog.ext) != (state_path.size()-file_dialog.ext.size())) + { + state_path += file_dialog.ext; + } + + emu_save_state_file(state_path.c_str()); + } +} + +static void file_dialog_choose_save_file_path(void) +{ + if(file_dialog.showFileDialog("Choose Save File Folder...", imgui_addons::ImGuiFileBrowser::DialogMode::SELECT, ImVec2(700, 310), "*.*", &dialog_in_use)) + { + strcpy(savefiles_path, file_dialog.selected_path.c_str()); + config_emulator.savefiles_path.assign(file_dialog.selected_path); + } +} + +static void file_dialog_choose_savestate_path(void) +{ + if(file_dialog.showFileDialog("Choose Savestate Folder...", imgui_addons::ImGuiFileBrowser::DialogMode::SELECT, ImVec2(700, 310), "*.*", &dialog_in_use)) + { + strcpy(savestates_path, file_dialog.selected_path.c_str()); + config_emulator.savestates_path.assign(file_dialog.selected_path); + } +} + +static void file_dialog_load_dmg_bootrom(void) +{ + if(file_dialog.showFileDialog("Load DMG Bootrom From...", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(700, 310), ".bin,.gb,*.*", &dialog_in_use)) + { + strcpy(dmg_bootrom_path, file_dialog.selected_path.c_str()); + config_emulator.dmg_bootrom_path.assign(file_dialog.selected_path); + + emu_load_bootrom_dmg(dmg_bootrom_path); + } +} + +static void file_dialog_load_gbc_bootrom(void) +{ + if(file_dialog.showFileDialog("Load GBC Bootrom From...", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(700, 310), ".bin,.gbc,*.*", &dialog_in_use)) + { + strcpy(gbc_bootrom_path, file_dialog.selected_path.c_str()); + config_emulator.gbc_bootrom_path.assign(file_dialog.selected_path); + + emu_load_bootrom_gbc(gbc_bootrom_path); + } +} + +static void file_dialog_load_symbols(void) +{ + if(file_dialog.showFileDialog("Load Symbols File...", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(700, 400), ".sym,*.*", &dialog_in_use)) + { + gui_debug_reset_symbols(); + gui_debug_load_symbols_file(file_dialog.selected_path.c_str()); + } +} + +static void keyboard_configuration_item(const char* text, SDL_Scancode* key) +{ + ImGui::Text("%s", text); + ImGui::SameLine(90); + + char button_label[256]; + sprintf(button_label, "%s##%s", SDL_GetScancodeName(*key), text); + + if (ImGui::Button(button_label, ImVec2(70,0))) + { + configured_key = key; + ImGui::OpenPopup("Keyboard Configuration"); + } +} + +static void gamepad_configuration_item(const char* text, int* button) +{ + ImGui::Text("%s", text); + ImGui::SameLine(70); + + static const char* gamepad_names[16] = {"A", "B", "X" ,"Y", "BACK", "GUID", "START", "L3", "R3", "L1", "R1", "UP", "DOWN", "LEFT", "RIGHT", "15"}; + + char button_label[256]; + sprintf(button_label, "%s##%s", gamepad_names[*button], text); + + if (ImGui::Button(button_label, ImVec2(70,0))) + { + configured_button = button; + ImGui::OpenPopup("Gamepad Configuration"); + } +} + +static void popup_modal_keyboard(void) +{ + if (ImGui::BeginPopupModal("Keyboard Configuration", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("Press any key...\n\n"); + ImGui::Separator(); + + for (int i = 0; i < IM_ARRAYSIZE(ImGui::GetIO().KeysDown); i++) + { + if (ImGui::IsKeyPressed(i)) + { + SDL_Scancode key = (SDL_Scancode)i; + + if ((key != SDL_SCANCODE_LCTRL) && (key != SDL_SCANCODE_RCTRL) && (key != SDL_SCANCODE_CAPSLOCK)) + { + *configured_key = key; + ImGui::CloseCurrentPopup(); + break; + } + } + } + + if (ImGui::Button("Cancel", ImVec2(120, 0))) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } +} + +static void popup_modal_gamepad(void) +{ + if (ImGui::BeginPopupModal("Gamepad Configuration", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("Press any button in your gamepad...\n\n"); + ImGui::Separator(); + + for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++) + { + if (SDL_GameControllerGetButton(application_gamepad, (SDL_GameControllerButton)i)) + { + *configured_button = i; + ImGui::CloseCurrentPopup(); + break; + } + } + + if (ImGui::Button("Cancel", ImVec2(120, 0))) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } +} + +static void popup_modal_about(void) +{ + if (ImGui::BeginPopupModal("About " GEARBOY_TITLE, NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("%s %s", GEARBOY_TITLE, GEARBOY_VERSION); + ImGui::Text("Build: %s", EMULATOR_BUILD); + + ImGui::Separator(); + + ImGui::Text("By Ignacio Sánchez (twitter.com/drhelius)"); + ImGui::Text("%s is licensed under the GPL-3.0 License, see LICENSE for more information.", GEARBOY_TITLE); + + ImGui::Separator(); + + if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None)) + { + if (ImGui::BeginTabItem("Special thanks to")) + { + ImGui::BeginChild("backers", ImVec2(0, 100), false, ImGuiWindowFlags_AlwaysVerticalScrollbar); + ImGui::Text("%s", BACKERS_STR); + ImGui::EndChild(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("LICENSE")) + { + ImGui::BeginChild("license", ImVec2(0, 100), false, ImGuiWindowFlags_AlwaysVerticalScrollbar); + ImGui::TextUnformatted(GPL_LICENSE_STR); + ImGui::EndChild(); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + + ImGui::Separator(); + + #ifdef _WIN64 + ImGui::Text("Windows 64 bit build"); + #elif defined(_WIN32) + ImGui::Text("Windows 32 bit build"); + #endif + #ifdef __linux__ + ImGui::Text("Linux build"); + #endif + #ifdef __APPLE__ + ImGui::Text("macOS build"); + #endif + #ifdef _MSC_VER + ImGui::Text("Microsoft C++ %d.", _MSC_VER); + #endif + #ifdef __MINGW32__ + ImGui::Text("MinGW 32 bit (%d.%d)", __MINGW32_MAJOR_VERSION, __MINGW32_MINOR_VERSION); + #endif + #ifdef __MINGW64__ + ImGui::Text("MinGW 64 bit (%d.%d)", __MINGW64_VERSION_MAJOR, __MINGW64_VERSION_MINOR); + #endif + #if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) + ImGui::Text("GCC %d.%d.%d", (int)__GNUC__, (int)__GNUC_MINOR__, (int)__GNUC_PATCHLEVEL__); + #endif + #ifdef __clang_version__ + ImGui::Text("Clang %s", __clang_version__); + #endif + + ImGui::Separator(); + + #ifdef DEBUG + ImGui::Text("define: DEBUG"); + #endif + #ifdef DEBUG_GEARBOY + ImGui::Text("define: DEBUG_GEARBOY"); + #endif + #ifdef __cplusplus + ImGui::Text("define: __cplusplus = %d", (int)__cplusplus); + #endif + #ifdef __STDC__ + ImGui::Text("define: __STDC__ = %d", (int)__STDC__); + #endif + #ifdef __STDC_VERSION__ + ImGui::Text("define: __STDC_VERSION__ = %d", (int)__STDC_VERSION__); + #endif + + ImGui::Separator(); + + ImGui::Text("SDL %d.%d.%d (build)", application_sdl_build_version.major, application_sdl_build_version.minor, application_sdl_build_version.patch); + ImGui::Text("SDL %d.%d.%d (link) ", application_sdl_link_version.major, application_sdl_link_version.minor, application_sdl_link_version.patch); + ImGui::Text("OpenGL %s", renderer_opengl_version); + #ifndef __APPLE__ + ImGui::Text("GLEW %s", renderer_glew_version); + #endif + ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); + + ImGui::Separator(); + + if (application_gamepad) + ImGui::Text("Gamepad detected"); + else + ImGui::Text("No gamepad detected"); + + if (application_gamepad_mappings > 0) + ImGui::Text("%d gamepad mappings loaded", application_gamepad_mappings); + else + ImGui::Text("Gamepad database not found"); + + ImGui::Separator(); + + if (ImGui::Button("OK", ImVec2(120, 0))) + { + ImGui::CloseCurrentPopup(); + dialog_in_use = false; + } + ImGui::SetItemDefaultFocus(); + + ImGui::EndPopup(); + } +} + +static GB_Color color_float_to_int(ImVec4 color) +{ + GB_Color ret; + ret.red = (u8)floor(color.x >= 1.0 ? 255.0 : color.x * 256.0); + ret.green = (u8)floor(color.y >= 1.0 ? 255.0 : color.y * 256.0); + ret.blue = (u8)floor(color.z >= 1.0 ? 255.0 : color.z * 256.0); + return ret; +} + +static ImVec4 color_int_to_float(GB_Color color) +{ + ImVec4 ret; + ret.w = 0; + ret.x = (1.0f / 255.0f) * color.red; + ret.y = (1.0f / 255.0f) * color.green; + ret.z = (1.0f / 255.0f) * color.blue; + return ret; +} + +static void update_palette(void) +{ + if (config_video.palette > 5) + { + int palette = config_video.palette - 6; + emu_dmg_palette(config_video.color[palette][0], config_video.color[palette][1], config_video.color[palette][2], config_video.color[palette][3]); + } + else + emu_dmg_predefined_palette(config_video.palette); +} + +static void push_recent_rom(std::string path) +{ + int slot = 0; + for (slot = 0; slot < config_max_recent_roms; slot++) + { + if (config_emulator.recent_roms[slot].compare(path) == 0) + { + break; + } + } + + slot = std::min(slot, config_max_recent_roms - 1); + + for (int i = slot; i > 0; i--) + { + config_emulator.recent_roms[i] = config_emulator.recent_roms[i - 1]; + } + + config_emulator.recent_roms[0] = path; +} + +static void menu_reset(void) +{ + emu_resume(); + emu_reset(config_emulator.force_dmg, get_mbc(config_emulator.mbc), config_emulator.force_gba); + + if (config_emulator.start_paused) + { + emu_pause(); + + for (int i=0; i < (GAMEBOY_WIDTH * GAMEBOY_HEIGHT); i++) + { + emu_frame_buffer[i].red = 0; + emu_frame_buffer[i].green = 0; + emu_frame_buffer[i].blue = 0; + } + } +} + +static void menu_pause(void) +{ + if (emu_is_paused()) + emu_resume(); + else + emu_pause(); +} + +static void menu_ffwd(void) +{ + config_audio.sync = !config_emulator.ffwd; + + if (config_emulator.ffwd) + SDL_GL_SetSwapInterval(0); + else + { + SDL_GL_SetSwapInterval(config_video.sync ? 1 : 0); + emu_audio_reset(); + } +} + +static void show_info(void) +{ + if (config_video.fps) + ImGui::SetCursorPosX(5.0f); + else + ImGui::SetCursorPos(ImVec2(5.0f, config_debug.debug ? 25.0f : 5.0f)); + + static char info[512]; + + emu_get_info(info); + ImGui::Text("%s", info); +} + +static void show_fps(void) +{ + ImGui::SetCursorPos(ImVec2(5.0f, config_debug.debug ? 25.0f : 5.0f )); + ImGui::Text("Frame Rate: %.2f FPS\nFrame Time: %.2f ms", ImGui::GetIO().Framerate, 1000.0f / ImGui::GetIO().Framerate); +} + +static Cartridge::CartridgeTypes get_mbc(int index) +{ + switch (index) + { + case 0: + return Cartridge::CartridgeNotSupported; + case 1: + return Cartridge::CartridgeNoMBC; + case 2: + return Cartridge::CartridgeMBC1; + case 3: + return Cartridge::CartridgeMBC2; + case 4: + return Cartridge::CartridgeMBC3; + case 5: + return Cartridge::CartridgeMBC5; + case 6: + return Cartridge::CartridgeMBC1Multi; + default: + return Cartridge::CartridgeNotSupported; + } +} |
