// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. // Copyright (C) 1999-2003 Forgotten // Copyright (C) 2004 Forgotten and the VBA development team // 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 2, or(at your option) // 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, write to the Free Software Foundation, // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "window.h" #include #include #include #include #include #include "../gba/GBA.h" #include "../gba/RTC.h" #include "../gba/Sound.h" #include "../gb/gb.h" #include "../gb/gbGlobals.h" #include "../gb/gbSound.h" #include "../gb/gbPrinter.h" #include "../Util.h" #include "tools.h" #include "intl.h" #include "screenarea-cairo.h" #ifdef USE_OPENGL #include "screenarea-opengl.h" #endif // USE_OPENGL extern int RGB_LOW_BITS_MASK; namespace VBA { using Gnome::Glade::Xml; Window * Window::m_poInstance = NULL; const Window::SJoypadKey Window::m_astJoypad[] = { { "left", KEY_LEFT }, { "right", KEY_RIGHT }, { "up", KEY_UP }, { "down", KEY_DOWN }, { "A", KEY_BUTTON_A }, { "B", KEY_BUTTON_B }, { "select", KEY_BUTTON_SELECT }, { "start", KEY_BUTTON_START }, { "L", KEY_BUTTON_L }, { "R", KEY_BUTTON_R }, { "speed", KEY_BUTTON_SPEED }, { "capture", KEY_BUTTON_CAPTURE }, { "speed", KEY_BUTTON_SPEED }, { "capture", KEY_BUTTON_CAPTURE }, { "autoA", KEY_BUTTON_AUTO_A }, { "autoB", KEY_BUTTON_AUTO_B } }; Window::Window(GtkWindow * _pstWindow, const Glib::RefPtr & _poXml) : Gtk::Window (_pstWindow), m_iGBScreenWidth (160), m_iGBScreenHeight (144), m_iSGBScreenWidth (256), m_iSGBScreenHeight(224), m_iGBAScreenWidth (240), m_iGBAScreenHeight(160), m_iFrameskipMin (0), m_iFrameskipMax (9), m_iScaleMin (1), m_iScaleMax (6), m_iShowSpeedMin (ShowNone), m_iShowSpeedMax (ShowDetailed), m_iSaveTypeMin (SaveAuto), m_iSaveTypeMax (SaveNone), m_iSoundSampleRateMin(11025), m_iSoundSampleRateMax(48000), m_fSoundVolumeMin (0.50f), m_fSoundVolumeMax (2.00f), m_iEmulatorTypeMin(EmulatorAuto), m_iEmulatorTypeMax(EmulatorSGB2), m_iFilter2xMin (FirstFilter), m_iFilter2xMax (LastFilter), m_iFilterIBMin (FirstFilterIB), m_iFilterIBMax (LastFilterIB), m_iJoypadMin (PAD_1), m_iJoypadMax (PAD_4), m_iVideoOutputMin (OutputCairo), m_iVideoOutputMax (OutputOpenGL), m_bFullscreen (false) { m_poXml = _poXml; m_poFileOpenDialog = NULL; m_iScreenWidth = m_iGBAScreenWidth; m_iScreenHeight = m_iGBAScreenHeight; m_eCartridge = CartridgeNone; vInitSDL(); vInitSystem(); vSetDefaultTitle(); // Get config // m_sUserDataDir = Glib::get_user_config_dir() + "/gvbam"; m_sConfigFile = m_sUserDataDir + "/config"; vInitConfig(); if (! Glib::file_test(m_sUserDataDir, Glib::FILE_TEST_EXISTS)) { mkdir(m_sUserDataDir.c_str(), 0777); } if (Glib::file_test(m_sConfigFile, Glib::FILE_TEST_EXISTS)) { vLoadConfig(m_sConfigFile); vCheckConfig(); } else { vSaveConfig(m_sConfigFile); } vCreateFileOpenDialog(); vApplyConfigJoypads(); vApplyConfigScreenArea(); vApplyConfigFilter(); vApplyConfigFilterIB(); vApplyConfigMute(); vApplyConfigVolume(); vApplyConfigGBSystem(); vApplyConfigGBBorder(); vApplyConfigGBPrinter(); vApplyConfigGBASaveType(); vApplyConfigGBAFlashSize(); vApplyConfigGBARTC(); Gtk::MenuItem * poMI; Gtk::CheckMenuItem * poCMI; // Menu bar m_poMenuBar = dynamic_cast(_poXml->get_widget("MenuBar")); m_poMenuBar->signal_deactivate().connect(sigc::mem_fun(*this, &Window::vOnMenuExit)); poMI = dynamic_cast(_poXml->get_widget("FileMenu")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnMenuEnter)); poMI = dynamic_cast(_poXml->get_widget("EmulationMenu")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnMenuEnter)); poMI = dynamic_cast(_poXml->get_widget("OptionsMenu")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnMenuEnter)); poMI = dynamic_cast(_poXml->get_widget("HelpMenu")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnMenuEnter)); // File menu // poMI = dynamic_cast(_poXml->get_widget("FileOpen")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileOpen)); poMI = dynamic_cast(_poXml->get_widget("FileLoad")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileLoad)); m_listSensitiveWhenPlaying.push_back(poMI); poMI = dynamic_cast(_poXml->get_widget("FileSave")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileSave)); m_listSensitiveWhenPlaying.push_back(poMI); for (int i = 0; i < 10; i++) { char csName[20]; snprintf(csName, 20, "LoadGameSlot%d", i + 1); m_apoLoadGameItem[i] = dynamic_cast(_poXml->get_widget(csName)); snprintf(csName, 20, "SaveGameSlot%d", i + 1); m_apoSaveGameItem[i] = dynamic_cast(_poXml->get_widget(csName)); m_apoLoadGameItem[i]->signal_activate().connect(sigc::bind( sigc::mem_fun(*this, &Window::vOnLoadGame), i + 1)); m_apoSaveGameItem[i]->signal_activate().connect(sigc::bind( sigc::mem_fun(*this, &Window::vOnSaveGame), i + 1)); } vUpdateGameSlots(); poMI = dynamic_cast(_poXml->get_widget("LoadGameMostRecent")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnLoadGameMostRecent)); m_listSensitiveWhenPlaying.push_back(poMI); poCMI = dynamic_cast(_poXml->get_widget("LoadGameAuto")); poCMI->set_active(m_poCoreConfig->oGetKey("load_game_auto")); vOnLoadGameAutoToggled(poCMI); poCMI->signal_toggled().connect(sigc::bind( sigc::mem_fun(*this, &Window::vOnLoadGameAutoToggled), poCMI)); poMI = dynamic_cast(_poXml->get_widget("SaveGameOldest")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnSaveGameOldest)); m_listSensitiveWhenPlaying.push_back(poMI); m_poFilePauseItem = dynamic_cast(_poXml->get_widget("FilePause")); m_poFilePauseItem->set_active(false); vOnFilePauseToggled(m_poFilePauseItem); m_poFilePauseItem->signal_toggled().connect(sigc::bind( sigc::mem_fun(*this, &Window::vOnFilePauseToggled), m_poFilePauseItem)); m_listSensitiveWhenPlaying.push_back(m_poFilePauseItem); poMI = dynamic_cast(_poXml->get_widget("FileReset")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileReset)); m_listSensitiveWhenPlaying.push_back(poMI); poMI = dynamic_cast(_poXml->get_widget("FileScreenCapture")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileScreenCapture)); m_listSensitiveWhenPlaying.push_back(poMI); poMI = dynamic_cast(_poXml->get_widget("FileClose")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileClose)); m_listSensitiveWhenPlaying.push_back(poMI); poMI = dynamic_cast(_poXml->get_widget("FileExit")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnFileExit)); // Recent menu // m_poRecentManager = Gtk::RecentManager::get_default(); Gtk::RecentFilter oRecentFilter; oRecentFilter.add_application( Glib::get_application_name() ); m_poRecentChooserMenu = Gtk::manage( new Gtk::RecentChooserMenu(m_poRecentManager) ); m_poRecentChooserMenu->set_show_numbers(); m_poRecentChooserMenu->set_show_tips(); m_poRecentChooserMenu->set_local_only(); m_poRecentChooserMenu->add_filter(oRecentFilter); m_poRecentChooserMenu->signal_item_activated().connect( sigc::mem_fun(*this, &Window::vOnRecentFile)); m_poRecentMenu = dynamic_cast(_poXml->get_widget("RecentMenu")); m_poRecentMenu->set_submenu(static_cast(*m_poRecentChooserMenu)); // Frameskip menu // struct { const char * m_csName; const int m_iFrameskip; } astFrameskip[] = { { "FrameskipAutomatic", -1 }, { "Frameskip0", 0 }, { "Frameskip1", 1 }, { "Frameskip2", 2 }, { "Frameskip3", 3 }, { "Frameskip4", 4 }, { "Frameskip5", 5 }, { "Frameskip6", 6 }, { "Frameskip7", 7 }, { "Frameskip8", 8 }, { "Frameskip9", 9 } }; int iDefaultFrameskip; if (m_poCoreConfig->sGetKey("frameskip") == "auto") { iDefaultFrameskip = -1; } else { iDefaultFrameskip = m_poCoreConfig->oGetKey("frameskip"); } for (guint i = 0; i < G_N_ELEMENTS(astFrameskip); i++) { poCMI = dynamic_cast(_poXml->get_widget(astFrameskip[i].m_csName)); if (astFrameskip[i].m_iFrameskip == iDefaultFrameskip) { poCMI->set_active(); vOnFrameskipToggled(poCMI, iDefaultFrameskip); } poCMI->signal_toggled().connect(sigc::bind( sigc::mem_fun(*this, &Window::vOnFrameskipToggled), poCMI, astFrameskip[i].m_iFrameskip)); } // Emulator menu // poMI = dynamic_cast(_poXml->get_widget("DirectoriesConfigure")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnDirectories)); poCMI = dynamic_cast(_poXml->get_widget("EmulatorPauseWhenInactive")); poCMI->set_active(m_poDisplayConfig->oGetKey("pause_when_inactive")); vOnPauseWhenInactiveToggled(poCMI); poCMI->signal_toggled().connect(sigc::bind( sigc::mem_fun(*this, &Window::vOnPauseWhenInactiveToggled), poCMI)); // Show speed menu // struct { const char * m_csName; const EShowSpeed m_eShowSpeed; } astShowSpeed[] = { { "ShowSpeedNone", ShowNone }, { "ShowSpeedPercentage", ShowPercentage }, { "ShowSpeedDetailed", ShowDetailed } }; EShowSpeed eDefaultShowSpeed = (EShowSpeed)m_poDisplayConfig->oGetKey("show_speed"); for (guint i = 0; i < G_N_ELEMENTS(astShowSpeed); i++) { poCMI = dynamic_cast(_poXml->get_widget(astShowSpeed[i].m_csName)); if (astShowSpeed[i].m_eShowSpeed == eDefaultShowSpeed) { poCMI->set_active(); vOnShowSpeedToggled(poCMI, eDefaultShowSpeed); } poCMI->signal_toggled().connect(sigc::bind( sigc::mem_fun(*this, &Window::vOnShowSpeedToggled), poCMI, astShowSpeed[i].m_eShowSpeed)); } // Game Boy menu poMI = dynamic_cast(_poXml->get_widget("GameBoyConfigure")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnGameBoyConfigure)); // Game Boy Advance menu poMI = dynamic_cast(_poXml->get_widget("GameBoyAdvanceConfigure")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnGameBoyAdvanceConfigure)); // Display menu poMI = dynamic_cast(_poXml->get_widget("DisplayConfigure")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnDisplayConfigure)); // Sound menu poMI = dynamic_cast(_poXml->get_widget("SoundConfigure")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnSoundConfigure)); // Joypad menu // poMI = dynamic_cast(_poXml->get_widget("JoypadConfigure")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnJoypadConfigure)); EPad eDefaultJoypad = (EPad)m_poInputConfig->oGetKey("active_joypad"); inputSetDefaultJoypad(eDefaultJoypad); // Fullscreen menu // poMI = dynamic_cast(_poXml->get_widget("VideoFullscreen")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnVideoFullscreen)); // Help menu // poMI = dynamic_cast(_poXml->get_widget("HelpAbout")); poMI->signal_activate().connect(sigc::mem_fun(*this, &Window::vOnHelpAbout)); // Init widgets sensitivity for (std::list::iterator it = m_listSensitiveWhenPlaying.begin(); it != m_listSensitiveWhenPlaying.end(); it++) { (*it)->set_sensitive(false); } if (m_poInstance == NULL) { m_poInstance = this; } else { abort(); } } Window::~Window() { vOnFileClose(); vUnInitSystem(); vSaveJoypadsToConfig(); vSaveConfig(m_sConfigFile); if (m_poFileOpenDialog != NULL) { delete m_poFileOpenDialog; } m_poInstance = NULL; } void Window::vInitColors(EColorFormat _eColorFormat) { switch (_eColorFormat) { case ColorFormatBGR: #if G_BYTE_ORDER == G_LITTLE_ENDIAN systemRedShift = 3; systemGreenShift = 11; systemBlueShift = 19; RGB_LOW_BITS_MASK = 0x00010101; #else systemRedShift = 27; systemGreenShift = 19; systemBlueShift = 11; RGB_LOW_BITS_MASK = 0x01010100; #endif break; default: #if G_BYTE_ORDER == G_LITTLE_ENDIAN systemRedShift = 19; systemGreenShift = 11; systemBlueShift = 3; RGB_LOW_BITS_MASK = 0x00010101; #else systemRedShift = 11; systemGreenShift = 19; systemBlueShift = 27; RGB_LOW_BITS_MASK = 0x01010100; #endif break; } for (int i = 0; i < 0x10000; i++) { systemColorMap32[i] = (((i & 0x1f) << systemRedShift) | (((i & 0x3e0) >> 5) << systemGreenShift) | (((i & 0x7c00) >> 10) << systemBlueShift)); } for (int i = 0; i < 24; ) { systemGbPalette[i++] = (0x1f) | (0x1f << 5) | (0x1f << 10); systemGbPalette[i++] = (0x15) | (0x15 << 5) | (0x15 << 10); systemGbPalette[i++] = (0x0c) | (0x0c << 5) | (0x0c << 10); systemGbPalette[i++] = 0; } Init_2xSaI(32); } void Window::vApplyConfigScreenArea() { EVideoOutput eVideoOutput = (EVideoOutput)m_poDisplayConfig->oGetKey("output"); Gtk::Alignment * poC; poC = dynamic_cast(m_poXml->get_widget("ScreenContainer")); poC->remove(); poC->set(Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER, 1.0, 1.0); try { switch (eVideoOutput) { #ifdef USE_OPENGL case OutputOpenGL: vInitColors(ColorFormatBGR); m_poScreenArea = Gtk::manage(new ScreenAreaGl(m_iScreenWidth, m_iScreenHeight)); break; #endif // USE_OPENGL case OutputCairo: default: vInitColors(ColorFormatRGB); m_poScreenArea = Gtk::manage(new ScreenAreaCairo(m_iScreenWidth, m_iScreenHeight)); break; } } catch (std::exception e) { fprintf(stderr, "Unable to initialize output, falling back to Cairo\n"); m_poScreenArea = Gtk::manage(new ScreenAreaCairo(m_iScreenWidth, m_iScreenHeight)); } poC->add(*m_poScreenArea); vDrawDefaultScreen(); m_poScreenArea->show(); } void Window::vInitSystem() { systemColorDepth = 32; systemVerbose = 0; systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; systemFrameSkip = 2; emulating = 0; gbFrameSkip = 0; m_iFrameCount = 0; soundInit(); } void Window::vUnInitSystem() { soundShutdown(); } void Window::vInitSDL() { static bool bDone = false; if (bDone) return; int iFlags = (SDL_INIT_EVERYTHING | SDL_INIT_NOPARACHUTE); if (SDL_Init(iFlags) < 0) { fprintf(stderr, "Failed to init SDL: %s", SDL_GetError()); abort(); } inputSetKeymap(PAD_DEFAULT, KEY_LEFT, GDK_Left); inputSetKeymap(PAD_DEFAULT, KEY_RIGHT, GDK_Right); inputSetKeymap(PAD_DEFAULT, KEY_UP, GDK_Up); inputSetKeymap(PAD_DEFAULT, KEY_DOWN, GDK_Down); inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_A, GDK_z); inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_B, GDK_x); inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_START, GDK_Return); inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_SELECT, GDK_BackSpace); inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_L, GDK_a); inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_R, GDK_s); inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_SPEED, GDK_space); inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_CAPTURE, GDK_F12); inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_AUTO_A, GDK_q); inputSetKeymap(PAD_DEFAULT, KEY_BUTTON_AUTO_B, GDK_w); // TODO : remove int sdlNumDevices = SDL_NumJoysticks(); for (int i = 0; i < sdlNumDevices; i++) SDL_JoystickOpen(i); inputInitJoysticks(); bDone = true; } void Window::vInitConfig() { m_oConfig.vClear(); // Directories section // m_poDirConfig = m_oConfig.poAddSection("Directories"); m_poDirConfig->vSetKey("gb_roms", Glib::get_home_dir()); m_poDirConfig->vSetKey("gba_roms", Glib::get_home_dir()); m_poDirConfig->vSetKey("batteries", m_sUserDataDir); m_poDirConfig->vSetKey("saves", m_sUserDataDir); m_poDirConfig->vSetKey("captures", m_sUserDataDir); // Core section // m_poCoreConfig = m_oConfig.poAddSection("Core"); m_poCoreConfig->vSetKey("load_game_auto", false ); m_poCoreConfig->vSetKey("frameskip", "auto" ); m_poCoreConfig->vSetKey("use_bios_file", false ); m_poCoreConfig->vSetKey("bios_file", "" ); m_poCoreConfig->vSetKey("enable_rtc", false ); m_poCoreConfig->vSetKey("save_type", SaveAuto ); m_poCoreConfig->vSetKey("flash_size", 64 ); m_poCoreConfig->vSetKey("gb_border", false ); m_poCoreConfig->vSetKey("gb_printer", false ); m_poCoreConfig->vSetKey("gb_use_bios_file", false ); m_poCoreConfig->vSetKey("gb_bios_file", "" ); m_poCoreConfig->vSetKey("emulator_type", EmulatorAuto ); // Display section // m_poDisplayConfig = m_oConfig.poAddSection("Display"); m_poDisplayConfig->vSetKey("scale", 1 ); m_poDisplayConfig->vSetKey("show_speed", ShowPercentage ); m_poDisplayConfig->vSetKey("pause_when_inactive", true ); m_poDisplayConfig->vSetKey("filter2x", FilterNone ); m_poDisplayConfig->vSetKey("filterIB", FilterIBNone ); #ifdef USE_OPENGL m_poDisplayConfig->vSetKey("output", OutputOpenGL ); #else m_poDisplayConfig->vSetKey("output", OutputCairo ); #endif // USE_OPENGL // Sound section // m_poSoundConfig = m_oConfig.poAddSection("Sound"); m_poSoundConfig->vSetKey("mute", false ); m_poSoundConfig->vSetKey("sample_rate", 44100 ); m_poSoundConfig->vSetKey("volume", 1.00f ); // Input section // m_poInputConfig = m_oConfig.poAddSection("Input"); m_poInputConfig->vSetKey("active_joypad", m_iJoypadMin ); for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++) { char csPrefix[20]; snprintf(csPrefix, sizeof(csPrefix), "joypadSDL%d_", i); std::string sPrefix(csPrefix); for (guint j = 0; j < G_N_ELEMENTS(m_astJoypad); j++) { m_poInputConfig->vSetKey(sPrefix + m_astJoypad[j].m_csKey, inputGetKeymap(PAD_DEFAULT, m_astJoypad[j].m_eKeyFlag)); } } } void Window::vCheckConfig() { int iValue; int iAdjusted; float fValue; float fAdjusted; std::string sValue; // Directories section // sValue = m_poDirConfig->sGetKey("gb_roms"); if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR)) { m_poDirConfig->vSetKey("gb_roms", Glib::get_home_dir()); } sValue = m_poDirConfig->sGetKey("gba_roms"); if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR)) { m_poDirConfig->vSetKey("gba_roms", Glib::get_home_dir()); } sValue = m_poDirConfig->sGetKey("batteries"); if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR)) { m_poDirConfig->vSetKey("batteries", m_sUserDataDir); } sValue = m_poDirConfig->sGetKey("saves"); if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR)) { m_poDirConfig->vSetKey("saves", m_sUserDataDir); } sValue = m_poDirConfig->sGetKey("captures"); if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR)) { m_poDirConfig->vSetKey("captures", m_sUserDataDir); } // Core section // if (m_poCoreConfig->sGetKey("frameskip") != "auto") { iValue = m_poCoreConfig->oGetKey("frameskip"); iAdjusted = CLAMP(iValue, m_iFrameskipMin, m_iFrameskipMax); if (iValue != iAdjusted) { m_poCoreConfig->vSetKey("frameskip", iAdjusted); } } sValue = m_poCoreConfig->sGetKey("bios_file"); if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_REGULAR)) { m_poCoreConfig->vSetKey("bios_file", ""); } if (m_poCoreConfig->sGetKey("bios_file") == "") { m_poCoreConfig->vSetKey("use_bios_file", false); } if (m_poCoreConfig->sGetKey("enable_rtc") == "") { m_poCoreConfig->vSetKey("enable_rtc", false); } sValue = m_poCoreConfig->sGetKey("gb_bios_file"); if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_REGULAR)) { m_poCoreConfig->vSetKey("gb_bios_file", ""); } if (m_poCoreConfig->sGetKey("gb_bios_file") == "") { m_poCoreConfig->vSetKey("gb_use_bios_file", false); } iValue = m_poCoreConfig->oGetKey("save_type"); if (iValue != 0) { iAdjusted = CLAMP(iValue, m_iSaveTypeMin, m_iSaveTypeMax); if (iValue != iAdjusted) { m_poCoreConfig->vSetKey("save_type", iAdjusted); } } iValue = m_poCoreConfig->oGetKey("flash_size"); if (iValue != 64 && iValue != 128) { m_poCoreConfig->vSetKey("flash_size", 64); } iValue = m_poCoreConfig->oGetKey("emulator_type"); iAdjusted = CLAMP(iValue, m_iEmulatorTypeMin, m_iEmulatorTypeMax); if (iValue != iAdjusted) { m_poCoreConfig->vSetKey("emulator_type", iAdjusted); } // Display section // iValue = m_poDisplayConfig->oGetKey("scale"); iAdjusted = CLAMP(iValue, m_iScaleMin, m_iScaleMax); if (iValue != iAdjusted) { m_poDisplayConfig->vSetKey("scale", iAdjusted); } iValue = m_poDisplayConfig->oGetKey("show_speed"); iAdjusted = CLAMP(iValue, m_iShowSpeedMin, m_iShowSpeedMax); if (iValue != iAdjusted) { m_poDisplayConfig->vSetKey("show_speed", iAdjusted); } iValue = m_poDisplayConfig->oGetKey("filter2x"); iAdjusted = CLAMP(iValue, m_iFilter2xMin, m_iFilter2xMax); if (iValue != iAdjusted) { m_poDisplayConfig->vSetKey("filter2x", iAdjusted); } iValue = m_poDisplayConfig->oGetKey("filterIB"); iAdjusted = CLAMP(iValue, m_iFilterIBMin, m_iFilterIBMax); if (iValue != iAdjusted) { m_poDisplayConfig->vSetKey("filterIB", iAdjusted); } iValue = m_poDisplayConfig->oGetKey("output"); iAdjusted = CLAMP(iValue, m_iVideoOutputMin, m_iVideoOutputMax); if (iValue != iAdjusted) { m_poDisplayConfig->vSetKey("output", iAdjusted); } // Sound section // iValue = m_poSoundConfig->oGetKey("sample_rate"); iAdjusted = CLAMP(iValue, m_iSoundSampleRateMin, m_iSoundSampleRateMax); if (iValue != iAdjusted) { m_poSoundConfig->vSetKey("sample_rate", iAdjusted); } fValue = m_poSoundConfig->oGetKey("volume"); fAdjusted = CLAMP(fValue, m_fSoundVolumeMin, m_fSoundVolumeMax); if (fValue != fAdjusted) { m_poSoundConfig->vSetKey("volume", fAdjusted); } // Input section // iValue = m_poInputConfig->oGetKey("active_joypad"); iAdjusted = CLAMP(iValue, m_iJoypadMin, m_iJoypadMax); if (iValue != iAdjusted) { m_poInputConfig->vSetKey("active_joypad", iAdjusted); } } void Window::vLoadConfig(const std::string & _rsFile) { try { m_oConfig.vLoad(_rsFile, false, false); } catch (const Glib::Error & e) { vPopupError(e.what().c_str()); } } void Window::vSaveConfig(const std::string & _rsFile) { try { m_oConfig.vSave(_rsFile); } catch (const Glib::Error & e) { vPopupError(e.what().c_str()); } } void Window::vApplyConfigFilter() { int iFilter = m_poDisplayConfig->oGetKey("filter2x"); m_poScreenArea->vSetFilter((EFilter)iFilter); if (emulating) { vDrawScreen(); } } void Window::vApplyConfigFilterIB() { int iFilter = m_poDisplayConfig->oGetKey("filterIB"); m_poScreenArea->vSetFilterIB((EFilterIB)iFilter); if (emulating) { vDrawScreen(); } } void Window::vApplyConfigMute() { bool bMute = m_poSoundConfig->oGetKey("mute"); if (bMute) { soundSetEnable(0x000); } else { soundSetEnable(0x30f); } } void Window::vApplyConfigVolume() { float fSoundVolume = m_poSoundConfig->oGetKey("volume"); soundSetVolume(fSoundVolume); } void Window::vApplyConfigSoundSampleRate() { long iSoundSampleRate = m_poSoundConfig->oGetKey("sample_rate"); if (m_eCartridge == CartridgeGBA) { soundSetSampleRate(iSoundSampleRate); } else if (m_eCartridge == CartridgeGB) { gbSoundSetSampleRate(iSoundSampleRate); } } void Window::vApplyConfigGBSystem() { gbEmulatorType = m_poCoreConfig->oGetKey("emulator_type"); } void Window::vApplyConfigGBBorder() { gbBorderOn = m_poCoreConfig->oGetKey("gb_border"); if (emulating && m_eCartridge == CartridgeGB && gbBorderOn) { gbSgbRenderBorder(); } vUpdateScreen(); } void Window::vApplyConfigGBPrinter() { bool bPrinter = m_poCoreConfig->oGetKey("gb_printer"); if (bPrinter) { gbSerialFunction = gbPrinterSend; } else { gbSerialFunction = NULL; } } void Window::vApplyConfigGBASaveType() { int iSaveType = m_poCoreConfig->oGetKey("save_type"); cpuSaveType = iSaveType; } void Window::vApplyConfigGBAFlashSize() { int iFlashSize = m_poCoreConfig->oGetKey("flash_size"); if (iFlashSize == 64) { flashSetSize(0x10000); } else { flashSetSize(0x20000); } } void Window::vApplyConfigGBARTC() { bool iRTC = m_poCoreConfig->oGetKey("enable_rtc"); rtcEnable(iRTC); } void Window::vHistoryAdd(const std::string & _rsFile) { std::string sURL = "file://" + _rsFile; m_poRecentManager->add_item(sURL); } void Window::vApplyConfigJoypads() { for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++) { char csPrefix[20]; snprintf(csPrefix, sizeof(csPrefix), "joypadSDL%d_", i); std::string sPrefix(csPrefix); for (guint j = 0; j < G_N_ELEMENTS(m_astJoypad); j++) { inputSetKeymap((EPad)i, m_astJoypad[j].m_eKeyFlag, m_poInputConfig->oGetKey(sPrefix + m_astJoypad[j].m_csKey)); } } } void Window::vSaveJoypadsToConfig() { for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++) { char csPrefix[20]; snprintf(csPrefix, sizeof(csPrefix), "joypadSDL%d_", i); std::string sPrefix(csPrefix); for (guint j = 0; j < G_N_ELEMENTS(m_astJoypad); j++) { m_poInputConfig->vSetKey(sPrefix + m_astJoypad[j].m_csKey, inputGetKeymap((EPad)i, m_astJoypad[j].m_eKeyFlag)); } } } void Window::vUpdateScreen() { if (m_eCartridge == CartridgeGB) { if (gbBorderOn) { m_iScreenWidth = m_iSGBScreenWidth; m_iScreenHeight = m_iSGBScreenHeight; gbBorderLineSkip = m_iSGBScreenWidth; gbBorderColumnSkip = (m_iSGBScreenWidth - m_iGBScreenWidth) / 2; gbBorderRowSkip = (m_iSGBScreenHeight - m_iGBScreenHeight) / 2; } else { m_iScreenWidth = m_iGBScreenWidth; m_iScreenHeight = m_iGBScreenHeight; gbBorderLineSkip = m_iGBScreenWidth; gbBorderColumnSkip = 0; gbBorderRowSkip = 0; } } else if (m_eCartridge == CartridgeGBA) { m_iScreenWidth = m_iGBAScreenWidth; m_iScreenHeight = m_iGBAScreenHeight; } g_return_if_fail(m_iScreenWidth >= 1 && m_iScreenHeight >= 1); m_poScreenArea->vSetSize(m_iScreenWidth, m_iScreenHeight); m_poScreenArea->vSetScale(m_poDisplayConfig->oGetKey("scale")); resize(1, 1); if (emulating) { vDrawScreen(); } else { vDrawDefaultScreen(); } } bool Window::bLoadROM(const std::string & _rsFile) { vOnFileClose(); m_sRomFile = _rsFile; const char * csFile = _rsFile.c_str(); IMAGE_TYPE eType = utilFindType(csFile); if (eType == IMAGE_UNKNOWN) { vPopupError(_("Unknown file type %s"), csFile); return false; } bool bLoaded = false; if (eType == IMAGE_GB) { bLoaded = gbLoadRom(csFile); if (bLoaded) { m_eCartridge = CartridgeGB; m_stEmulator = GBSystem; useBios = m_poCoreConfig->oGetKey("gb_use_bios_file"); gbGetHardwareType(); if (gbHardware & 5) { gbCPUInit(m_poCoreConfig->sGetKey("gb_bios_file").c_str(), useBios); } // If the bios file was rejected by gbCPUInit if (m_poCoreConfig->oGetKey("gb_use_bios_file") && ! useBios) { m_poCoreConfig->vSetKey("gb_bios_file", ""); } gbReset(); } } else if (eType == IMAGE_GBA) { int iSize = CPULoadRom(csFile); bLoaded = (iSize > 0); if (bLoaded) { m_eCartridge = CartridgeGBA; m_stEmulator = GBASystem; useBios = m_poCoreConfig->oGetKey("use_bios_file"); CPUInit(m_poCoreConfig->sGetKey("bios_file").c_str(), useBios); CPUReset(); // If the bios file was rejected by CPUInit if (m_poCoreConfig->oGetKey("use_bios_file") && ! useBios) { m_poUseBiosItem->set_active(false); m_poUseBiosItem->set_sensitive(false); m_poCoreConfig->vSetKey("bios_file", ""); } rtcEnable(m_poCoreConfig->oGetKey("enable_rtc")); } } if (! bLoaded) { return false; } vLoadBattery(); vUpdateScreen(); emulating = 1; m_bWasEmulating = false; vApplyConfigSoundSampleRate(); vUpdateGameSlots(); vHistoryAdd(_rsFile); for (std::list::iterator it = m_listSensitiveWhenPlaying.begin(); it != m_listSensitiveWhenPlaying.end(); it++) { (*it)->set_sensitive(); } if (m_poCoreConfig->oGetKey("load_game_auto")) { vOnLoadGameMostRecent(); } vStartEmu(); return true; } void Window::vPopupError(const char * _csFormat, ...) { va_list args; va_start(args, _csFormat); char * csMsg = g_strdup_vprintf(_csFormat, args); va_end(args); Gtk::MessageDialog oDialog(*this, csMsg, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); oDialog.run(); g_free(csMsg); } void Window::vPopupErrorV(const char * _csFormat, va_list _args) { char * csMsg = g_strdup_vprintf(_csFormat, _args); Gtk::MessageDialog oDialog(*this, csMsg, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK); oDialog.run(); g_free(csMsg); } void Window::vDrawScreen() { m_poScreenArea->vDrawPixels(pix); m_iFrameCount++; } void Window::vDrawDefaultScreen() { m_poScreenArea->vDrawBlackScreen(); } void Window::vSetDefaultTitle() { set_title("VBA-M"); } void Window::vShowSpeed(int _iSpeed) { char csTitle[50]; if (m_eShowSpeed == ShowPercentage) { snprintf(csTitle, 50, "VBA-M - %d%%", _iSpeed); set_title(csTitle); } else if (m_eShowSpeed == ShowDetailed) { snprintf(csTitle, 50, "VBA-M - %d%% (%d, %d fps)", _iSpeed, systemFrameSkip, m_iFrameCount); set_title(csTitle); } m_iFrameCount = 0; } void Window::vComputeFrameskip(int _iRate) { static Glib::TimeVal uiLastTime; static int iFrameskipAdjust = 0; Glib::TimeVal uiTime; uiTime.assign_current_time(); if (m_bWasEmulating) { if (m_bAutoFrameskip) { Glib::TimeVal uiDiff = uiTime - uiLastTime; const int iWantedSpeed = 100; int iSpeed = iWantedSpeed; if (uiDiff != Glib::TimeVal(0, 0)) { iSpeed = (1000000 / _iRate) / (uiDiff.as_double() * 1000); } if (iSpeed >= iWantedSpeed - 2) { iFrameskipAdjust++; if (iFrameskipAdjust >= 3) { iFrameskipAdjust = 0; if (systemFrameSkip > 0) { systemFrameSkip--; } } } else { if (iSpeed < iWantedSpeed - 20) { iFrameskipAdjust -= ((iWantedSpeed - 10) - iSpeed) / 5; } else if (systemFrameSkip < 9) { iFrameskipAdjust--; } if (iFrameskipAdjust <= -4) { iFrameskipAdjust = 0; if (systemFrameSkip < 9) { systemFrameSkip++; } } } } } else { m_bWasEmulating = true; } uiLastTime = uiTime; } void Window::vCaptureScreen(int _iNum) { std::string sBaseName; std::string sDir = m_poDirConfig->sGetKey("captures"); if (sDir == "") { sDir = m_sUserDataDir; } sBaseName = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)); char * csFile = g_strdup_printf("%s_%02d.png", sBaseName.c_str(), _iNum); m_stEmulator.emuWritePNG(csFile); g_free(csFile); } void Window::vCreateFileOpenDialog() { if (m_poFileOpenDialog != NULL) { return; } std::string sGBDir = m_poDirConfig->sGetKey("gb_roms"); std::string sGBADir = m_poDirConfig->sGetKey("gba_roms"); Gtk::FileChooserDialog * poDialog = new Gtk::FileChooserDialog(*this, _("Open")); poDialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); poDialog->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); try { if (sGBDir != "") { poDialog->add_shortcut_folder(sGBDir); poDialog->set_current_folder(sGBDir); } if (sGBADir != "" && sGBADir != sGBDir) { poDialog->add_shortcut_folder(sGBADir); poDialog->set_current_folder(sGBADir); } } catch (const Gtk::FileChooserError& e) { // Most likely the shortcut already exists, so do nothing } const char * acsPattern[] = { // GBA "*.[bB][iI][nN]", "*.[aA][gG][bB]", "*.[gG][bB][aA]", // GB "*.[gG][bB]", "*.[sS][gG][bB]", "*.[cC][gG][bB]", "*.[gG][bB][cC]", // Both "*.[mM][bB]", "*.[eE][lL][fF]", "*.[zZ][iI][pP]", "*.[zZ]", "*.[gG][zZ]" }; Gtk::FileFilter oAllGBAFilter; oAllGBAFilter.set_name(_("All Gameboy Advance files")); for (guint i = 0; i < G_N_ELEMENTS(acsPattern); i++) { oAllGBAFilter.add_pattern(acsPattern[i]); } Gtk::FileFilter oGBAFilter; oGBAFilter.set_name(_("Gameboy Advance files")); for (int i = 0; i < 3; i++) { oGBAFilter.add_pattern(acsPattern[i]); } Gtk::FileFilter oGBFilter; oGBFilter.set_name(_("Gameboy files")); for (int i = 3; i < 7; i++) { oGBFilter.add_pattern(acsPattern[i]); } poDialog->add_filter(oAllGBAFilter); poDialog->add_filter(oGBAFilter); poDialog->add_filter(oGBFilter); m_poFileOpenDialog = poDialog; } void Window::vLoadBattery() { std::string sBattery; std::string sDir = m_poDirConfig->sGetKey("batteries"); if (sDir == "") { sDir = m_sUserDataDir; } sBattery = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)) + ".sav"; if (m_stEmulator.emuReadBattery(sBattery.c_str())) { systemScreenMessage(_("Loaded battery")); } } void Window::vSaveBattery() { std::string sBattery; std::string sDir = m_poDirConfig->sGetKey("batteries"); if (sDir == "") { sDir = m_sUserDataDir; } sBattery = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)) + ".sav"; if (m_stEmulator.emuWriteBattery(sBattery.c_str())) { systemScreenMessage(_("Saved battery")); } } void Window::vStartEmu() { if (m_oEmuSig.connected()) { return; } m_oEmuSig = Glib::signal_idle().connect(sigc::mem_fun(*this, &Window::bOnEmuIdle), Glib::PRIORITY_HIGH_IDLE + 30); } void Window::vStopEmu() { m_oEmuSig.disconnect(); m_bWasEmulating = false; } void Window::vUpdateGameSlots() { if (m_eCartridge == CartridgeNone) { std::string sDateTime = _("----/--/-- --:--:--"); for (int i = 0; i < 10; i++) { char csPrefix[10]; snprintf(csPrefix, sizeof(csPrefix), "%2d ", i + 1); Gtk::Label * poLabel; poLabel = dynamic_cast(m_apoLoadGameItem[i]->get_child()); poLabel->set_text(csPrefix + sDateTime); m_apoLoadGameItem[i]->set_sensitive(false); poLabel = dynamic_cast(m_apoSaveGameItem[i]->get_child()); poLabel->set_text(csPrefix + sDateTime); m_apoSaveGameItem[i]->set_sensitive(false); m_astGameSlot[i].m_bEmpty = true; } } else { std::string sFileBase; std::string sDir = m_poDirConfig->sGetKey("saves"); if (sDir == "") { sDir = m_sUserDataDir; } sFileBase = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)); const char * csDateFormat = _("%Y/%m/%d %H:%M:%S"); for (int i = 0; i < 10; i++) { char csPrefix[10]; snprintf(csPrefix, sizeof(csPrefix), "%2d ", i + 1); char csSlot[10]; snprintf(csSlot, sizeof(csSlot), "%d", i + 1); m_astGameSlot[i].m_sFile = sFileBase + csSlot + ".sgm"; std::string sDateTime; struct stat stStat; if (stat(m_astGameSlot[i].m_sFile.c_str(), &stStat) == -1) { sDateTime = _("----/--/-- --:--:--"); m_astGameSlot[i].m_bEmpty = true; } else { char csDateTime[30]; strftime(csDateTime, sizeof(csDateTime), csDateFormat, localtime(&stStat.st_mtime)); sDateTime = csDateTime; m_astGameSlot[i].m_bEmpty = false; m_astGameSlot[i].m_uiTime = stStat.st_mtime; } Gtk::Label * poLabel; poLabel = dynamic_cast(m_apoLoadGameItem[i]->get_child()); poLabel->set_text(csPrefix + sDateTime); m_apoLoadGameItem[i]->set_sensitive(! m_astGameSlot[i].m_bEmpty); poLabel = dynamic_cast(m_apoSaveGameItem[i]->get_child()); poLabel->set_text(csPrefix + sDateTime); m_apoSaveGameItem[i]->set_sensitive(); } } } void Window::vToggleFullscreen() { if(!m_bFullscreen) { fullscreen(); m_poMenuBar->hide(); } else { unfullscreen(); m_poMenuBar->show(); } } void Window::vSDLPollEvents() { SDL_Event event; while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_JOYHATMOTION: case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: case SDL_JOYAXISMOTION: case SDL_KEYDOWN: case SDL_KEYUP: inputProcessSDLEvent(event); break; } } } std::string Window::sGetUiFilePath(const std::string &_sFileName) { // Use the ui file from the source folder if it exists // to make gvbam runnable without installation std::string sUiFile = "src/gtk/ui/" + _sFileName; if (!Glib::file_test(sUiFile, Glib::FILE_TEST_EXISTS)) { sUiFile = PKGDATADIR "/ui/" + _sFileName; } return sUiFile; } } // VBA namespace