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