1 // VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
2 // Copyright (C) 1999-2003 Forgotten
3 // Copyright (C) 2004 Forgotten and the VBA development team
4 
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2, or(at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 
19 #include "window.h"
20 
21 #include <sys/stat.h>
22 
23 #include <stdio.h>
24 #include <time.h>
25 
26 #include <SDL.h>
27 
28 #include "../GBA.h"
29 #include "../gb/GB.h"
30 #include "../gb/gbGlobals.h"
31 #include "../gb/gbPrinter.h"
32 #include "../Sound.h"
33 #include "../Util.h"
34 
35 #include "menuitem.h"
36 #include "tools.h"
37 #include "intl.h"
38 
39 extern int systemRenderedFrames;
40 extern int systemFPS;
41 extern bool debugger;
42 extern int RGB_LOW_BITS_MASK;
43 extern void (*dbgMain)();
44 extern void (*dbgSignal)(int, int);
45 extern void (*dbgOutput)(char *, u32);
46 extern void remoteInit();
47 extern void remoteCleanUp();
48 extern void remoteStubMain();
49 extern void remoteStubSignal(int, int);
50 extern void remoteOutput(char *, u32);
51 extern void remoteSetProtocol(int);
52 extern void remoteSetPort(int);
53 
54 #ifdef MMX
55 extern "C" bool cpu_mmx;
56 #endif // MMX
57 
58 namespace VBA
59 {
60 
61 using Gnome::Glade::Xml;
62 
63 Window * Window::m_poInstance = NULL;
64 
Window(GtkWindow * _pstWindow,const Glib::RefPtr<Xml> & _poXml)65 Window::Window(GtkWindow * _pstWindow, const Glib::RefPtr<Xml> & _poXml) :
66   Gtk::Window       (_pstWindow),
67   m_iGBScreenWidth  (160),
68   m_iGBScreenHeight (144),
69   m_iSGBScreenWidth (256),
70   m_iSGBScreenHeight(224),
71   m_iGBAScreenWidth (240),
72   m_iGBAScreenHeight(160),
73   m_iFrameskipMin   (0),
74   m_iFrameskipMax   (9),
75   m_iThrottleMin    (5),
76   m_iThrottleMax    (1000),
77   m_iScaleMin       (1),
78   m_iScaleMax       (6),
79   m_iShowSpeedMin   (ShowNone),
80   m_iShowSpeedMax   (ShowDetailed),
81   m_iSaveTypeMin    (SaveAuto),
82   m_iSaveTypeMax    (SaveNone),
83   m_iSoundQualityMin(Sound44K),
84   m_iSoundQualityMax(Sound11K),
85   m_iSoundVolumeMin (Sound100),
86   m_iSoundVolumeMax (Sound50),
87   m_iEmulatorTypeMin(EmulatorAuto),
88   m_iEmulatorTypeMax(EmulatorSGB2),
89   m_iFilter2xMin    (FirstFilter),
90   m_iFilter2xMax    (LastFilter),
91   m_iFilterIBMin    (FirstFilterIB),
92   m_iFilterIBMax    (LastFilterIB),
93   m_iJoypadMin      (1),
94   m_iJoypadMax      (4)
95 {
96   m_poXml            = _poXml;
97   m_poFileOpenDialog = NULL;
98   m_iScreenWidth     = m_iGBAScreenWidth;
99   m_iScreenHeight    = m_iGBAScreenHeight;
100   m_eCartridge       = CartridgeNone;
101   m_uiJoypadState    = 0;
102 
103   vInitSystem();
104   vInitSDL();
105 
106   Gtk::Container * poC;
107   poC = dynamic_cast<Gtk::Container *>(_poXml->get_widget("ScreenContainer"));
108   m_poScreenArea = Gtk::manage(new ScreenArea(m_iScreenWidth, m_iScreenHeight));
109   poC->add(*m_poScreenArea);
110   vDrawDefaultScreen();
111   m_poScreenArea->show();
112 
113   // Get config
114   //
115   vInitConfig();
116 
117   m_sUserDataDir = Glib::get_home_dir() + "/.gvba";
118   m_sConfigFile  = m_sUserDataDir + "/config";
119 
120   if (! Glib::file_test(m_sUserDataDir, Glib::FILE_TEST_EXISTS))
121   {
122     mkdir(m_sUserDataDir.c_str(), 0777);
123   }
124   if (Glib::file_test(m_sConfigFile, Glib::FILE_TEST_EXISTS))
125   {
126     vLoadConfig(m_sConfigFile);
127     vCheckConfig();
128   }
129   else
130   {
131     vSaveConfig(m_sConfigFile);
132   }
133 
134   vCreateFileOpenDialog();
135   vLoadHistoryFromConfig();
136   vLoadJoypadsFromConfig();
137 
138   Gtk::MenuItem *      poMI;
139   Gtk::CheckMenuItem * poCMI;
140 
141   // File menu
142   //
143   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileOpen"));
144   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnFileOpen));
145 
146   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileLoad"));
147   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnFileLoad));
148   m_listSensitiveWhenPlaying.push_back(poMI);
149 
150   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileSave"));
151   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnFileSave));
152   m_listSensitiveWhenPlaying.push_back(poMI);
153 
154   for (int i = 0; i < 10; i++)
155   {
156     char csName[20];
157     snprintf(csName, 20, "LoadGameSlot%d", i + 1);
158     m_apoLoadGameItem[i] = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget(csName));
159     snprintf(csName, 20, "SaveGameSlot%d", i + 1);
160     m_apoSaveGameItem[i] = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget(csName));
161 
162     m_apoLoadGameItem[i]->signal_activate().connect(SigC::bind<int>(
163                                                       SigC::slot(*this, &Window::vOnLoadGame),
164                                                       i + 1));
165     m_apoSaveGameItem[i]->signal_activate().connect(SigC::bind<int>(
166                                                       SigC::slot(*this, &Window::vOnSaveGame),
167                                                       i + 1));
168   }
169   vUpdateGameSlots();
170 
171   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("LoadGameMostRecent"));
172   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnLoadGameMostRecent));
173   m_listSensitiveWhenPlaying.push_back(poMI);
174 
175   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("LoadGameAuto"));
176   poCMI->set_active(m_poCoreConfig->oGetKey<bool>("load_game_auto"));
177   vOnLoadGameAutoToggled(poCMI);
178   poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *>(
179                                     SigC::slot(*this, &Window::vOnLoadGameAutoToggled),
180                                     poCMI));
181 
182   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("SaveGameOldest"));
183   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnSaveGameOldest));
184   m_listSensitiveWhenPlaying.push_back(poMI);
185 
186   m_poFilePauseItem = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("FilePause"));
187   m_poFilePauseItem->set_active(false);
188   vOnFilePauseToggled(m_poFilePauseItem);
189   m_poFilePauseItem->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *>(
190                                                 SigC::slot(*this, &Window::vOnFilePauseToggled),
191                                                 m_poFilePauseItem));
192   m_listSensitiveWhenPlaying.push_back(m_poFilePauseItem);
193 
194   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileReset"));
195   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnFileReset));
196   m_listSensitiveWhenPlaying.push_back(poMI);
197 
198   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileScreenCapture"));
199   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnFileScreenCapture));
200   m_listSensitiveWhenPlaying.push_back(poMI);
201 
202   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileClose"));
203   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnFileClose));
204   m_listSensitiveWhenPlaying.push_back(poMI);
205 
206   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("FileExit"));
207   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnFileExit));
208 
209   // Recent menu
210   //
211   m_poRecentMenu = dynamic_cast<Gtk::Menu *>(_poXml->get_widget("RecentMenu_menu"));
212   vUpdateHistoryMenu();
213 
214   m_poRecentResetItem = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("RecentReset"));
215   m_poRecentResetItem->signal_activate().connect(SigC::slot(*this, &Window::vOnRecentReset));
216 
217   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("RecentFreeze"));
218   poCMI->set_active(m_poHistoryConfig->oGetKey<bool>("freeze"));
219   vOnRecentFreezeToggled(poCMI);
220   poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *>(
221                                     SigC::slot(*this, &Window::vOnRecentFreezeToggled),
222                                     poCMI));
223 
224   // Import menu
225   //
226   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("ImportBatteryFile"));
227   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnImportBatteryFile));
228   m_listSensitiveWhenPlaying.push_back(poMI);
229 
230   // Export menu
231   //
232   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("ExportBatteryFile"));
233   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnExportBatteryFile));
234   m_listSensitiveWhenPlaying.push_back(poMI);
235 
236   // Frameskip menu
237   //
238   struct
239   {
240     const char * m_csName;
241     const int    m_iFrameskip;
242   }
243   astFrameskip[] =
244   {
245     { "FrameskipAutomatic", -1 },
246     { "Frameskip0",          0 },
247     { "Frameskip1",          1 },
248     { "Frameskip2",          2 },
249     { "Frameskip3",          3 },
250     { "Frameskip4",          4 },
251     { "Frameskip5",          5 },
252     { "Frameskip6",          6 },
253     { "Frameskip7",          7 },
254     { "Frameskip8",          8 },
255     { "Frameskip9",          9 }
256   };
257   int iDefaultFrameskip;
258   if (m_poCoreConfig->sGetKey("frameskip") == "auto")
259   {
260     iDefaultFrameskip = -1;
261   }
262   else
263   {
264     iDefaultFrameskip = m_poCoreConfig->oGetKey<int>("frameskip");
265   }
266   for (guint i = 0; i < G_N_ELEMENTS(astFrameskip); i++)
267   {
268     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astFrameskip[i].m_csName));
269     if (astFrameskip[i].m_iFrameskip == iDefaultFrameskip)
270     {
271       poCMI->set_active();
272       vOnFrameskipToggled(poCMI, iDefaultFrameskip);
273     }
274     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
275                                       SigC::slot(*this, &Window::vOnFrameskipToggled),
276                                       poCMI, astFrameskip[i].m_iFrameskip));
277   }
278 
279   // Throttle menu
280   //
281   struct
282   {
283     const char * m_csName;
284     const int    m_iThrottle;
285   }
286   astThrottle[] =
287   {
288     { "ThrottleNoThrottle",   0 },
289     { "Throttle25",          25 },
290     { "Throttle50",          50 },
291     { "Throttle100",        100 },
292     { "Throttle150",        150 },
293     { "Throttle200",        200 }
294   };
295   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("ThrottleOther"));
296   poCMI->set_active();
297   poCMI->signal_activate().connect(SigC::bind<Gtk::CheckMenuItem *>(
298                                      SigC::slot(*this, &Window::vOnThrottleOther),
299                                      poCMI));
300 
301   int iDefaultThrottle = m_poCoreConfig->oGetKey<int>("throttle");
302   for (guint i = 0; i < G_N_ELEMENTS(astThrottle); i++)
303   {
304     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astThrottle[i].m_csName));
305     if (astThrottle[i].m_iThrottle == iDefaultThrottle)
306     {
307       poCMI->set_active();
308     }
309     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
310                                       SigC::slot(*this, &Window::vOnThrottleToggled),
311                                       poCMI, astThrottle[i].m_iThrottle));
312   }
313   vSetThrottle(iDefaultThrottle);
314 
315   // Video menu
316   //
317   struct
318   {
319     const char * m_csName;
320     const int    m_iScale;
321   }
322   astVideoScale[] =
323   {
324     { "Video1x", 1 },
325     { "Video2x", 2 },
326     { "Video3x", 3 },
327     { "Video4x", 4 },
328     { "Video5x", 5 },
329     { "Video6x", 6 }
330   };
331   int iDefaultScale = m_poDisplayConfig->oGetKey<int>("scale");
332   for (guint i = 0; i < G_N_ELEMENTS(astVideoScale); i++)
333   {
334     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astVideoScale[i].m_csName));
335     if (astVideoScale[i].m_iScale == iDefaultScale)
336     {
337       poCMI->set_active();
338       vOnVideoScaleToggled(poCMI, iDefaultScale);
339     }
340     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
341                                       SigC::slot(*this, &Window::vOnVideoScaleToggled),
342                                       poCMI, astVideoScale[i].m_iScale));
343   }
344 
345   // Layers menu
346   //
347   struct
348   {
349     const char * m_csName;
350     const char * m_csKey;
351     const int    m_iLayer;
352   }
353   astLayer[] =
354   {
355     { "LayersBg0",    "layer_bg0",    0 },
356     { "LayersBg1",    "layer_bg1",    1 },
357     { "LayersBg2",    "layer_bg2",    2 },
358     { "LayersBg3",    "layer_bg3",    3 },
359     { "LayersObj",    "layer_obj",    4 },
360     { "LayersWin0",   "layer_win0",   5 },
361     { "LayersWin1",   "layer_win1",   6 },
362     { "LayersObjWin", "layer_objwin", 7 }
363   };
364   for (guint i = 0; i < G_N_ELEMENTS(astLayer); i++)
365   {
366     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astLayer[i].m_csName));
367     poCMI->set_active(m_poCoreConfig->oGetKey<bool>(astLayer[i].m_csKey));
368     vOnLayerToggled(poCMI, astLayer[i].m_iLayer);
369     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
370                                       SigC::slot(*this, &Window::vOnLayerToggled),
371                                       poCMI, astLayer[i].m_iLayer));
372   }
373 
374   // Emulator menu
375   //
376   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("EmulatorDirectories"));
377   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnDirectories));
378 
379   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("EmulatorPauseWhenInactive"));
380   poCMI->set_active(m_poDisplayConfig->oGetKey<bool>("pause_when_inactive"));
381   vOnPauseWhenInactiveToggled(poCMI);
382   poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *>(
383                                     SigC::slot(*this, &Window::vOnPauseWhenInactiveToggled),
384                                     poCMI));
385 
386   m_poUseBiosItem = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("EmulatorUseBios"));
387   m_poUseBiosItem->set_active(m_poCoreConfig->oGetKey<bool>("use_bios_file"));
388   if (m_poCoreConfig->sGetKey("bios_file") == "")
389   {
390     m_poUseBiosItem->set_sensitive(false);
391   }
392   m_poUseBiosItem->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *>(
393                                               SigC::slot(*this, &Window::vOnUseBiosToggled),
394                                               m_poUseBiosItem));
395 
396   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("EmulatorSelectBios"));
397   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnSelectBios));
398 
399   // Show speed menu
400   //
401   struct
402   {
403     const char *     m_csName;
404     const EShowSpeed m_eShowSpeed;
405   }
406   astShowSpeed[] =
407   {
408     { "ShowSpeedNone",       ShowNone       },
409     { "ShowSpeedPercentage", ShowPercentage },
410     { "ShowSpeedDetailed",   ShowDetailed   }
411   };
412   EShowSpeed eDefaultShowSpeed = (EShowSpeed)m_poDisplayConfig->oGetKey<int>("show_speed");
413   for (guint i = 0; i < G_N_ELEMENTS(astShowSpeed); i++)
414   {
415     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astShowSpeed[i].m_csName));
416     if (astShowSpeed[i].m_eShowSpeed == eDefaultShowSpeed)
417     {
418       poCMI->set_active();
419       vOnShowSpeedToggled(poCMI, eDefaultShowSpeed);
420     }
421     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
422                                       SigC::slot(*this, &Window::vOnShowSpeedToggled),
423                                       poCMI, astShowSpeed[i].m_eShowSpeed));
424   }
425 
426   // Save type menu
427   //
428   struct
429   {
430     const char *    m_csName;
431     const ESaveType m_eSaveType;
432   }
433   astSaveType[] =
434   {
435     { "SaveTypeAutomatic",    SaveAuto         },
436     { "SaveTypeEeprom",       SaveEEPROM       },
437     { "SaveTypeSram",         SaveSRAM         },
438     { "SaveTypeFlash",        SaveFlash        },
439     { "SaveTypeEepromSensor", SaveEEPROMSensor },
440     { "SaveTypeNone",         SaveNone         }
441   };
442   ESaveType eDefaultSaveType = (ESaveType)m_poCoreConfig->oGetKey<int>("save_type");
443   for (guint i = 0; i < G_N_ELEMENTS(astSaveType); i++)
444   {
445     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astSaveType[i].m_csName));
446     if (astSaveType[i].m_eSaveType == eDefaultSaveType)
447     {
448       poCMI->set_active();
449       vOnSaveTypeToggled(poCMI, eDefaultSaveType);
450     }
451     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
452                                       SigC::slot(*this, &Window::vOnSaveTypeToggled),
453                                       poCMI, astSaveType[i].m_eSaveType));
454   }
455 
456   // Flash size menu
457   //
458   struct
459   {
460     const char * m_csName;
461     const int    m_iFlashSize;
462   }
463   astFlashSize[] =
464   {
465     { "SaveTypeFlash64K",   64 },
466     { "SaveTypeFlash128K", 128 }
467   };
468   int iDefaultFlashSize = m_poCoreConfig->oGetKey<int>("flash_size");
469   for (guint i = 0; i < G_N_ELEMENTS(astFlashSize); i++)
470   {
471     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astFlashSize[i].m_csName));
472     if (astFlashSize[i].m_iFlashSize == iDefaultFlashSize)
473     {
474       poCMI->set_active();
475       vOnFlashSizeToggled(poCMI, iDefaultFlashSize);
476     }
477     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
478                                       SigC::slot(*this, &Window::vOnFlashSizeToggled),
479                                       poCMI, astFlashSize[i].m_iFlashSize));
480   }
481 
482   // Screenshot format menu
483   //
484   struct
485   {
486     const char * m_csName;
487     const char * m_csScreenshotFormat;
488   }
489   astScreenshotFormat[] =
490   {
491     { "ScreenshotFormatPNG", "png" },
492     { "ScreenshotFormatBMP", "bmp" }
493   };
494   std::string sDefaultScreenshotFormat = m_poCoreConfig->sGetKey("screenshot_format");
495   for (guint i = 0; i < G_N_ELEMENTS(astScreenshotFormat); i++)
496   {
497     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astScreenshotFormat[i].m_csName));
498     if (astScreenshotFormat[i].m_csScreenshotFormat == sDefaultScreenshotFormat)
499     {
500       poCMI->set_active();
501       vOnScreenshotFormatToggled(poCMI, sDefaultScreenshotFormat);
502     }
503     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, std::string>(
504                                       SigC::slot(*this, &Window::vOnScreenshotFormatToggled),
505                                       poCMI, std::string(astScreenshotFormat[i].m_csScreenshotFormat)));
506   }
507 
508   // Sound menu
509   //
510   std::string sDefaultSoundStatus = m_poSoundConfig->sGetKey("status");
511 
512   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("SoundOff"));
513   if (sDefaultSoundStatus == "off")
514   {
515     poCMI->set_active();
516     vOnSoundStatusToggled(poCMI, SoundOff);
517   }
518   poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
519                                     SigC::slot(*this, &Window::vOnSoundStatusToggled),
520                                     poCMI, SoundOff));
521   m_poSoundOffItem = poCMI;
522 
523   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("SoundMute"));
524   if (sDefaultSoundStatus == "mute")
525   {
526     poCMI->set_active();
527     vOnSoundStatusToggled(poCMI, SoundMute);
528   }
529   poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
530                                     SigC::slot(*this, &Window::vOnSoundStatusToggled),
531                                     poCMI, SoundMute));
532 
533   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("SoundOn"));
534   if (sDefaultSoundStatus == "on")
535   {
536     poCMI->set_active();
537     vOnSoundStatusToggled(poCMI, SoundOn);
538   }
539   poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
540                                     SigC::slot(*this, &Window::vOnSoundStatusToggled),
541                                     poCMI, SoundOn));
542 
543   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("SoundEcho"));
544   poCMI->set_active(m_poSoundConfig->oGetKey<bool>("echo"));
545   vOnSoundEchoToggled(poCMI);
546   poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *>(
547                                     SigC::slot(*this, &Window::vOnSoundEchoToggled),
548                                     poCMI));
549 
550   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("SoundLowPass"));
551   poCMI->set_active(m_poSoundConfig->oGetKey<bool>("low_pass"));
552   vOnSoundLowPassToggled(poCMI);
553   poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *>(
554                                     SigC::slot(*this, &Window::vOnSoundLowPassToggled),
555                                     poCMI));
556 
557   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("SoundReverseStereo"));
558   poCMI->set_active(m_poSoundConfig->oGetKey<bool>("reverse_stereo"));
559   vOnSoundReverseToggled(poCMI);
560   poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *>(
561                                     SigC::slot(*this, &Window::vOnSoundReverseToggled),
562                                     poCMI));
563 
564   struct
565   {
566     const char * m_csName;
567     const char * m_csKey;
568     const int    m_iSoundChannel;
569   }
570   astSoundChannel[] =
571   {
572     { "SoundChannel1", "channel_1", 0 },
573     { "SoundChannel2", "channel_2", 1 },
574     { "SoundChannel3", "channel_3", 2 },
575     { "SoundChannel4", "channel_4", 3 },
576     { "SoundChannelA", "channel_A", 4 },
577     { "SoundChannelB", "channel_B", 5 }
578   };
579   for (guint i = 0; i < G_N_ELEMENTS(astSoundChannel); i++)
580   {
581     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astSoundChannel[i].m_csName));
582     poCMI->set_active(m_poSoundConfig->oGetKey<bool>(astSoundChannel[i].m_csKey));
583     vOnSoundChannelToggled(poCMI, astSoundChannel[i].m_iSoundChannel);
584     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
585                                       SigC::slot(*this, &Window::vOnSoundChannelToggled),
586                                       poCMI, astSoundChannel[i].m_iSoundChannel));
587   }
588 
589   struct
590   {
591     const char *        m_csName;
592     const ESoundQuality m_eSoundQuality;
593   }
594   astSoundQuality[] =
595   {
596     { "Sound11Khz", Sound11K },
597     { "Sound22Khz", Sound22K },
598     { "Sound44Khz", Sound44K }
599   };
600   ESoundQuality eDefaultSoundQuality = (ESoundQuality)m_poSoundConfig->oGetKey<int>("quality");
601   for (guint i = 0; i < G_N_ELEMENTS(astSoundQuality); i++)
602   {
603     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astSoundQuality[i].m_csName));
604     if (astSoundQuality[i].m_eSoundQuality == eDefaultSoundQuality)
605     {
606       poCMI->set_active();
607       vOnSoundQualityToggled(poCMI, eDefaultSoundQuality);
608     }
609     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
610                                       SigC::slot(*this, &Window::vOnSoundQualityToggled),
611                                       poCMI, astSoundQuality[i].m_eSoundQuality));
612   }
613 
614   // Volume menu
615   //
616   struct
617   {
618     const char *       m_csName;
619     const ESoundVolume m_eSoundVolume;
620   }
621   astSoundVolume[] =
622   {
623     { "Volume25",   Sound25  },
624     { "Volume50",   Sound50  },
625     { "Volume100",  Sound100 },
626     { "Volume200",  Sound200 },
627     { "Volume300",  Sound300 },
628     { "Volume400",  Sound400 }
629   };
630   ESoundVolume eDefaultSoundVolume = (ESoundVolume)m_poSoundConfig->oGetKey<int>("volume");
631   for (guint i = 0; i < G_N_ELEMENTS(astSoundVolume); i++)
632   {
633     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astSoundVolume[i].m_csName));
634     if (astSoundVolume[i].m_eSoundVolume == eDefaultSoundVolume)
635     {
636       poCMI->set_active();
637       vOnSoundVolumeToggled(poCMI, eDefaultSoundVolume);
638     }
639     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
640                                       SigC::slot(*this, &Window::vOnSoundVolumeToggled),
641                                       poCMI, astSoundVolume[i].m_eSoundVolume));
642   }
643 
644   // Gameboy menu
645   //
646   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("GameboyBorder"));
647   poCMI->set_active(m_poCoreConfig->oGetKey<bool>("gb_border"));
648   vOnGBBorderToggled(poCMI);
649   poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *>(
650                                     SigC::slot(*this, &Window::vOnGBBorderToggled),
651                                     poCMI));
652 
653   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("GameboyPrinter"));
654   poCMI->set_active(m_poCoreConfig->oGetKey<bool>("gb_printer"));
655   vOnGBPrinterToggled(poCMI);
656   poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *>(
657                                     SigC::slot(*this, &Window::vOnGBPrinterToggled),
658                                     poCMI));
659 
660   struct
661   {
662     const char *        m_csName;
663     const EEmulatorType m_eEmulatorType;
664   }
665   astEmulatorType[] =
666   {
667     { "GameboyAutomatic", EmulatorAuto },
668     { "GameboyGba",       EmulatorGBA  },
669     { "GameboyCgb",       EmulatorCGB  },
670     { "GameboySgb",       EmulatorSGB  },
671     { "GameboySgb2",      EmulatorSGB2 },
672     { "GameboyGb",        EmulatorGB   }
673   };
674   EEmulatorType eDefaultEmulatorType = (EEmulatorType)m_poCoreConfig->oGetKey<int>("emulator_type");
675   for (guint i = 0; i < G_N_ELEMENTS(astEmulatorType); i++)
676   {
677     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astEmulatorType[i].m_csName));
678     if (astEmulatorType[i].m_eEmulatorType == eDefaultEmulatorType)
679     {
680       poCMI->set_active();
681       vOnEmulatorTypeToggled(poCMI, eDefaultEmulatorType);
682     }
683     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
684                                       SigC::slot(*this, &Window::vOnEmulatorTypeToggled),
685                                       poCMI, astEmulatorType[i].m_eEmulatorType));
686   }
687 
688   // Filter menu
689   //
690   struct
691   {
692     const char *    m_csName;
693     const EFilter2x m_eFilter2x;
694   }
695   astFilter2x[] =
696   {
697     { "FilterNone",          FilterNone         },
698     { "FilterTVMode",        FilterScanlinesTV  },
699     { "Filter2xSaI",         Filter2xSaI        },
700     { "FilterSuper2xSaI",    FilterSuper2xSaI   },
701     { "FilterSuperEagle",    FilterSuperEagle   },
702     { "FilterPixelate",      FilterPixelate     },
703     { "FilterMotionBlur",    FilterMotionBlur   },
704     { "FilterAdvanceMame2x", FilterAdMame2x     },
705     { "FilterSimple2x",      FilterSimple2x     },
706     { "FilterBilinear",      FilterBilinear     },
707     { "FilterBilinearPlus",  FilterBilinearPlus },
708     { "FilterScanlines",     FilterScanlines    },
709     { "FilterHq2x",          FilterHq2x         },
710     { "FilterLq2x",          FilterLq2x         }
711   };
712   EFilter2x eDefaultFilter2x = (EFilter2x)m_poDisplayConfig->oGetKey<int>("filter2x");
713   for (guint i = 0; i < G_N_ELEMENTS(astFilter2x); i++)
714   {
715     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astFilter2x[i].m_csName));
716     if (astFilter2x[i].m_eFilter2x == eDefaultFilter2x)
717     {
718       poCMI->set_active();
719       vOnFilter2xToggled(poCMI, eDefaultFilter2x);
720     }
721     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
722                                       SigC::slot(*this, &Window::vOnFilter2xToggled),
723                                       poCMI, astFilter2x[i].m_eFilter2x));
724   }
725 
726   poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget("FilterDisableMmx"));
727 #ifdef MMX
728   poCMI->set_active(m_poDisplayConfig->oGetKey<bool>("filter_disable_mmx"));
729   vOnDisableMMXToggled(poCMI);
730   poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *>(
731                                     SigC::slot(*this, &Window::vOnDisableMMXToggled),
732                                     poCMI));
733 #else // ! MMX
734   poCMI->set_active();
735   poCMI->set_sensitive(false);
736 #endif // ! MMX
737 
738   // Interframe blending menu
739   //
740   struct
741   {
742     const char *    m_csName;
743     const EFilterIB m_eFilterIB;
744   }
745   astFilterIB[] =
746   {
747     { "IFBNone",       FilterIBNone       },
748     { "IFBSmart",      FilterIBSmart      },
749     { "IFBMotionBlur", FilterIBMotionBlur }
750   };
751   EFilterIB eDefaultFilterIB = (EFilterIB)m_poDisplayConfig->oGetKey<int>("filterIB");
752   for (guint i = 0; i < G_N_ELEMENTS(astFilterIB); i++)
753   {
754     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astFilterIB[i].m_csName));
755     if (astFilterIB[i].m_eFilterIB == eDefaultFilterIB)
756     {
757       poCMI->set_active();
758       vOnFilterIBToggled(poCMI, eDefaultFilterIB);
759     }
760     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
761                                       SigC::slot(*this, &Window::vOnFilterIBToggled),
762                                       poCMI, astFilterIB[i].m_eFilterIB));
763   }
764 
765   // Joypad menu
766   //
767   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("JoypadConfigure1"));
768   poMI->signal_activate().connect(SigC::bind<int>(
769                                     SigC::slot(*this, &Window::vOnJoypadConfigure), 1));
770 
771   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("JoypadConfigure2"));
772   poMI->signal_activate().connect(SigC::bind<int>(
773                                     SigC::slot(*this, &Window::vOnJoypadConfigure), 2));
774 
775   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("JoypadConfigure3"));
776   poMI->signal_activate().connect(SigC::bind<int>(
777                                     SigC::slot(*this, &Window::vOnJoypadConfigure), 3));
778 
779   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("JoypadConfigure4"));
780   poMI->signal_activate().connect(SigC::bind<int>(
781                                     SigC::slot(*this, &Window::vOnJoypadConfigure), 4));
782 
783   int iDefaultJoypad = m_poInputConfig->oGetKey<int>("active_joypad");
784   for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++)
785   {
786     char csName[20];
787     snprintf(csName, sizeof(csName), "Joypad%d", i);
788 
789     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(csName));
790     if (i == iDefaultJoypad)
791     {
792       poCMI->set_active();
793       vOnJoypadToggled(poCMI, iDefaultJoypad);
794     }
795     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, int>(
796                                       SigC::slot(*this, &Window::vOnJoypadToggled),
797                                       poCMI, i));
798   }
799 
800   // Autofire menu
801   //
802   struct
803   {
804     const char *   m_csName;
805     const char *   m_csKey;
806     const EKeyFlag m_eKeyFlag;
807   }
808   astAutofire[] =
809   {
810     { "AutofireA", "autofire_A", KeyFlagA },
811     { "AutofireB", "autofire_B", KeyFlagB },
812     { "AutofireL", "autofire_L", KeyFlagL },
813     { "AutofireR", "autofire_R", KeyFlagR }
814   };
815   for (guint i = 0; i < G_N_ELEMENTS(astAutofire); i++)
816   {
817     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(_poXml->get_widget(astAutofire[i].m_csName));
818     poCMI->set_active(m_poInputConfig->oGetKey<bool>(astAutofire[i].m_csKey));
819     vOnAutofireToggled(poCMI, astAutofire[i].m_eKeyFlag);
820     poCMI->signal_toggled().connect(SigC::bind<Gtk::CheckMenuItem *, u32>(
821                                       SigC::slot(*this, &Window::vOnAutofireToggled),
822                                       poCMI, astAutofire[i].m_eKeyFlag));
823   }
824 
825   // GDB menu
826   //
827   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("GdbWait"));
828   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnGDBWait));
829 
830   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("GdbLoadAndWait"));
831   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnGDBLoadAndWait));
832 
833   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("GdbBreak"));
834   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnGDBBreak));
835 
836   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("GdbDisconnect"));
837   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnGDBDisconnect));
838 
839   // Help menu
840   //
841   poMI = dynamic_cast<Gtk::MenuItem *>(_poXml->get_widget("HelpAbout"));
842   poMI->signal_activate().connect(SigC::slot(*this, &Window::vOnHelpAbout));
843 
844   // Init widgets sensitivity
845   for (std::list<Gtk::Widget *>::iterator it = m_listSensitiveWhenPlaying.begin();
846        it != m_listSensitiveWhenPlaying.end();
847        it++)
848   {
849     (*it)->set_sensitive(false);
850   }
851 
852   if (m_poInstance == NULL)
853   {
854     m_poInstance = this;
855   }
856   else
857   {
858     abort();
859   }
860 }
861 
~Window()862 Window::~Window()
863 {
864   vOnFileClose();
865   vSaveHistoryToConfig();
866   vSaveJoypadsToConfig();
867   vSaveConfig(m_sConfigFile);
868 
869   if (m_poFileOpenDialog != NULL)
870   {
871     delete m_poFileOpenDialog;
872   }
873 
874   if (m_poKeymap != NULL)
875   {
876     delete m_poKeymap;
877   }
878 
879   m_poInstance = NULL;
880 }
881 
vInitSystem()882 void Window::vInitSystem()
883 {
884 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
885   systemRedShift    = 3;
886   systemGreenShift  = 11;
887   systemBlueShift   = 19;
888   RGB_LOW_BITS_MASK = 0x00010101;
889 #else
890   systemRedShift    = 27;
891   systemGreenShift  = 19;
892   systemBlueShift   = 11;
893   RGB_LOW_BITS_MASK = 0x01010100;
894 #endif
895 
896   systemColorDepth = 32;
897   systemDebug = 0;
898   systemVerbose = 0;
899   systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
900   systemFrameSkip = 0;
901   systemSoundOn = false;
902   soundOffFlag = true;
903 
904   systemRenderedFrames = 0;
905   systemFPS = 0;
906 
907   emulating = 0;
908   debugger = false;
909 
910   for (int i = 0; i < 0x10000; i++)
911   {
912 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
913     systemColorMap32[i] = (((i & 0x1f) << systemRedShift)
914                            | (((i & 0x3e0) >> 5) << systemGreenShift)
915                            | (((i & 0x7c00) >> 10) << systemBlueShift));
916 #else
917     systemColorMap32[i] = (((i & 0x1f) << systemRedShift)
918                            | (((i & 0x3e0) >> 5) << systemGreenShift)
919                            | (((i & 0x7c00) >> 10) << systemBlueShift));
920 #endif
921   }
922 
923   gbFrameSkip = 0;
924 
925   for (int i = 0; i < 24; )
926   {
927     systemGbPalette[i++] = (0x1f) | (0x1f << 5) | (0x1f << 10);
928     systemGbPalette[i++] = (0x15) | (0x15 << 5) | (0x15 << 10);
929     systemGbPalette[i++] = (0x0c) | (0x0c << 5) | (0x0c << 10);
930     systemGbPalette[i++] = 0;
931   }
932 
933   Init_2xSaI(32);
934 }
935 
vInitSDL()936 void Window::vInitSDL()
937 {
938   static bool bDone = false;
939 
940   if (bDone)
941     return;
942 
943   int iFlags = (SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE);
944 
945   if (SDL_Init(iFlags) < 0)
946   {
947     fprintf(stderr, "Failed to init SDL: %s", SDL_GetError());
948     abort();
949   }
950 
951   bDone = true;
952 }
953 
vInitConfig()954 void Window::vInitConfig()
955 {
956   m_oConfig.vClear();
957 
958   // History section
959   //
960   m_poHistoryConfig = m_oConfig.poAddSection("History");
961   m_poHistoryConfig->vSetKey("freeze", false );
962   m_poHistoryConfig->vSetKey("0",      ""    );
963   m_poHistoryConfig->vSetKey("1",      ""    );
964   m_poHistoryConfig->vSetKey("2",      ""    );
965   m_poHistoryConfig->vSetKey("3",      ""    );
966   m_poHistoryConfig->vSetKey("4",      ""    );
967   m_poHistoryConfig->vSetKey("5",      ""    );
968   m_poHistoryConfig->vSetKey("6",      ""    );
969   m_poHistoryConfig->vSetKey("7",      ""    );
970   m_poHistoryConfig->vSetKey("8",      ""    );
971   m_poHistoryConfig->vSetKey("9",      ""    );
972 
973   // Directories section
974   //
975   m_poDirConfig = m_oConfig.poAddSection("Directories");
976   m_poDirConfig->vSetKey("gb_roms",   "" );
977   m_poDirConfig->vSetKey("gba_roms",  "" );
978   m_poDirConfig->vSetKey("batteries", "" );
979   m_poDirConfig->vSetKey("saves",     "" );
980   m_poDirConfig->vSetKey("captures",  "" );
981 
982   // Core section
983   //
984   m_poCoreConfig = m_oConfig.poAddSection("Core");
985   m_poCoreConfig->vSetKey("load_game_auto",    false        );
986   m_poCoreConfig->vSetKey("frameskip",         "auto"       );
987   m_poCoreConfig->vSetKey("throttle",          0            );
988   m_poCoreConfig->vSetKey("layer_bg0",         true         );
989   m_poCoreConfig->vSetKey("layer_bg1",         true         );
990   m_poCoreConfig->vSetKey("layer_bg2",         true         );
991   m_poCoreConfig->vSetKey("layer_bg3",         true         );
992   m_poCoreConfig->vSetKey("layer_obj",         true         );
993   m_poCoreConfig->vSetKey("layer_win0",        true         );
994   m_poCoreConfig->vSetKey("layer_win1",        true         );
995   m_poCoreConfig->vSetKey("layer_objwin",      true         );
996   m_poCoreConfig->vSetKey("use_bios_file",     false        );
997   m_poCoreConfig->vSetKey("bios_file",         ""           );
998   m_poCoreConfig->vSetKey("save_type",         SaveAuto     );
999   m_poCoreConfig->vSetKey("flash_size",        64           );
1000   m_poCoreConfig->vSetKey("gb_border",         true         );
1001   m_poCoreConfig->vSetKey("gb_printer",        false        );
1002   m_poCoreConfig->vSetKey("emulator_type",     EmulatorAuto );
1003   m_poCoreConfig->vSetKey("screenshot_format", "png"        );
1004 
1005   // Display section
1006   //
1007   m_poDisplayConfig = m_oConfig.poAddSection("Display");
1008   m_poDisplayConfig->vSetKey("scale",               1              );
1009   m_poDisplayConfig->vSetKey("show_speed",          ShowPercentage );
1010   m_poDisplayConfig->vSetKey("pause_when_inactive", true           );
1011   m_poDisplayConfig->vSetKey("filter2x",            FilterNone     );
1012   m_poDisplayConfig->vSetKey("filterIB",            FilterIBNone   );
1013 #ifdef MMX
1014   m_poDisplayConfig->vSetKey("filter_disable_mmx",  false          );
1015 #endif // MMX
1016 
1017   // Sound section
1018   //
1019   m_poSoundConfig = m_oConfig.poAddSection("Sound");
1020   m_poSoundConfig->vSetKey("status",         "on"     );
1021   m_poSoundConfig->vSetKey("echo",           false    );
1022   m_poSoundConfig->vSetKey("low_pass",       false    );
1023   m_poSoundConfig->vSetKey("reverse_stereo", false    );
1024   m_poSoundConfig->vSetKey("channel_1",      true     );
1025   m_poSoundConfig->vSetKey("channel_2",      true     );
1026   m_poSoundConfig->vSetKey("channel_3",      true     );
1027   m_poSoundConfig->vSetKey("channel_4",      true     );
1028   m_poSoundConfig->vSetKey("channel_A",      true     );
1029   m_poSoundConfig->vSetKey("channel_B",      true     );
1030   m_poSoundConfig->vSetKey("quality",        Sound22K );
1031   m_poSoundConfig->vSetKey("volume",         Sound100 );
1032 
1033   // Input section
1034   //
1035   JoypadConfig oJoypadConfig;
1036   oJoypadConfig.vSetDefault();
1037   m_poInputConfig = m_oConfig.poAddSection("Input");
1038   m_poInputConfig->vSetKey("active_joypad", m_iJoypadMin );
1039   for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++)
1040   {
1041     char csPrefix[20];
1042     snprintf(csPrefix, sizeof(csPrefix), "joypad%d_", i);
1043     std::string sPrefix(csPrefix);
1044     m_poInputConfig->vSetKey(sPrefix + "up",      oJoypadConfig.m_uiUp      );
1045     m_poInputConfig->vSetKey(sPrefix + "down",    oJoypadConfig.m_uiDown    );
1046     m_poInputConfig->vSetKey(sPrefix + "left",    oJoypadConfig.m_uiLeft    );
1047     m_poInputConfig->vSetKey(sPrefix + "right",   oJoypadConfig.m_uiRight   );
1048     m_poInputConfig->vSetKey(sPrefix + "A",       oJoypadConfig.m_uiA       );
1049     m_poInputConfig->vSetKey(sPrefix + "B",       oJoypadConfig.m_uiB       );
1050     m_poInputConfig->vSetKey(sPrefix + "L",       oJoypadConfig.m_uiL       );
1051     m_poInputConfig->vSetKey(sPrefix + "R",       oJoypadConfig.m_uiR       );
1052     m_poInputConfig->vSetKey(sPrefix + "select",  oJoypadConfig.m_uiSelect  );
1053     m_poInputConfig->vSetKey(sPrefix + "start",   oJoypadConfig.m_uiStart   );
1054     m_poInputConfig->vSetKey(sPrefix + "speed",   oJoypadConfig.m_uiSpeed   );
1055     m_poInputConfig->vSetKey(sPrefix + "capture", oJoypadConfig.m_uiCapture );
1056   }
1057   m_poInputConfig->vSetKey("autofire_A", false );
1058   m_poInputConfig->vSetKey("autofire_B", false );
1059   m_poInputConfig->vSetKey("autofire_L", false );
1060   m_poInputConfig->vSetKey("autofire_R", false );
1061 }
1062 
vCheckConfig()1063 void Window::vCheckConfig()
1064 {
1065   int iValue;
1066   int iAdjusted;
1067   std::string sValue;
1068 
1069   // Directories section
1070   //
1071   sValue = m_poDirConfig->sGetKey("gb_roms");
1072   if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR))
1073   {
1074     m_poDirConfig->vSetKey("gb_roms", "");
1075   }
1076   sValue = m_poDirConfig->sGetKey("gba_roms");
1077   if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR))
1078   {
1079     m_poDirConfig->vSetKey("gba_roms", "");
1080   }
1081   sValue = m_poDirConfig->sGetKey("batteries");
1082   if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR))
1083   {
1084     m_poDirConfig->vSetKey("batteries", "");
1085   }
1086   sValue = m_poDirConfig->sGetKey("saves");
1087   if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR))
1088   {
1089     m_poDirConfig->vSetKey("saves", "");
1090   }
1091   sValue = m_poDirConfig->sGetKey("captures");
1092   if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_DIR))
1093   {
1094     m_poDirConfig->vSetKey("captures", "");
1095   }
1096 
1097   // Core section
1098   //
1099   if (m_poCoreConfig->sGetKey("frameskip") != "auto")
1100   {
1101     iValue = m_poCoreConfig->oGetKey<int>("frameskip");
1102     iAdjusted = CLAMP(iValue, m_iFrameskipMin, m_iFrameskipMax);
1103     if (iValue != iAdjusted)
1104     {
1105       m_poCoreConfig->vSetKey("frameskip", iAdjusted);
1106     }
1107   }
1108 
1109   iValue = m_poCoreConfig->oGetKey<int>("throttle");
1110   if (iValue != 0)
1111   {
1112     iAdjusted = CLAMP(iValue, m_iThrottleMin, m_iThrottleMax);
1113     if (iValue != iAdjusted)
1114     {
1115       m_poCoreConfig->vSetKey("throttle", iAdjusted);
1116     }
1117   }
1118 
1119   sValue = m_poCoreConfig->sGetKey("bios_file");
1120   if (sValue != "" && ! Glib::file_test(sValue, Glib::FILE_TEST_IS_REGULAR))
1121   {
1122     m_poCoreConfig->vSetKey("bios_file", "");
1123   }
1124   if (m_poCoreConfig->sGetKey("bios_file") == "")
1125   {
1126     m_poCoreConfig->vSetKey("use_bios_file", false);
1127   }
1128 
1129   iValue = m_poCoreConfig->oGetKey<int>("save_type");
1130   if (iValue != 0)
1131   {
1132     iAdjusted = CLAMP(iValue, m_iSaveTypeMin, m_iSaveTypeMax);
1133     if (iValue != iAdjusted)
1134     {
1135       m_poCoreConfig->vSetKey("save_type", iAdjusted);
1136     }
1137   }
1138 
1139   iValue = m_poCoreConfig->oGetKey<int>("flash_size");
1140   if (iValue != 64 && iValue != 128)
1141   {
1142     m_poCoreConfig->vSetKey("flash_size", 64);
1143   }
1144 
1145   iValue = m_poCoreConfig->oGetKey<int>("emulator_type");
1146   iAdjusted = CLAMP(iValue, m_iEmulatorTypeMin, m_iEmulatorTypeMax);
1147   if (iValue != iAdjusted)
1148   {
1149     m_poCoreConfig->vSetKey("emulator_type", iAdjusted);
1150   }
1151 
1152   sValue = m_poCoreConfig->sGetKey("screenshot_format");
1153   if (sValue != "png" && sValue != "bmp")
1154   {
1155     sValue = "png";
1156   }
1157 
1158   // Display section
1159   //
1160   iValue = m_poDisplayConfig->oGetKey<int>("scale");
1161   iAdjusted = CLAMP(iValue, m_iScaleMin, m_iScaleMax);
1162   if (iValue != iAdjusted)
1163   {
1164     m_poDisplayConfig->vSetKey("scale", iAdjusted);
1165   }
1166 
1167   iValue = m_poDisplayConfig->oGetKey<int>("show_speed");
1168   iAdjusted = CLAMP(iValue, m_iShowSpeedMin, m_iShowSpeedMax);
1169   if (iValue != iAdjusted)
1170   {
1171     m_poDisplayConfig->vSetKey("show_speed", iAdjusted);
1172   }
1173 
1174   iValue = m_poDisplayConfig->oGetKey<int>("filter2x");
1175   iAdjusted = CLAMP(iValue, m_iFilter2xMin, m_iFilter2xMax);
1176   if (iValue != iAdjusted)
1177   {
1178     m_poDisplayConfig->vSetKey("filter2x", iAdjusted);
1179   }
1180 
1181   iValue = m_poDisplayConfig->oGetKey<int>("filterIB");
1182   iAdjusted = CLAMP(iValue, m_iFilterIBMin, m_iFilterIBMax);
1183   if (iValue != iAdjusted)
1184   {
1185     m_poDisplayConfig->vSetKey("filterIB", iAdjusted);
1186   }
1187 
1188   // Sound section
1189   //
1190   sValue = m_poSoundConfig->sGetKey("status");
1191   if (sValue != "off" && sValue != "on" && sValue != "mute")
1192   {
1193     m_poSoundConfig->vSetKey("status", "on");
1194   }
1195 
1196   iValue = m_poSoundConfig->oGetKey<int>("quality");
1197   iAdjusted = CLAMP(iValue, m_iSoundQualityMin, m_iSoundQualityMax);
1198   if (iValue != iAdjusted)
1199   {
1200     m_poSoundConfig->vSetKey("quality", iAdjusted);
1201   }
1202 
1203   iValue = m_poSoundConfig->oGetKey<int>("volume");
1204   iAdjusted = CLAMP(iValue, m_iSoundVolumeMin, m_iSoundVolumeMax);
1205   if (iValue != iAdjusted)
1206   {
1207     m_poSoundConfig->vSetKey("volume", iAdjusted);
1208   }
1209 
1210   // Input section
1211   //
1212   iValue = m_poInputConfig->oGetKey<int>("active_joypad");
1213   iAdjusted = CLAMP(iValue, m_iJoypadMin, m_iJoypadMax);
1214   if (iValue != iAdjusted)
1215   {
1216     m_poInputConfig->vSetKey("active_joypad", iAdjusted);
1217   }
1218 }
1219 
vLoadConfig(const std::string & _rsFile)1220 void Window::vLoadConfig(const std::string & _rsFile)
1221 {
1222   try
1223   {
1224     m_oConfig.vLoad(_rsFile, false, false);
1225   }
1226   catch (const Glib::Error & e)
1227   {
1228     vPopupError(e.what().c_str());
1229   }
1230 }
1231 
vSaveConfig(const std::string & _rsFile)1232 void Window::vSaveConfig(const std::string & _rsFile)
1233 {
1234   try
1235   {
1236     m_oConfig.vSave(_rsFile);
1237   }
1238   catch (const Glib::Error & e)
1239   {
1240     vPopupError(e.what().c_str());
1241   }
1242 }
1243 
vLoadHistoryFromConfig()1244 void Window::vLoadHistoryFromConfig()
1245 {
1246   char csKey[] = "0";
1247   for (int i = 0; i < 10; i++, csKey[0]++)
1248   {
1249     std::string sFile = m_poHistoryConfig->sGetKey(csKey);
1250     if (sFile == "")
1251     {
1252       break;
1253     }
1254     m_listHistory.push_back(sFile);
1255   }
1256 }
1257 
vSaveHistoryToConfig()1258 void Window::vSaveHistoryToConfig()
1259 {
1260   char csKey[] = "0";
1261   for (std::list<std::string>::const_iterator it = m_listHistory.begin();
1262        it != m_listHistory.end();
1263        it++, csKey[0]++)
1264   {
1265     m_poHistoryConfig->vSetKey(csKey, *it);
1266   }
1267 }
1268 
vHistoryAdd(const std::string & _rsFile)1269 void Window::vHistoryAdd(const std::string & _rsFile)
1270 {
1271   if (m_poHistoryConfig->oGetKey<bool>("freeze"))
1272   {
1273     return;
1274   }
1275 
1276   m_listHistory.remove(_rsFile);
1277   m_listHistory.push_front(_rsFile);
1278   if (m_listHistory.size() > 10)
1279   {
1280     m_listHistory.pop_back();
1281   }
1282 
1283   vUpdateHistoryMenu();
1284 }
1285 
vClearHistoryMenu()1286 void Window::vClearHistoryMenu()
1287 {
1288   Gtk::Menu_Helpers::MenuList::iterator it = m_poRecentMenu->items().begin();
1289   for (int i = 0; i < 3; i++, it++)
1290     ;
1291 
1292   m_poRecentMenu->items().erase(it, m_poRecentMenu->items().end());
1293 }
1294 
vUpdateHistoryMenu()1295 void Window::vUpdateHistoryMenu()
1296 {
1297   vClearHistoryMenu();
1298 
1299   guint uiAccelKey = GDK_F1;
1300   for (std::list<std::string>::const_iterator it = m_listHistory.begin();
1301        it != m_listHistory.end();
1302        it++, uiAccelKey++)
1303   {
1304     Gtk::Image * poImage = Gtk::manage(new Gtk::Image(Gtk::Stock::OPEN, Gtk::ICON_SIZE_MENU));
1305     Glib::ustring sLabel = Glib::path_get_basename(*it);
1306     VBA::ImageMenuItem * poIMI = Gtk::manage(new VBA::ImageMenuItem(*poImage, sLabel));
1307 
1308     m_oTooltips.set_tip(*poIMI, *it);
1309 
1310     poIMI->signal_activate().connect(SigC::bind<std::string>(
1311                                       SigC::slot(*this, &Window::vOnRecentFile),
1312                                       *it));
1313 
1314     poIMI->set_accel_key(Gtk::AccelKey(uiAccelKey, Gdk::CONTROL_MASK));
1315     poIMI->accelerate(*this);
1316 
1317     poIMI->show();
1318     m_poRecentMenu->items().push_back(*poIMI);
1319   }
1320 }
1321 
vLoadJoypadsFromConfig()1322 void Window::vLoadJoypadsFromConfig()
1323 {
1324   m_oJoypads.clear();
1325 
1326   for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++)
1327   {
1328     char csPrefix[20];
1329     snprintf(csPrefix, sizeof(csPrefix), "joypad%d_", i);
1330     std::string sPrefix(csPrefix);
1331 
1332     JoypadConfig oJoypadConfig;
1333     oJoypadConfig.m_uiUp      = m_poInputConfig->oGetKey<guint>(sPrefix + "up");
1334     oJoypadConfig.m_uiDown    = m_poInputConfig->oGetKey<guint>(sPrefix + "down");
1335     oJoypadConfig.m_uiLeft    = m_poInputConfig->oGetKey<guint>(sPrefix + "left");
1336     oJoypadConfig.m_uiRight   = m_poInputConfig->oGetKey<guint>(sPrefix + "right");
1337     oJoypadConfig.m_uiA       = m_poInputConfig->oGetKey<guint>(sPrefix + "A");
1338     oJoypadConfig.m_uiB       = m_poInputConfig->oGetKey<guint>(sPrefix + "B");
1339     oJoypadConfig.m_uiL       = m_poInputConfig->oGetKey<guint>(sPrefix + "L");
1340     oJoypadConfig.m_uiR       = m_poInputConfig->oGetKey<guint>(sPrefix + "R");
1341     oJoypadConfig.m_uiSelect  = m_poInputConfig->oGetKey<guint>(sPrefix + "select");
1342     oJoypadConfig.m_uiStart   = m_poInputConfig->oGetKey<guint>(sPrefix + "start");
1343     oJoypadConfig.m_uiSpeed   = m_poInputConfig->oGetKey<guint>(sPrefix + "speed");
1344     oJoypadConfig.m_uiCapture = m_poInputConfig->oGetKey<guint>(sPrefix + "capture");
1345 
1346     m_oJoypads.push_back(oJoypadConfig);
1347   }
1348 }
1349 
vSaveJoypadsToConfig()1350 void Window::vSaveJoypadsToConfig()
1351 {
1352   for (int i = m_iJoypadMin; i <= m_iJoypadMax; i++)
1353   {
1354     char csPrefix[20];
1355     snprintf(csPrefix, sizeof(csPrefix), "joypad%d_", i);
1356     std::string sPrefix(csPrefix);
1357 
1358     m_poInputConfig->vSetKey(sPrefix + "up",      m_oJoypads[i - 1].m_uiUp      );
1359     m_poInputConfig->vSetKey(sPrefix + "down",    m_oJoypads[i - 1].m_uiDown    );
1360     m_poInputConfig->vSetKey(sPrefix + "left",    m_oJoypads[i - 1].m_uiLeft    );
1361     m_poInputConfig->vSetKey(sPrefix + "right",   m_oJoypads[i - 1].m_uiRight   );
1362     m_poInputConfig->vSetKey(sPrefix + "A",       m_oJoypads[i - 1].m_uiA       );
1363     m_poInputConfig->vSetKey(sPrefix + "B",       m_oJoypads[i - 1].m_uiB       );
1364     m_poInputConfig->vSetKey(sPrefix + "L",       m_oJoypads[i - 1].m_uiL       );
1365     m_poInputConfig->vSetKey(sPrefix + "R",       m_oJoypads[i - 1].m_uiR       );
1366     m_poInputConfig->vSetKey(sPrefix + "select",  m_oJoypads[i - 1].m_uiSelect  );
1367     m_poInputConfig->vSetKey(sPrefix + "start",   m_oJoypads[i - 1].m_uiStart   );
1368     m_poInputConfig->vSetKey(sPrefix + "speed",   m_oJoypads[i - 1].m_uiSpeed   );
1369     m_poInputConfig->vSetKey(sPrefix + "capture", m_oJoypads[i - 1].m_uiCapture );
1370   }
1371 }
1372 
vUpdateScreen()1373 void Window::vUpdateScreen()
1374 {
1375   if (m_eCartridge == CartridgeGB)
1376   {
1377     if (gbBorderOn)
1378     {
1379       m_iScreenWidth     = m_iSGBScreenWidth;
1380       m_iScreenHeight    = m_iSGBScreenHeight;
1381       gbBorderLineSkip   = m_iSGBScreenWidth;
1382       gbBorderColumnSkip = (m_iSGBScreenWidth - m_iGBScreenWidth) / 2;
1383       gbBorderRowSkip    = (m_iSGBScreenHeight - m_iGBScreenHeight) / 2;
1384     }
1385     else
1386     {
1387       m_iScreenWidth     = m_iGBScreenWidth;
1388       m_iScreenHeight    = m_iGBScreenHeight;
1389       gbBorderLineSkip   = m_iGBScreenWidth;
1390       gbBorderColumnSkip = 0;
1391       gbBorderRowSkip    = 0;
1392     }
1393   }
1394   else if (m_eCartridge == CartridgeGBA)
1395   {
1396     m_iScreenWidth  = m_iGBAScreenWidth;
1397     m_iScreenHeight = m_iGBAScreenHeight;
1398   }
1399 
1400   g_return_if_fail(m_iScreenWidth >= 1 && m_iScreenHeight >= 1);
1401 
1402   m_poScreenArea->vSetSize(m_iScreenWidth, m_iScreenHeight);
1403   m_poScreenArea->vSetScale(m_poDisplayConfig->oGetKey<int>("scale"));
1404 
1405   resize(1, 1);
1406 
1407   if (emulating)
1408   {
1409     vDrawScreen();
1410   }
1411   else
1412   {
1413     vDrawDefaultScreen();
1414   }
1415 }
1416 
bLoadROM(const std::string & _rsFile)1417 bool Window::bLoadROM(const std::string & _rsFile)
1418 {
1419   vOnFileClose();
1420 
1421   m_sRomFile = _rsFile;
1422   const char * csFile = _rsFile.c_str();
1423 
1424   IMAGE_TYPE eType = utilFindType(csFile);
1425   if (eType == IMAGE_UNKNOWN)
1426   {
1427     vPopupError(_("Unknown file type %s"), csFile);
1428     return false;
1429   }
1430 
1431   bool bLoaded = false;
1432   if (eType == IMAGE_GB)
1433   {
1434     bLoaded = gbLoadRom(csFile);
1435     if (bLoaded)
1436     {
1437       m_eCartridge = CartridgeGB;
1438       m_stEmulator = GBSystem;
1439     }
1440   }
1441   else if (eType == IMAGE_GBA)
1442   {
1443     int iSize = CPULoadRom(csFile);
1444     bLoaded = (iSize > 0);
1445     if (bLoaded)
1446     {
1447       m_eCartridge = CartridgeGBA;
1448       m_stEmulator = GBASystem;
1449 
1450       useBios = m_poCoreConfig->oGetKey<bool>("use_bios_file");
1451       CPUInit(m_poCoreConfig->sGetKey("bios_file").c_str(), useBios);
1452       CPUReset();
1453 
1454       // If the bios file was rejected by CPUInit
1455       if (m_poCoreConfig->oGetKey<bool>("use_bios_file") && ! useBios)
1456       {
1457         m_poUseBiosItem->set_active(false);
1458         m_poUseBiosItem->set_sensitive(false);
1459         m_poCoreConfig->vSetKey("bios_file", "");
1460       }
1461     }
1462   }
1463 
1464   if (! bLoaded)
1465   {
1466     return false;
1467   }
1468 
1469   vLoadBattery();
1470   vUpdateScreen();
1471 
1472   debugger = false; // May cause conflicts
1473   emulating = 1;
1474   m_bWasEmulating = false;
1475   m_uiThrottleDelay = 0;
1476 
1477   if (m_eCartridge == CartridgeGBA)
1478   {
1479     soundSetQuality(m_eSoundQuality);
1480   }
1481   else
1482   {
1483     gbSoundSetQuality(m_eSoundQuality);
1484   }
1485 
1486   vUpdateGameSlots();
1487   vHistoryAdd(_rsFile);
1488 
1489   for (std::list<Gtk::Widget *>::iterator it = m_listSensitiveWhenPlaying.begin();
1490        it != m_listSensitiveWhenPlaying.end();
1491        it++)
1492   {
1493     (*it)->set_sensitive();
1494   }
1495 
1496   if (m_poCoreConfig->oGetKey<bool>("load_game_auto"))
1497   {
1498     vOnLoadGameMostRecent();
1499   }
1500 
1501   vStartEmu();
1502 
1503   return true;
1504 }
1505 
vPopupError(const char * _csFormat,...)1506 void Window::vPopupError(const char * _csFormat, ...)
1507 {
1508   va_list args;
1509   va_start(args, _csFormat);
1510   char * csMsg = g_strdup_vprintf(_csFormat, args);
1511   va_end(args);
1512 
1513   Gtk::MessageDialog oDialog(*this,
1514                              csMsg,
1515 #ifndef GTKMM20
1516                              false,
1517 #endif // ! GTKMM20
1518                              Gtk::MESSAGE_ERROR,
1519                              Gtk::BUTTONS_OK);
1520   oDialog.run();
1521   g_free(csMsg);
1522 }
1523 
vPopupErrorV(const char * _csFormat,va_list _args)1524 void Window::vPopupErrorV(const char * _csFormat, va_list _args)
1525 {
1526   char * csMsg = g_strdup_vprintf(_csFormat, _args);
1527 
1528   Gtk::MessageDialog oDialog(*this,
1529                              csMsg,
1530 #ifndef GTKMM20
1531                              false,
1532 #endif // ! GTKMM20
1533                              Gtk::MESSAGE_ERROR,
1534                              Gtk::BUTTONS_OK);
1535   oDialog.run();
1536   g_free(csMsg);
1537 }
1538 
vDrawScreen()1539 void Window::vDrawScreen()
1540 {
1541   m_poScreenArea->vDrawPixels(pix);
1542 }
1543 
vDrawDefaultScreen()1544 void Window::vDrawDefaultScreen()
1545 {
1546   m_poScreenArea->vDrawColor(0x000000); // Black
1547 }
1548 
vSetDefaultTitle()1549 void Window::vSetDefaultTitle()
1550 {
1551   set_title("VBA");
1552 }
1553 
vShowSpeed(int _iSpeed)1554 void Window::vShowSpeed(int _iSpeed)
1555 {
1556   char csTitle[50];
1557 
1558   if (m_eShowSpeed == ShowPercentage)
1559   {
1560     snprintf(csTitle, 50, "VBA - %d%%", _iSpeed);
1561     set_title(csTitle);
1562   }
1563   else if (m_eShowSpeed == ShowDetailed)
1564   {
1565     snprintf(csTitle, 50, "VBA - %d%% (%d, %d fps)",
1566              _iSpeed, systemFrameSkip, systemFPS);
1567     set_title(csTitle);
1568   }
1569 }
1570 
vComputeFrameskip(int _iRate)1571 void Window::vComputeFrameskip(int _iRate)
1572 {
1573   static u32 uiLastTime = 0;
1574   static int iFrameskipAdjust = 0;
1575 
1576   u32 uiTime = SDL_GetTicks();
1577 
1578   if (m_bWasEmulating)
1579   {
1580     int iWantedSpeed = 100;
1581 
1582     if (m_iThrottle > 0)
1583     {
1584       if (! speedup)
1585       {
1586         u32 uiDiff  = uiTime - m_uiThrottleLastTime;
1587         int iTarget = 1000000 / (_iRate * m_iThrottle);
1588         int iDelay  = iTarget - uiDiff;
1589         if (iDelay > 0)
1590         {
1591           m_uiThrottleDelay = iDelay;
1592         }
1593       }
1594       iWantedSpeed = m_iThrottle;
1595     }
1596 
1597     if (m_bAutoFrameskip)
1598     {
1599       u32 uiDiff = uiTime - uiLastTime;
1600       int iSpeed = iWantedSpeed;
1601 
1602       if (uiDiff != 0)
1603       {
1604         iSpeed = (1000000 / _iRate) / uiDiff;
1605       }
1606 
1607       if (iSpeed >= iWantedSpeed - 2)
1608       {
1609         iFrameskipAdjust++;
1610         if (iFrameskipAdjust >= 3)
1611         {
1612           iFrameskipAdjust = 0;
1613           if (systemFrameSkip > 0)
1614           {
1615             systemFrameSkip--;
1616           }
1617         }
1618       }
1619       else
1620       {
1621         if (iSpeed < iWantedSpeed - 20)
1622         {
1623           iFrameskipAdjust -= ((iWantedSpeed - 10) - iSpeed) / 5;
1624         }
1625         else if (systemFrameSkip < 9)
1626         {
1627           iFrameskipAdjust--;
1628         }
1629 
1630         if (iFrameskipAdjust <= -2)
1631         {
1632           iFrameskipAdjust += 2;
1633           if (systemFrameSkip < 9)
1634           {
1635             systemFrameSkip++;
1636           }
1637         }
1638       }
1639     }
1640   }
1641   else
1642   {
1643     m_bWasEmulating = true;
1644   }
1645 
1646   uiLastTime = uiTime;
1647   m_uiThrottleLastTime = uiTime;
1648 }
1649 
vCaptureScreen(int _iNum)1650 void Window::vCaptureScreen(int _iNum)
1651 {
1652   std::string sBaseName;
1653   std::string sCaptureDir = m_poDirConfig->sGetKey("captures");
1654   if (sCaptureDir == "")
1655   {
1656     sBaseName = sCutSuffix(m_sRomFile);
1657   }
1658   else
1659   {
1660     sBaseName = sCaptureDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile));
1661   }
1662   std::string sFormat = m_poCoreConfig->sGetKey("screenshot_format");
1663 
1664   char * csFile = g_strdup_printf("%s_%02d.%s",
1665                                   sBaseName.c_str(),
1666                                   _iNum,
1667                                   sFormat.c_str());
1668   if (sFormat == "png")
1669   {
1670     m_stEmulator.emuWritePNG(csFile);
1671   }
1672   else
1673   {
1674     m_stEmulator.emuWriteBMP(csFile);
1675   }
1676   g_free(csFile);
1677 }
1678 
uiReadJoypad()1679 u32 Window::uiReadJoypad()
1680 {
1681   u32 uiJoypad = m_uiJoypadState;
1682 
1683   if (m_uiAutofireState != 0)
1684   {
1685     uiJoypad &= ~m_uiAutofireState;
1686     if (m_bAutofireToggle)
1687     {
1688       uiJoypad |= m_uiAutofireState;
1689     }
1690     m_bAutofireToggle = ! m_bAutofireToggle;
1691   }
1692 
1693   return uiJoypad;
1694 }
1695 
vCreateFileOpenDialog()1696 void Window::vCreateFileOpenDialog()
1697 {
1698   if (m_poFileOpenDialog != NULL)
1699   {
1700     return;
1701   }
1702 
1703   std::string sGBDir  = m_poDirConfig->sGetKey("gb_roms");
1704   std::string sGBADir = m_poDirConfig->sGetKey("gba_roms");
1705 
1706 #ifdef GTKMM20
1707 
1708   Gtk::FileSelection * poDialog = new Gtk::FileSelection(_("Open"));
1709   poDialog->set_transient_for(*this);
1710 
1711   if (sGBADir != "")
1712   {
1713     poDialog->set_filename(sGBADir + "/");
1714   }
1715   else if (sGBDir != "")
1716   {
1717     poDialog->set_filename(sGBDir + "/");
1718   }
1719 
1720 #else // ! GTKMM20
1721 
1722   Gtk::FileChooserDialog * poDialog = new Gtk::FileChooserDialog(*this, _("Open"));
1723   poDialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1724   poDialog->add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_OK);
1725 
1726   if (sGBDir != "")
1727   {
1728     poDialog->add_shortcut_folder(sGBDir);
1729     poDialog->set_current_folder(sGBDir);
1730   }
1731 
1732   if (sGBADir != "" && sGBADir != sGBDir)
1733   {
1734     poDialog->add_shortcut_folder(sGBADir);
1735     poDialog->set_current_folder(sGBADir);
1736   }
1737 
1738   const char * acsPattern[] =
1739   {
1740     // GBA
1741     "*.[bB][iI][nN]", "*.[aA][gG][bB]", "*.[gG][bB][aA]",
1742     // GB
1743     "*.[gG][bB]", "*.[sS][gG][bB]", "*.[cC][gG][bB]", "*.[gG][bB][cC]",
1744     // Both
1745     "*.[mM][bB]", "*.[eE][lL][fF]", "*.[zZ][iI][pP]", "*.[zZ]", "*.[gG][zZ]"
1746   };
1747 
1748   Gtk::FileFilter oAllGBAFilter;
1749   oAllGBAFilter.set_name(_("All Gameboy Advance files"));
1750   for (guint i = 0; i < G_N_ELEMENTS(acsPattern); i++)
1751   {
1752     oAllGBAFilter.add_pattern(acsPattern[i]);
1753   }
1754 
1755   Gtk::FileFilter oGBAFilter;
1756   oGBAFilter.set_name(_("Gameboy Advance files"));
1757   for (int i = 0; i < 3; i++)
1758   {
1759     oGBAFilter.add_pattern(acsPattern[i]);
1760   }
1761 
1762   Gtk::FileFilter oGBFilter;
1763   oGBFilter.set_name(_("Gameboy files"));
1764   for (int i = 3; i < 7; i++)
1765   {
1766     oGBFilter.add_pattern(acsPattern[i]);
1767   }
1768 
1769   poDialog->add_filter(oAllGBAFilter);
1770   poDialog->add_filter(oGBAFilter);
1771   poDialog->add_filter(oGBFilter);
1772 
1773 #endif // ! GTKMM20
1774 
1775   m_poFileOpenDialog = poDialog;
1776 }
1777 
vLoadBattery()1778 void Window::vLoadBattery()
1779 {
1780   std::string sBattery;
1781   std::string sDir = m_poDirConfig->sGetKey("batteries");
1782   if (sDir == "")
1783   {
1784     sBattery = sCutSuffix(m_sRomFile) + ".sav";
1785   }
1786   else
1787   {
1788     sBattery = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)) + ".sav";
1789   }
1790 
1791   if (m_stEmulator.emuReadBattery(sBattery.c_str()))
1792   {
1793     systemScreenMessage(_("Loaded battery"));
1794   }
1795 }
1796 
vSaveBattery()1797 void Window::vSaveBattery()
1798 {
1799   std::string sBattery;
1800   std::string sDir = m_poDirConfig->sGetKey("batteries");
1801   if (sDir == "")
1802   {
1803     sBattery = sCutSuffix(m_sRomFile) + ".sav";
1804   }
1805   else
1806   {
1807     sBattery = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile)) + ".sav";
1808   }
1809 
1810   if (m_stEmulator.emuWriteBattery(sBattery.c_str()))
1811   {
1812     systemScreenMessage(_("Saved battery"));
1813   }
1814 }
1815 
vStartEmu()1816 void Window::vStartEmu()
1817 {
1818   if (m_oEmuSig.connected())
1819   {
1820     return;
1821   }
1822 
1823   m_oEmuSig = Glib::signal_idle().connect(SigC::slot(*this, &Window::bOnEmuIdle),
1824                                           Glib::PRIORITY_DEFAULT_IDLE);
1825 }
1826 
vStopEmu()1827 void Window::vStopEmu()
1828 {
1829   m_oEmuSig.disconnect();
1830   m_bWasEmulating = false;
1831 }
1832 
vSetThrottle(int _iPercent)1833 void Window::vSetThrottle(int _iPercent)
1834 {
1835   m_iThrottle = _iPercent;
1836   m_poCoreConfig->vSetKey("throttle", _iPercent);
1837 }
1838 
vSelectBestThrottleItem()1839 void Window::vSelectBestThrottleItem()
1840 {
1841   struct
1842   {
1843     const char * m_csName;
1844     const int    m_iThrottle;
1845   }
1846   astThrottle[] =
1847   {
1848     { "ThrottleNoThrottle",   0 },
1849     { "Throttle25",          25 },
1850     { "Throttle50",          50 },
1851     { "Throttle100",        100 },
1852     { "Throttle150",        150 },
1853     { "Throttle200",        200 }
1854   };
1855   for (guint i = 0; i < G_N_ELEMENTS(astThrottle); i++)
1856   {
1857     Gtk::CheckMenuItem * poCMI;
1858     poCMI = dynamic_cast<Gtk::CheckMenuItem *>(m_poXml->get_widget(astThrottle[i].m_csName));
1859     if (astThrottle[i].m_iThrottle == m_iThrottle)
1860     {
1861       poCMI->set_active();
1862     }
1863   }
1864 }
1865 
vUpdateGameSlots()1866 void Window::vUpdateGameSlots()
1867 {
1868   if (m_eCartridge == CartridgeNone)
1869   {
1870     std::string sDateTime = _("----/--/-- --:--:--");
1871 
1872     for (int i = 0; i < 10; i++)
1873     {
1874       char csPrefix[10];
1875       snprintf(csPrefix, sizeof(csPrefix), "%2d ", i + 1);
1876 
1877       Gtk::Label * poLabel;
1878       poLabel = dynamic_cast<Gtk::Label *>(m_apoLoadGameItem[i]->get_child());
1879       poLabel->set_text(csPrefix + sDateTime);
1880       m_apoLoadGameItem[i]->set_sensitive(false);
1881 
1882       poLabel = dynamic_cast<Gtk::Label *>(m_apoSaveGameItem[i]->get_child());
1883       poLabel->set_text(csPrefix + sDateTime);
1884       m_apoSaveGameItem[i]->set_sensitive(false);
1885 
1886       m_astGameSlot[i].m_bEmpty = true;
1887     }
1888   }
1889   else
1890   {
1891     std::string sFileBase;
1892     std::string sDir = m_poDirConfig->sGetKey("saves");
1893     if (sDir == "")
1894     {
1895       sFileBase = sCutSuffix(m_sRomFile);
1896     }
1897     else
1898     {
1899       sFileBase = sDir + "/" + sCutSuffix(Glib::path_get_basename(m_sRomFile));
1900     }
1901 
1902     const char * csDateFormat = _("%Y/%m/%d %H:%M:%S");
1903 
1904     for (int i = 0; i < 10; i++)
1905     {
1906       char csPrefix[10];
1907       snprintf(csPrefix, sizeof(csPrefix), "%2d ", i + 1);
1908 
1909       char csSlot[10];
1910       snprintf(csSlot, sizeof(csSlot), "%d", i + 1);
1911       m_astGameSlot[i].m_sFile = sFileBase + csSlot + ".sgm";
1912 
1913       std::string sDateTime;
1914       struct stat stStat;
1915       if (stat(m_astGameSlot[i].m_sFile.c_str(), &stStat) == -1)
1916       {
1917         sDateTime = _("----/--/-- --:--:--");
1918         m_astGameSlot[i].m_bEmpty = true;
1919       }
1920       else
1921       {
1922         char csDateTime[30];
1923         strftime(csDateTime, sizeof(csDateTime), csDateFormat,
1924                  localtime(&stStat.st_mtime));
1925         sDateTime = csDateTime;
1926         m_astGameSlot[i].m_bEmpty = false;
1927         m_astGameSlot[i].m_uiTime = stStat.st_mtime;
1928       }
1929 
1930       Gtk::Label * poLabel;
1931       poLabel = dynamic_cast<Gtk::Label *>(m_apoLoadGameItem[i]->get_child());
1932       poLabel->set_text(csPrefix + sDateTime);
1933       m_apoLoadGameItem[i]->set_sensitive(! m_astGameSlot[i].m_bEmpty);
1934 
1935       poLabel = dynamic_cast<Gtk::Label *>(m_apoSaveGameItem[i]->get_child());
1936       poLabel->set_text(csPrefix + sDateTime);
1937       m_apoSaveGameItem[i]->set_sensitive();
1938     }
1939   }
1940 }
1941 
1942 } // VBA namespace
1943