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