]> Shamusworld >> Repos - virtualjaguar/blob - src/gui/mainwin.cpp
Hooked up CD BIOS loading/running.
[virtualjaguar] / src / gui / mainwin.cpp
1 //
2 // mainwin.cpp - Qt-based GUI for Virtual Jaguar: Main Application Window
3 // by James L. Hammons
4 // (C) 2009 Underground Software
5 //
6 // JLH = James L. Hammons <jlhamm@acm.org>
7 //
8 // Who  When        What
9 // ---  ----------  -------------------------------------------------------------
10 // JLH  12/23/2009  Created this file
11 // JLH  12/20/2010  Added settings, menus & toolbars
12 //
13
14 // FIXED:
15 //
16 //
17 // STILL TO BE DONE:
18 //
19 // - Autoscan/autoload all available BIOS from 'software' folder
20 // - Controller configuration
21 // - Remove SDL dependencies (sound, mainly) from Jaguar core lib
22 // - Add 1 key jumping in cartridge list (press 'R', jumps to carts starting with 'R', etc)
23 // - Add dbl click/enter to select in cart list, ESC to dimiss
24 //
25
26 /*
27 For BIOS autoscan, infrastructure is already there in filethread.cpp; just need to figure out
28 if we want to scan every time, or stuff filenames found into the config file, or what.
29 Should filethread emit signal that's intercepted here? Maybe...
30 */
31
32 // Uncomment this for debugging...
33 //#define DEBUG
34 //#define DEBUGFOO                      // Various tool debugging...
35 //#define DEBUGTP                               // Toolpalette debugging...
36
37 #include "mainwin.h"
38
39 #include "SDL.h"
40 #include "glwidget.h"
41 #include "about.h"
42 #include "settings.h"
43 #include "filepicker.h"
44 #include "configdialog.h"
45 #include "generaltab.h"
46 #include "version.h"
47
48 #include "jaguar.h"
49 #include "tom.h"
50 #include "log.h"
51 #include "file.h"
52 #include "joystick.h"
53
54 #ifdef __GCCWIN32__
55 // Apparently on win32, usleep() is not pulled in by the usual suspects.
56 #include <unistd.h>
57 #endif
58
59 // Uncomment this to use built-in BIOS/CD-ROM BIOS
60 // You'll need a copy of jagboot.h & jagcd.h for this to work...!
61 // Creating those is left as an exercise for the reader. ;-)
62 //#define USE_BUILT_IN_BIOS
63
64 #ifdef USE_BUILT_IN_BIOS
65 #include "jagboot.h"
66 #include "jagcd.h"
67 #endif
68
69 // The way BSNES controls things is by setting a timer with a zero
70 // timeout, sleeping if not emulating anything. Seems there has to be a
71 // better way.
72
73 // It has a novel approach to plugging-in/using different video/audio/input
74 // methods, can we do something similar or should we just use the built-in
75 // QOpenGL?
76
77 // We're going to try to use the built-in OpenGL support and see how it goes.
78 // We'll make the VJ core modular so that it doesn't matter what GUI is in
79 // use, we can drop it in anywhere and use it as-is.
80
81 MainWin::MainWin(): running(false), powerButtonOn(false), showUntunedTankCircuit(true),
82         cartridgeLoaded(false), CDActive(false)
83 {
84         videoWidget = new GLWidget(this);
85         setCentralWidget(videoWidget);
86         setWindowIcon(QIcon(":/res/vj-icon.png"));
87 //      setWindowTitle("Virtual Jaguar v2.0.0");
88         setWindowTitle("Virtual Jaguar " VJ_RELEASE_VERSION );
89
90         aboutWin = new AboutWindow(this);
91         filePickWin = new FilePickerWindow(this);
92
93     videoWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
94     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
95
96         setUnifiedTitleAndToolBarOnMac(true);
97
98         // Create actions
99
100         quitAppAct = new QAction(tr("E&xit"), this);
101         quitAppAct->setShortcuts(QKeySequence::Quit);
102         quitAppAct->setStatusTip(tr("Quit Virtual Jaguar"));
103         connect(quitAppAct, SIGNAL(triggered()), this, SLOT(close()));
104
105         powerAct = new QAction(QIcon(":/res/power.png"), tr("&Power"), this);
106         powerAct->setStatusTip(tr("Powers Jaguar on/off"));
107         powerAct->setCheckable(true);
108         powerAct->setChecked(false);
109         powerAct->setDisabled(true);
110         connect(powerAct, SIGNAL(triggered()), this, SLOT(TogglePowerState()));
111
112         pauseAct = new QAction(QIcon(":/res/pause.png"), tr("Pause"), this);
113         pauseAct->setStatusTip(tr("Toggles the running state"));
114         pauseAct->setCheckable(true);
115         pauseAct->setDisabled(true);
116         pauseAct->setShortcut(QKeySequence(tr("Esc")));
117         connect(pauseAct, SIGNAL(triggered()), this, SLOT(ToggleRunState()));
118
119         zoomActs = new QActionGroup(this);
120
121         x1Act = new QAction(QIcon(":/res/zoom100.png"), tr("Zoom 100%"), zoomActs);
122         x1Act->setStatusTip(tr("Set window zoom to 100%"));
123         x1Act->setCheckable(true);
124         connect(x1Act, SIGNAL(triggered()), this, SLOT(SetZoom100()));
125
126         x2Act = new QAction(QIcon(":/res/zoom200.png"), tr("Zoom 200%"), zoomActs);
127         x2Act->setStatusTip(tr("Set window zoom to 200%"));
128         x2Act->setCheckable(true);
129         connect(x2Act, SIGNAL(triggered()), this, SLOT(SetZoom200()));
130
131         x3Act = new QAction(QIcon(":/res/zoom300.png"), tr("Zoom 300%"), zoomActs);
132         x3Act->setStatusTip(tr("Set window zoom to 300%"));
133         x3Act->setCheckable(true);
134         connect(x3Act, SIGNAL(triggered()), this, SLOT(SetZoom300()));
135
136         tvTypeActs = new QActionGroup(this);
137
138         ntscAct = new QAction(QIcon(":/res/ntsc.png"), tr("NTSC"), tvTypeActs);
139         ntscAct->setStatusTip(tr("Sets Jaguar to NTSC mode"));
140         ntscAct->setCheckable(true);
141         connect(ntscAct, SIGNAL(triggered()), this, SLOT(SetNTSC()));
142
143         palAct = new QAction(QIcon(":/res/pal.png"), tr("PAL"), tvTypeActs);
144         palAct->setStatusTip(tr("Sets Jaguar to PAL mode"));
145         palAct->setCheckable(true);
146         connect(palAct, SIGNAL(triggered()), this, SLOT(SetPAL()));
147
148         blurAct = new QAction(QIcon(":/res/generic.png"), tr("Blur"), this);
149         blurAct->setStatusTip(tr("Sets OpenGL rendering to GL_NEAREST"));
150         blurAct->setCheckable(true);
151         connect(blurAct, SIGNAL(triggered()), this, SLOT(ToggleBlur()));
152
153         aboutAct = new QAction(QIcon(":/res/vj-icon.png"), tr("&About..."), this);
154         aboutAct->setStatusTip(tr("Blatant self-promotion"));
155         connect(aboutAct, SIGNAL(triggered()), this, SLOT(ShowAboutWin()));
156
157         filePickAct = new QAction(QIcon(":/res/software.png"), tr("&Insert Cartridge..."), this);
158         filePickAct->setStatusTip(tr("Insert a cartridge into Virtual Jaguar"));
159         filePickAct->setShortcut(QKeySequence(tr("Ctrl+i")));
160         connect(filePickAct, SIGNAL(triggered()), this, SLOT(InsertCart()));
161
162         configAct = new QAction(QIcon(":/res/generic.png"), tr("&Configure"), this);
163         configAct->setStatusTip(tr("Configure options for Virtual Jaguar"));
164         configAct->setShortcut(QKeySequence(tr("Ctrl+c")));
165         connect(configAct, SIGNAL(triggered()), this, SLOT(Configure()));
166
167         useCDAct = new QAction(QIcon(":/res/generic.png"), tr("&Use CD Unit"), this);
168         useCDAct->setStatusTip(tr("Use Jaguar Virtual CD unit"));
169 //      useCDAct->setShortcut(QKeySequence(tr("Ctrl+c")));
170         useCDAct->setCheckable(true);
171         connect(useCDAct, SIGNAL(triggered()), this, SLOT(ToggleCDUsage()));
172
173         // Misc. connections...
174         connect(filePickWin, SIGNAL(RequestLoad(QString)), this, SLOT(LoadSoftware(QString)));
175
176         // Create menus & toolbars
177
178         fileMenu = menuBar()->addMenu(tr("&File"));
179         fileMenu->addAction(filePickAct);
180         fileMenu->addAction(useCDAct);
181         fileMenu->addAction(powerAct);
182         fileMenu->addAction(pauseAct);
183         fileMenu->addAction(configAct);
184         fileMenu->addAction(quitAppAct);
185
186         helpMenu = menuBar()->addMenu(tr("&Help"));
187         helpMenu->addAction(aboutAct);
188
189         toolbar = addToolBar(tr("Stuff"));
190         toolbar->addAction(powerAct);
191         toolbar->addAction(pauseAct);
192         toolbar->addAction(filePickAct);
193         toolbar->addAction(useCDAct);
194         toolbar->addSeparator();
195         toolbar->addAction(x1Act);
196         toolbar->addAction(x2Act);
197         toolbar->addAction(x3Act);
198         toolbar->addSeparator();
199         toolbar->addAction(ntscAct);
200         toolbar->addAction(palAct);
201         toolbar->addSeparator();
202         toolbar->addAction(blurAct);
203
204         //      Create status bar
205         statusBar()->showMessage(tr("Ready"));
206
207         ReadSettings();
208
209         // Set toolbar buttons/menus based on settings read in (sync the UI)...
210         blurAct->setChecked(vjs.glFilter);
211         x1Act->setChecked(zoomLevel == 1);
212         x2Act->setChecked(zoomLevel == 2);
213         x3Act->setChecked(zoomLevel == 3);
214         running = powerAct->isChecked();
215         ntscAct->setChecked(vjs.hardwareTypeNTSC);
216         palAct->setChecked(!vjs.hardwareTypeNTSC);
217
218         // Do this in case original size isn't correct (mostly for the first-run case)
219         ResizeMainWindow();
220
221         // Set up timer based loop for animation...
222         timer = new QTimer(this);
223         connect(timer, SIGNAL(timeout()), this, SLOT(Timer()));
224         timer->start(20);
225
226         WriteLog("Virtual Jaguar %s (Last full build was on %s %s)\n", VJ_RELEASE_VERSION, __DATE__, __TIME__);
227         WriteLog("VJ: Initializing jaguar subsystem...\n");
228         JaguarInit();
229
230         // Get the BIOS ROM
231 #ifdef USE_BUILT_IN_BIOS
232         WriteLog("VJ: Using built in BIOS/CD BIOS...\n");
233         memcpy(jaguarBootROM, jagBootROM, 0x20000);
234         memcpy(jaguarCDBootROM, jagCDROM, 0x40000);
235         BIOSLoaded = CDBIOSLoaded = true;
236 #else
237 // What would be nice here would be a way to check if the BIOS was loaded so that we
238 // could disable the pushbutton on the Misc Options menu... !!! FIX !!! [DONE here, but needs to be fixed in GUI as well!]
239         WriteLog("VJ: About to attempt to load BIOSes...\n");
240 //This is short-circuiting the file finding thread... ??? WHY ???
241 //Not anymore. Was related to a QImage object creation/corruption bug elsewhere.
242         BIOSLoaded = (JaguarLoadROM(jaguarBootROM, vjs.jagBootPath) == 0x20000 ? true : false);
243         WriteLog("VJ: BIOS is %savailable...\n", (BIOSLoaded ? "" : "not "));
244         CDBIOSLoaded = (JaguarLoadROM(jaguarCDBootROM, vjs.CDBootPath) == 0x40000 ? true : false);
245         WriteLog("VJ: CD BIOS is %savailable...\n", (CDBIOSLoaded ? "" : "not "));
246 #endif
247
248         filePickWin->ScanSoftwareFolder(allowUnknownSoftware);
249 }
250
251 void MainWin::closeEvent(QCloseEvent * event)
252 {
253         WriteSettings();
254         event->accept(); // ignore() if can't close for some reason
255 }
256
257 void MainWin::keyPressEvent(QKeyEvent * e)
258 {
259         HandleKeys(e, true);
260 }
261
262 void MainWin::keyReleaseEvent(QKeyEvent * e)
263 {
264         HandleKeys(e, false);
265 }
266
267 void MainWin::HandleKeys(QKeyEvent * e, bool state)
268 {
269         // We kill bad key combos here, before they can get to the emulator...
270         // This also kills the illegal instruction problem that cropped up in Rayman!
271         // May want to do this by killing the old one instead of ignoring the new one...
272         // Seems to work better that way...
273 #if 0
274         if ((e->key() == vjs.p1KeyBindings[BUTTON_L] && joypad_0_buttons[BUTTON_R])
275                 || (e->key() == vjs.p1KeyBindings[BUTTON_R] && joypad_0_buttons[BUTTON_L])
276                 || (e->key() == vjs.p1KeyBindings[BUTTON_U] && joypad_0_buttons[BUTTON_D])
277                 || (e->key() == vjs.p1KeyBindings[BUTTON_D] && joypad_0_buttons[BUTTON_U]))
278                 return;
279 #else
280         if (e->key() == vjs.p1KeyBindings[BUTTON_L] && joypad_0_buttons[BUTTON_R])
281                 joypad_0_buttons[BUTTON_R] = 0;
282         if (e->key() == vjs.p1KeyBindings[BUTTON_R] && joypad_0_buttons[BUTTON_L])
283                 joypad_0_buttons[BUTTON_L] = 0;
284         if (e->key() == vjs.p1KeyBindings[BUTTON_U] && joypad_0_buttons[BUTTON_D])
285                 joypad_0_buttons[BUTTON_D] = 0;
286         if (e->key() == vjs.p1KeyBindings[BUTTON_D] && joypad_0_buttons[BUTTON_U])
287                 joypad_0_buttons[BUTTON_U] = 0;
288 #endif
289
290         // No bad combos exist, let's stuff the emulator key buffers...!
291         for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++)
292         {
293                 if (e->key() == vjs.p1KeyBindings[i])
294                         joypad_0_buttons[i] = (uint8)state;
295         }
296 }
297
298 void MainWin::Open(void)
299 {
300 }
301
302 void MainWin::Configure(void)
303 {
304         // Call the configuration dialog and update settings
305         ConfigDialog dlg(this);
306         //ick.
307         dlg.generalTab->useUnknownSoftware->setChecked(allowUnknownSoftware);
308
309         if (dlg.exec() == false)
310                 return;
311
312         QString before = vjs.ROMPath;
313         dlg.UpdateVJSettings();
314         QString after = vjs.ROMPath;
315
316         bool allowOld = allowUnknownSoftware;
317         //ick.
318         allowUnknownSoftware = dlg.generalTab->useUnknownSoftware->isChecked();
319
320         // We rescan the "software" folder if the user either changed the path or
321         // checked/unchecked the "Allow unknown files" option in the config dialog.
322         if ((before != after) || (allowOld != allowUnknownSoftware))
323                 filePickWin->ScanSoftwareFolder(allowUnknownSoftware);
324
325         // Just in case we crash before a clean exit...
326         WriteSettings();
327 }
328
329 //
330 // Here's the main emulator loop
331 //
332 void MainWin::Timer(void)
333 {
334         if (!running)
335                 return;
336
337         if (showUntunedTankCircuit)
338         {
339                 // Random hash & trash
340                 // We try to simulate an untuned tank circuit here... :-)
341                 for(uint32_t x=0; x<videoWidget->rasterWidth; x++)
342                 {
343                         for(uint32_t y=0; y<videoWidget->rasterHeight; y++)
344                         {
345                                 videoWidget->buffer[(y * videoWidget->textureWidth) + x] = (rand() & 0xFF) << 8 | (rand() & 0xFF) << 16 | (rand() & 0xFF) << 24;// | (rand() & 0xFF);//0x000000FF;
346         //                      buffer[(y * textureWidth) + x] = x*y;
347                         }
348                 }
349         }
350         else
351         {
352                 // Otherwise, run the Jaguar simulation
353                 JaguarExecuteNew();
354 //              memcpy(videoWidget->buffer, backbuffer, videoWidget->rasterHeight * videoWidget->rasterWidth);
355                 memcpy(videoWidget->buffer, backbuffer, videoWidget->rasterHeight * videoWidget->textureWidth * sizeof(uint32_t));
356 //              memcpy(surface->pixels, backbuffer, TOMGetVideoModeWidth() * TOMGetVideoModeHeight() * 4);
357         }
358
359         videoWidget->updateGL();
360 }
361
362 #if 0
363 Window * RunEmu(void)
364 {
365 //      extern uint32 * backbuffer;
366         uint32 * overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
367         memset(overlayPixels, 0x00, 640 * 480 * 4);                     // Clear out overlay...
368
369 //This is crappy... !!! FIX !!!
370 //      extern bool finished, showGUI;
371
372         sdlemuDisableOverlay();
373
374 //      uint32 nFrame = 0, nFrameskip = 0;
375         uint32 totalFrames = 0;
376         finished = false;
377         bool showMessage = true;
378         uint32 showMsgFrames = 120;
379         uint8 transparency = 0xFF;
380         // Pass a message to the "joystick" code to debounce the ESC key...
381         debounceRunKey = true;
382
383         uint32 cartType = 4;
384         if (jaguarRomSize == 0x200000)
385                 cartType = 0;
386         else if (jaguarRomSize == 0x400000)
387                 cartType = 1;
388         else if (jaguarMainRomCRC32 == 0x687068D5)
389                 cartType = 2;
390         else if (jaguarMainRomCRC32 == 0x55A0669C)
391                 cartType = 3;
392
393         const char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
394         uint32 elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
395
396         while (!finished)
397         {
398                 // Set up new backbuffer with new pixels and data
399                 JaguarExecuteNew();
400                 totalFrames++;
401 //WriteLog("Frame #%u...\n", totalFrames);
402 //extern bool doDSPDis;
403 //if (totalFrames == 373)
404 //      doDSPDis = true;
405
406 //Problem: Need to do this *only* when the state changes from visible to not...
407 //Also, need to clear out the GUI when not on (when showMessage is active...)
408 if (showGUI || showMessage)
409         sdlemuEnableOverlay();
410 else
411         sdlemuDisableOverlay();
412
413 //Add in a new function for clearing patches of screen (ClearOverlayRect)
414
415 // Also: Take frame rate into account when calculating fade time...
416
417                 // Some QnD GUI stuff here...
418                 if (showGUI)
419                 {
420                         FillScreenRectangle(overlayPixels, 8, 1*FONT_HEIGHT, 128, 4*FONT_HEIGHT, 0x00000000);
421                         extern uint32 gpu_pc, dsp_pc;
422                         DrawString(overlayPixels, 8, 1*FONT_HEIGHT, false, "GPU PC: %08X", gpu_pc);
423                         DrawString(overlayPixels, 8, 2*FONT_HEIGHT, false, "DSP PC: %08X", dsp_pc);
424                         DrawString(overlayPixels, 8, 4*FONT_HEIGHT, false, "%u FPS", framesPerSecond);
425                 }
426
427                 if (showMessage)
428                 {
429                         DrawString2(overlayPixels, 8, 24*FONT_HEIGHT, 0x007F63FF, transparency, "Running...");
430                         DrawString2(overlayPixels, 8, 26*FONT_HEIGHT, 0x001FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
431                         DrawString2(overlayPixels, 8, 27*FONT_HEIGHT, 0x001FFF3F, transparency, "CRC: %08X", jaguarMainRomCRC32);
432
433                         if (showMsgFrames == 0)
434                         {
435                                 transparency--;
436
437                                 if (transparency == 0)
438 {
439                                         showMessage = false;
440 /*extern bool doGPUDis;
441 doGPUDis = true;//*/
442 }
443
444                         }
445                         else
446                                 showMsgFrames--;
447                 }
448
449                 frameCount++;
450
451                 if (SDL_GetTicks() - elapsedTicks > 250)
452                         elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
453         }
454
455         // Save the background for the GUI...
456         // In this case, we squash the color to monochrome, then force it to blue + green...
457         for(uint32 i=0; i<TOMGetVideoModeWidth() * 256; i++)
458         {
459                 uint32 pixel = backbuffer[i];
460                 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
461                 pixel = ((r + g + b) / 3) & 0x00FF;
462                 backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
463         }
464
465         sdlemuEnableOverlay();
466
467         return NULL;
468 }
469 #endif
470
471 void MainWin::TogglePowerState(void)
472 {
473         powerButtonOn = !powerButtonOn;
474
475         if (!powerButtonOn)
476         {
477                 pauseAct->setChecked(false);
478                 pauseAct->setDisabled(true);
479                 showUntunedTankCircuit = true;
480                 running = true;
481         }
482         else
483         {
484                 if (!CDActive)
485                 {
486                         showUntunedTankCircuit = (cartridgeLoaded ? false : true);
487                         pauseAct->setChecked(false);
488                         pauseAct->setDisabled(!cartridgeLoaded);
489                 }
490                 else
491                 {
492                         showUntunedTankCircuit = false;
493                         pauseAct->setChecked(false);
494                         pauseAct->setDisabled(false);
495                         memcpy(jagMemSpace + 0x800000, jaguarCDBootROM, 0x40000);
496                 }
497
498 //(Err, what's so crappy about this? It seems to do what it's supposed to...)
499 //This is crappy!!! !!! FIX !!!
500 //Is this even needed any more? Hmm. Maybe. Dunno.
501 //Seems like it is... But then again, maybe not. Have to test it to see.
502                 WriteLog("GUI: Resetting Jaguar...\n");
503                 JaguarReset();
504                 running = true;
505         }
506 }
507
508 void MainWin::ToggleRunState(void)
509 {
510         running = !running;
511
512         if (!running)
513         {
514 #if 0
515                 for(uint32_t x=0; x<videoWidget->rasterWidth; x++)
516                         for(uint32_t y=0; y<videoWidget->rasterHeight; y++)
517                                 videoWidget->buffer[(y * videoWidget->textureWidth) + x] = 0x00000000;
518 #else
519 //              for(uint32_t i=0; i<TOMGetVideoModeWidth() * 256; i++)
520                 for(uint32_t i=0; i<videoWidget->textureWidth * 256; i++)
521                 {
522                         uint32_t pixel = backbuffer[i];
523 //                      uint8_t b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
524                         uint8_t r = (pixel >> 24) & 0xFF, g = (pixel >> 16) & 0xFF, b = (pixel >> 8) & 0xFF;
525                         pixel = ((r + g + b) / 3) & 0x00FF;
526 //                      backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
527                         backbuffer[i] = 0x000000FF | (pixel << 16) | (pixel << 8);
528                 }
529
530 //              memcpy(videoWidget->buffer, backbuffer, videoWidget->rasterHeight * videoWidget->rasterWidth);
531                 memcpy(videoWidget->buffer, backbuffer, videoWidget->rasterHeight * videoWidget->textureWidth * sizeof(uint32_t));
532 #endif
533
534                 videoWidget->updateGL();
535         }
536 }
537
538 void MainWin::SetZoom100(void)
539 {
540         zoomLevel = 1;
541         ResizeMainWindow();
542 }
543
544 void MainWin::SetZoom200(void)
545 {
546         zoomLevel = 2;
547         ResizeMainWindow();
548 }
549
550 void MainWin::SetZoom300(void)
551 {
552         zoomLevel = 3;
553         ResizeMainWindow();
554 }
555
556 void MainWin::SetNTSC(void)
557 {
558         vjs.hardwareTypeNTSC = true;
559         ResizeMainWindow();
560 }
561
562 void MainWin::SetPAL(void)
563 {
564         vjs.hardwareTypeNTSC = false;
565         ResizeMainWindow();
566 }
567
568 void MainWin::ToggleBlur(void)
569 {
570         vjs.glFilter = !vjs.glFilter;
571 }
572
573 void MainWin::ShowAboutWin(void)
574 {
575         aboutWin->show();
576 }
577
578 void MainWin::InsertCart(void)
579 {
580         filePickWin->show();
581 }
582
583 void MainWin::LoadSoftware(QString file)
584 {
585         running = false;                                                        //  Prevent bad things(TM) from happening...
586         SET32(jaguarMainRAM, 0, 0x00200000);            // Set top of stack...
587         cartridgeLoaded = (JaguarLoadFile(file.toAscii().data()) ? true : false);
588
589         powerAct->setDisabled(false);
590         powerAct->setChecked(true);
591         powerButtonOn = false;
592         TogglePowerState();
593
594 //      QString newTitle = QString("Virtual Jaguar v2.0.0 - Now playing: %1")
595         QString newTitle = QString("Virtual Jaguar " VJ_RELEASE_VERSION
596                 " - Now playing: %1")
597                 .arg(filePickWin->GetSelectedPrettyName());
598         setWindowTitle(newTitle);
599 }
600
601 void MainWin::ToggleCDUsage(void)
602 {
603         CDActive = !CDActive;
604
605         if (CDActive)
606         {
607                 powerAct->setDisabled(false);
608         }
609         else
610         {
611                 powerAct->setDisabled(true);
612         }
613 }
614
615 void MainWin::ResizeMainWindow(void)
616 {
617         videoWidget->setFixedSize(zoomLevel * 320, zoomLevel * (vjs.hardwareTypeNTSC ? 240 : 256));
618         show();
619
620         for(int i=0; i<2; i++)
621         {
622                 resize(0, 0);
623                 usleep(2000);
624                 QApplication::processEvents();
625         }
626 }
627
628 void MainWin::ReadSettings(void)
629 {
630         QSettings settings("Underground Software", "Virtual Jaguar");
631         QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
632         QSize size = settings.value("size", QSize(400, 400)).toSize();
633         resize(size);
634         move(pos);
635         pos = settings.value("cartLoadPos", QPoint(200, 200)).toPoint();
636         filePickWin->move(pos);
637
638         zoomLevel = settings.value("zoom", 1).toInt();
639         allowUnknownSoftware = settings.value("showUnknownSoftware", false).toBool();
640
641         vjs.useJoystick      = settings.value("useJoystick", false).toBool();
642         vjs.joyport          = settings.value("joyport", 0).toInt();
643         vjs.hardwareTypeNTSC = settings.value("hardwareTypeNTSC", true).toBool();
644         vjs.frameSkip        = settings.value("frameSkip", 0).toInt();
645         vjs.useJaguarBIOS    = settings.value("useJaguarBIOS", false).toBool();
646         vjs.DSPEnabled       = settings.value("DSPEnabled", false).toBool();
647         vjs.usePipelinedDSP  = settings.value("usePipelinedDSP", false).toBool();
648         vjs.fullscreen       = settings.value("fullscreen", false).toBool();
649         vjs.useOpenGL        = settings.value("useOpenGL", true).toBool();
650         vjs.glFilter         = settings.value("glFilterType", 0).toInt();
651         vjs.renderType       = settings.value("renderType", 0).toInt();
652         strcpy(vjs.jagBootPath, settings.value("JagBootROM", "./bios/[BIOS] Atari Jaguar (USA, Europe).zip").toString().toAscii().data());
653         strcpy(vjs.CDBootPath, settings.value("CDBootROM", "./bios/jagcd.rom").toString().toAscii().data());
654         strcpy(vjs.EEPROMPath, settings.value("EEPROMs", "./eeproms").toString().toAscii().data());
655         strcpy(vjs.ROMPath, settings.value("ROMs", "./software").toString().toAscii().data());
656 WriteLog("MainWin: Paths\n");
657 WriteLog("    jagBootPath = \"%s\"\n", vjs.jagBootPath);
658 WriteLog("    CDBootPath  = \"%s\"\n", vjs.CDBootPath);
659 WriteLog("    EEPROMPath  = \"%s\"\n", vjs.EEPROMPath);
660 WriteLog("    ROMPath     = \"%s\"\n", vjs.ROMPath);
661
662         // Keybindings in order of U, D, L, R, C, B, A, Op, Pa, 0-9, #, *
663         vjs.p1KeyBindings[BUTTON_U] = settings.value("p1k_up", Qt::Key_Up).toInt();
664         vjs.p1KeyBindings[BUTTON_D] = settings.value("p1k_down", Qt::Key_Down).toInt();
665         vjs.p1KeyBindings[BUTTON_L] = settings.value("p1k_left", Qt::Key_Left).toInt();
666         vjs.p1KeyBindings[BUTTON_R] = settings.value("p1k_right", Qt::Key_Right).toInt();
667         vjs.p1KeyBindings[BUTTON_C] = settings.value("p1k_c", Qt::Key_Z).toInt();
668         vjs.p1KeyBindings[BUTTON_B] = settings.value("p1k_b", Qt::Key_X).toInt();
669         vjs.p1KeyBindings[BUTTON_A] = settings.value("p1k_a", Qt::Key_C).toInt();
670         vjs.p1KeyBindings[BUTTON_OPTION] = settings.value("p1k_option", Qt::Key_Apostrophe).toInt();
671         vjs.p1KeyBindings[BUTTON_PAUSE] = settings.value("p1k_pause", Qt::Key_Return).toInt();
672         vjs.p1KeyBindings[BUTTON_0] = settings.value("p1k_0", Qt::Key_0).toInt();
673         vjs.p1KeyBindings[BUTTON_1] = settings.value("p1k_1", Qt::Key_1).toInt();
674         vjs.p1KeyBindings[BUTTON_2] = settings.value("p1k_2", Qt::Key_2).toInt();
675         vjs.p1KeyBindings[BUTTON_3] = settings.value("p1k_3", Qt::Key_3).toInt();
676         vjs.p1KeyBindings[BUTTON_4] = settings.value("p1k_4", Qt::Key_4).toInt();
677         vjs.p1KeyBindings[BUTTON_5] = settings.value("p1k_5", Qt::Key_5).toInt();
678         vjs.p1KeyBindings[BUTTON_6] = settings.value("p1k_6", Qt::Key_6).toInt();
679         vjs.p1KeyBindings[BUTTON_7] = settings.value("p1k_7", Qt::Key_7).toInt();
680         vjs.p1KeyBindings[BUTTON_8] = settings.value("p1k_8", Qt::Key_8).toInt();
681         vjs.p1KeyBindings[BUTTON_9] = settings.value("p1k_9", Qt::Key_9).toInt();
682         vjs.p1KeyBindings[BUTTON_d] = settings.value("p1k_pound", Qt::Key_Slash).toInt();
683         vjs.p1KeyBindings[BUTTON_s] = settings.value("p1k_star", Qt::Key_Asterisk).toInt();
684
685         vjs.p2KeyBindings[BUTTON_U] = settings.value("p2k_up", Qt::Key_Up).toInt();
686         vjs.p2KeyBindings[BUTTON_D] = settings.value("p2k_down", Qt::Key_Down).toInt();
687         vjs.p2KeyBindings[BUTTON_L] = settings.value("p2k_left", Qt::Key_Left).toInt();
688         vjs.p2KeyBindings[BUTTON_R] = settings.value("p2k_right", Qt::Key_Right).toInt();
689         vjs.p2KeyBindings[BUTTON_C] = settings.value("p2k_c", Qt::Key_Z).toInt();
690         vjs.p2KeyBindings[BUTTON_B] = settings.value("p2k_b", Qt::Key_X).toInt();
691         vjs.p2KeyBindings[BUTTON_A] = settings.value("p2k_a", Qt::Key_C).toInt();
692         vjs.p2KeyBindings[BUTTON_OPTION] = settings.value("p2k_option", Qt::Key_Apostrophe).toInt();
693         vjs.p2KeyBindings[BUTTON_PAUSE] = settings.value("p2k_pause", Qt::Key_Return).toInt();
694         vjs.p2KeyBindings[BUTTON_0] = settings.value("p2k_0", Qt::Key_0).toInt();
695         vjs.p2KeyBindings[BUTTON_1] = settings.value("p2k_1", Qt::Key_1).toInt();
696         vjs.p2KeyBindings[BUTTON_2] = settings.value("p2k_2", Qt::Key_2).toInt();
697         vjs.p2KeyBindings[BUTTON_3] = settings.value("p2k_3", Qt::Key_3).toInt();
698         vjs.p2KeyBindings[BUTTON_4] = settings.value("p2k_4", Qt::Key_4).toInt();
699         vjs.p2KeyBindings[BUTTON_5] = settings.value("p2k_5", Qt::Key_5).toInt();
700         vjs.p2KeyBindings[BUTTON_6] = settings.value("p2k_6", Qt::Key_6).toInt();
701         vjs.p2KeyBindings[BUTTON_7] = settings.value("p2k_7", Qt::Key_7).toInt();
702         vjs.p2KeyBindings[BUTTON_8] = settings.value("p2k_8", Qt::Key_8).toInt();
703         vjs.p2KeyBindings[BUTTON_9] = settings.value("p2k_9", Qt::Key_9).toInt();
704         vjs.p2KeyBindings[BUTTON_d] = settings.value("p2k_pound", Qt::Key_Slash).toInt();
705         vjs.p2KeyBindings[BUTTON_s] = settings.value("p2k_star", Qt::Key_Asterisk).toInt();
706 }
707
708 void MainWin::WriteSettings(void)
709 {
710         QSettings settings("Underground Software", "Virtual Jaguar");
711         settings.setValue("pos", pos());
712         settings.setValue("size", size());
713         settings.setValue("cartLoadPos", filePickWin->pos());
714
715         settings.setValue("zoom", zoomLevel);
716         settings.setValue("showUnknownSoftware", allowUnknownSoftware);
717
718         settings.setValue("useJoystick", vjs.useJoystick);
719         settings.setValue("joyport", vjs.joyport);
720         settings.setValue("hardwareTypeNTSC", vjs.hardwareTypeNTSC);
721         settings.setValue("frameSkip", vjs.frameSkip);
722         settings.setValue("useJaguarBIOS", vjs.useJaguarBIOS);
723         settings.setValue("DSPEnabled", vjs.DSPEnabled);
724         settings.setValue("usePipelinedDSP", vjs.usePipelinedDSP);
725         settings.setValue("fullscreen", vjs.fullscreen);
726         settings.setValue("useOpenGL", vjs.useOpenGL);
727         settings.setValue("glFilterType", vjs.glFilter);
728         settings.setValue("renderType", vjs.renderType);
729         settings.setValue("JagBootROM", vjs.jagBootPath);
730         settings.setValue("CDBootROM", vjs.CDBootPath);
731         settings.setValue("EEPROMs", vjs.EEPROMPath);
732         settings.setValue("ROMs", vjs.ROMPath);
733
734         settings.setValue("p1k_up", vjs.p1KeyBindings[BUTTON_U]);
735         settings.setValue("p1k_down", vjs.p1KeyBindings[BUTTON_D]);
736         settings.setValue("p1k_left", vjs.p1KeyBindings[BUTTON_L]);
737         settings.setValue("p1k_right", vjs.p1KeyBindings[BUTTON_R]);
738         settings.setValue("p1k_c", vjs.p1KeyBindings[BUTTON_C]);
739         settings.setValue("p1k_b", vjs.p1KeyBindings[BUTTON_B]);
740         settings.setValue("p1k_a", vjs.p1KeyBindings[BUTTON_A]);
741         settings.setValue("p1k_option", vjs.p1KeyBindings[BUTTON_OPTION]);
742         settings.setValue("p1k_pause", vjs.p1KeyBindings[BUTTON_PAUSE]);
743         settings.setValue("p1k_0", vjs.p1KeyBindings[BUTTON_0]);
744         settings.setValue("p1k_1", vjs.p1KeyBindings[BUTTON_1]);
745         settings.setValue("p1k_2", vjs.p1KeyBindings[BUTTON_2]);
746         settings.setValue("p1k_3", vjs.p1KeyBindings[BUTTON_3]);
747         settings.setValue("p1k_4", vjs.p1KeyBindings[BUTTON_4]);
748         settings.setValue("p1k_5", vjs.p1KeyBindings[BUTTON_5]);
749         settings.setValue("p1k_6", vjs.p1KeyBindings[BUTTON_6]);
750         settings.setValue("p1k_7", vjs.p1KeyBindings[BUTTON_7]);
751         settings.setValue("p1k_8", vjs.p1KeyBindings[BUTTON_8]);
752         settings.setValue("p1k_9", vjs.p1KeyBindings[BUTTON_9]);
753         settings.setValue("p1k_pound", vjs.p1KeyBindings[BUTTON_d]);
754         settings.setValue("p1k_star", vjs.p1KeyBindings[BUTTON_s]);
755
756         settings.setValue("p2k_up", vjs.p2KeyBindings[BUTTON_U]);
757         settings.setValue("p2k_down", vjs.p2KeyBindings[BUTTON_D]);
758         settings.setValue("p2k_left", vjs.p2KeyBindings[BUTTON_L]);
759         settings.setValue("p2k_right", vjs.p2KeyBindings[BUTTON_R]);
760         settings.setValue("p2k_c", vjs.p2KeyBindings[BUTTON_C]);
761         settings.setValue("p2k_b", vjs.p2KeyBindings[BUTTON_B]);
762         settings.setValue("p2k_a", vjs.p2KeyBindings[BUTTON_A]);
763         settings.setValue("p2k_option", vjs.p2KeyBindings[BUTTON_OPTION]);
764         settings.setValue("p2k_pause", vjs.p2KeyBindings[BUTTON_PAUSE]);
765         settings.setValue("p2k_0", vjs.p2KeyBindings[BUTTON_0]);
766         settings.setValue("p2k_1", vjs.p2KeyBindings[BUTTON_1]);
767         settings.setValue("p2k_2", vjs.p2KeyBindings[BUTTON_2]);
768         settings.setValue("p2k_3", vjs.p2KeyBindings[BUTTON_3]);
769         settings.setValue("p2k_4", vjs.p2KeyBindings[BUTTON_4]);
770         settings.setValue("p2k_5", vjs.p2KeyBindings[BUTTON_5]);
771         settings.setValue("p2k_6", vjs.p2KeyBindings[BUTTON_6]);
772         settings.setValue("p2k_7", vjs.p2KeyBindings[BUTTON_7]);
773         settings.setValue("p2k_8", vjs.p2KeyBindings[BUTTON_8]);
774         settings.setValue("p2k_9", vjs.p2KeyBindings[BUTTON_9]);
775         settings.setValue("p2k_pound", vjs.p2KeyBindings[BUTTON_d]);
776         settings.setValue("p2k_star", vjs.p2KeyBindings[BUTTON_s]);
777 }
778
779 // Here's how Byuu does it...
780 // I think I have it working now... :-)
781 #if 0
782 void Utility::resizeMainWindow()
783 {
784   unsigned region = config().video.context->region;
785   unsigned multiplier = config().video.context->multiplier;
786   unsigned width = 256 * multiplier;
787   unsigned height = (region == 0 ? 224 : 239) * multiplier;
788
789   if(config().video.context->correctAspectRatio)
790   {
791     if(region == 0)
792         {
793       width = (double)width * config().video.ntscAspectRatio + 0.5;  //NTSC adjust
794     }
795         else
796         {
797       width = (double)width * config().video.palAspectRatio  + 0.5;  //PAL adjust
798     }
799   }
800
801   if(config().video.isFullscreen == false)
802   {
803     //get effective desktop work area region (ignore Windows taskbar, OS X dock, etc.)
804     QRect deskRect = QApplication::desktop()->availableGeometry(mainWindow);
805
806     //ensure window size will not be larger than viewable desktop area
807     constrainSize(height, width, deskRect.height()); //- frameHeight);
808     constrainSize(width, height, deskRect.width());  //- frameWidth );
809
810     mainWindow->canvas->setFixedSize(width, height);
811     mainWindow->show();
812   }
813   else
814   {
815     for(unsigned i = 0; i < 2; i++)
816         {
817       unsigned iWidth = width, iHeight = height;
818
819       constrainSize(iHeight, iWidth, mainWindow->canvasContainer->size().height());
820       constrainSize(iWidth, iHeight, mainWindow->canvasContainer->size().width());
821
822       //center canvas onscreen; ensure it is not larger than viewable area
823       mainWindow->canvas->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
824       mainWindow->canvas->setFixedSize(iWidth, iHeight);
825       mainWindow->canvas->setMinimumSize(0, 0);
826
827       usleep(2000);
828       QApplication::processEvents();
829     }
830   }
831
832   //workaround for Qt/Xlib bug:
833   //if window resize occurs with cursor over it, Qt shows Qt::Size*DiagCursor;
834   //so force it to show Qt::ArrowCursor, as expected
835   mainWindow->setCursor(Qt::ArrowCursor);
836   mainWindow->canvasContainer->setCursor(Qt::ArrowCursor);
837   mainWindow->canvas->setCursor(Qt::ArrowCursor);
838
839   //workaround for DirectSound(?) bug:
840   //window resizing sometimes breaks audio sync, this call re-initializes it
841   updateAvSync();
842 }
843
844 void Utility::setScale(unsigned scale)
845 {
846   config().video.context->multiplier = scale;
847   resizeMainWindow();
848   mainWindow->shrink();
849   mainWindow->syncUi();
850 }
851
852 void QbWindow::shrink()
853 {
854   if(config().video.isFullscreen == false)
855   {
856     for(unsigned i = 0; i < 2; i++)
857         {
858       resize(0, 0);
859       usleep(2000);
860       QApplication::processEvents();
861     }
862   }
863 }
864 #endif