]> Shamusworld >> Repos - virtualjaguar/blob - src/gui.cpp
Fixed "in-game" overlay to display properly
[virtualjaguar] / src / gui.cpp
1 //
2 // GUI.CPP
3 //
4 // Graphical User Interface support
5 // by James L. Hammons
6 //
7
8 #include <stdarg.h>
9 #include <sys/types.h>                                                          // For MacOS <dirent.h> dependency
10 #include <dirent.h>
11 #include <SDL.h>
12 #include <string>
13 #include <vector>
14 #include <algorithm>
15 #include <ctype.h>                                                                      // For toupper()
16 #include "settings.h"
17 #include "tom.h"
18 #include "video.h"
19 #include "clock.h"
20 #include "font1.h"
21 #include "font14pt.h"                                                           // Also 15, 16, 17, 18
22 #include "guielements.h"
23 #include "crc32.h"
24 #include "zlib.h"
25 #include "unzip.h"
26 #include "sdlemu_opengl.h"
27 #include "gui.h"
28
29 using namespace std;                                                            // For STL stuff
30
31 // Private function prototypes
32
33 class Window;                                                                           // Forward declaration...
34
35 //void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap, uint8 * alpha = NULL);
36 void DrawTransparentBitmapDeprecated(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap);
37 void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap);
38 void DrawBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap);
39 void ClearScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h);
40 void DrawStringTrans(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 opacity, const char * text, ...);
41 void DrawStringOpaque(uint32 * screen, uint32 x, uint32 y, uint32 color1, uint32 color2, const char * text, ...);
42 void DrawString(uint32 * screen, uint32 x, uint32 y, bool invert, const char * text, ...);
43 void DrawString2(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 transparency, const char * text, ...);
44 Window * LoadROM(void);
45 Window * ResetJaguar(void);
46 Window * ResetJaguarCD(void);
47 Window * RunEmu(void);
48 Window * Quit(void);
49 Window * About(void);
50 Window * MiscOptions(void);
51
52 int gzfilelength(gzFile gd);
53
54 // External variables
55
56 extern uint8 * jaguar_mainRam;
57 extern uint8 * jaguar_mainRom;
58 extern uint8 * jaguar_bootRom;
59 extern uint8 * jaguar_CDBootROM;
60 extern bool BIOSLoaded;
61 extern bool CDBIOSLoaded;
62
63 // Local global variables
64
65 bool exitGUI = false;                                                           // GUI (emulator) done variable
66 int mouseX = 0, mouseY = 0;
67 uint32 background[1280 * 256];                                          // GUI background buffer
68
69 char separator[] = "--------------------------------------------------------";
70
71 //
72 // Case insensitive string compare function
73 // Taken straight out of Thinking In C++ by Bruce Eckel. Thanks Bruce!
74 //
75
76 int stringCmpi(const string &s1, const string &s2)
77 {
78         // Select the first element of each string:
79         string::const_iterator p1 = s1.begin(), p2 = s2.begin();
80
81         while (p1 != s1.end() && p2 != s2.end())                // Don\92t run past the end
82         {
83                 if (toupper(*p1) != toupper(*p2))                       // Compare upper-cased chars
84                         return (toupper(*p1) < toupper(*p2) ? -1 : 1);// Report which was lexically greater
85
86                 p1++;
87                 p2++;
88         }
89
90         // If they match up to the detected eos, say which was longer. Return 0 if the same.
91         return s2.size() - s1.size();
92 }
93
94 //
95 // Local GUI classes
96 //
97
98 enum { WINDOW_CLOSE, MENU_ITEM_CHOSEN };
99
100 class Element
101 {
102         public:
103                 Element(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0)
104                         { extents.x = x, extents.y = y, extents.w = w, extents.h = h; }
105                 virtual void HandleKey(SDLKey key) = 0;         // These are "pure" virtual functions...
106                 virtual void HandleMouseMove(uint32 x, uint32 y) = 0;
107                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) = 0;
108                 virtual void Draw(uint32, uint32) = 0;
109                 virtual void Notify(Element *) = 0;
110 //Needed?               virtual ~Element() = 0;
111 //We're not allocating anything in the base class, so the answer would be NO.
112                 bool Inside(uint32 x, uint32 y);
113                 // Class method
114 //              static void SetScreenAndPitch(int16 * s, uint32 p) { screenBuffer = s, pitch = p; }
115                 static void SetScreenAndPitch(uint32 * s, uint32 p) { screenBuffer = s, pitch = p; }
116
117         protected:
118                 SDL_Rect extents;
119                 uint32 state;
120                 // Class variables...
121 //              static int16 * screenBuffer;
122                 static uint32 * screenBuffer;
123                 static uint32 pitch;
124 };
125
126 // Initialize class variables (Element)
127 //int16 * Element::screenBuffer = NULL;
128 uint32 * Element::screenBuffer = NULL;
129 uint32 Element::pitch = 0;
130
131 bool Element::Inside(uint32 x, uint32 y)
132 {
133         return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
134                 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false);
135 }
136
137
138 //
139 // Button class
140 //
141
142 class Button: public Element
143 {
144         public:
145                 Button(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
146                         activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
147                         bgColor(0xFF00FF00), pic(NULL), elementToTell(NULL) {}
148                 Button(uint32 x, uint32 y, uint32 w, uint32 h, uint32 * p): Element(x, y, w, h),
149                         activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
150                         bgColor(0xFF00FF00), pic(p), elementToTell(NULL) {}
151 //              Button(uint32 x, uint32 y, uint32 * p): Element(x, y, 0, 0),
152                 Button(uint32 x, uint32 y, uint32 * p, uint32 * pH = NULL, uint32 * pD = NULL): Element(x, y, 0, 0),
153                         activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
154                         bgColor(0xFF00FF00), pic(p), picHover(pH), picDown(pD), elementToTell(NULL)
155                         { if (pic) extents.w = pic[0], extents.h = pic[1]; }
156                 Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
157                         activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
158                         bgColor(0xFF00FF00), pic(NULL), text(s), elementToTell(NULL) {}
159                 Button(uint32 x, uint32 y, string s): Element(x, y, 0, FONT_HEIGHT),
160                         activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
161                         bgColor(0xFF00FF00), pic(NULL), text(s), elementToTell(NULL)
162                         { extents.w = s.length() * FONT_WIDTH; }
163                 virtual void HandleKey(SDLKey key) {}
164                 virtual void HandleMouseMove(uint32 x, uint32 y);
165                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
166                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
167                 virtual void Notify(Element *) {}
168                 bool ButtonClicked(void) { return activated; }
169                 void SetNotificationElement(Element * e) { elementToTell = e; }
170
171         protected:
172                 bool activated, clicked, inside;
173                 uint32 fgColor, bgColor;
174                 uint32 * pic, * picHover, * picDown;
175                 string text;
176                 Element * elementToTell;
177 };
178
179 void Button::HandleMouseMove(uint32 x, uint32 y)
180 {
181         inside = Inside(x, y);
182 }
183
184 void Button::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
185 {
186         if (inside)
187         {
188                 if (mouseDown)
189                         clicked = true;
190
191                 if (clicked && !mouseDown)
192                 {
193                         clicked = false, activated = true;
194
195                         // Send a message that we're activated (if there's someone to tell, that is)
196                         if (elementToTell)
197                                 elementToTell->Notify(this);
198                 }
199         }
200         else
201                 clicked = activated = false;
202 }
203
204 void Button::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
205 {
206         uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
207
208         if (text.length() > 0)                                                  // Simple text button
209 //      if (pic == NULL)
210         {
211                 for(uint32 y=0; y<extents.h; y++)
212                 {
213                         for(uint32 x=0; x<extents.w; x++)
214                         {
215                                 // Doesn't clip in y axis! !!! FIX !!!
216                                 if (extents.x + x < pitch)
217                                         screenBuffer[addr + x + (y * pitch)] 
218 //                                      = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
219 //43F0 -> 010000 11111 10000 -> 0100 0001 1111 1111 1000 0100 -> 41 FF 84
220                                                 = (clicked && inside ? fgColor : (inside ? 0xFF84FF41 : bgColor));
221                         }
222                 }
223
224                 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
225         }
226         else                                                                                    // Graphical button
227         {
228                 uint32 * picToShow = pic;
229
230                 if (picHover != NULL && inside && !clicked)
231                         picToShow = picHover;
232
233                 if (picDown != NULL && inside && clicked)
234                         picToShow = picDown;
235
236                 DrawTransparentBitmapDeprecated(screenBuffer, extents.x + offsetX, extents.y + offsetY, picToShow);
237         }
238 }
239
240
241 //
242 // PushButton class
243 //
244
245 class PushButton: public Element
246 {
247 // How to handle?
248 // Save state externally?
249 //We pass in a state variable if we want to track it externally, otherwise we use our own
250 //internal state var. Still need to do some kind of callback for pushbuttons that do things
251 //like change from fullscreen to windowed... !!! FIX !!!
252
253         public:
254 //              PushButton(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
255 //                      activated(false), clicked(false), inside(false), fgColor(0xFFFF),
256 //                      bgColor(0x03E0), pic(NULL), elementToTell(NULL) {}
257 //              PushButton(uint32 x, uint32 y, bool * st, string s): Element(x, y, 8, 8), state(st),
258 //                      inside(false), text(s) { if (st == NULL) state = &internalState; }
259                 PushButton(uint32 x, uint32 y, bool * st, string s): Element(x, y, 16, 16), state(st),
260                         inside(false), text(s) { if (st == NULL) state = &internalState; }
261 /*              Button(uint32 x, uint32 y, uint32 w, uint32 h, uint32 * p): Element(x, y, w, h),
262                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
263                         bgColor(0x03E0), pic(p), elementToTell(NULL) {}
264                 Button(uint32 x, uint32 y, uint32 * p): Element(x, y, 0, 0),
265                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
266                         bgColor(0x03E0), pic(p), elementToTell(NULL)
267                         { if (pic) extents.w = pic[0], extents.h = pic[1]; }
268                 Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
269                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
270                         bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL) {}
271                 PushButton(uint32 x, uint32 y, string s): Element(x, y, 0, 8),
272                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
273                         bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL)
274                         { extents.w = s.length() * 8; }*/
275                 virtual void HandleKey(SDLKey key) {}
276                 virtual void HandleMouseMove(uint32 x, uint32 y);
277                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
278                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
279                 virtual void Notify(Element *) {}
280 //              bool ButtonClicked(void) { return activated; }
281 //              void SetNotificationElement(Element * e) { elementToTell = e; }
282
283         protected:
284                 bool * state;
285                 bool inside;
286 //              bool activated, clicked, inside;
287 //              uint16 fgColor, bgColor;
288 //              uint32 * pic;
289                 string text;
290 //              Element * elementToTell;
291                 bool internalState;
292 };
293
294 void PushButton::HandleMouseMove(uint32 x, uint32 y)
295 {
296         inside = Inside(x, y);
297 }
298
299 void PushButton::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
300 {
301         if (inside && mouseDown)
302         {
303 /*              if (mouseDown)
304                         clicked = true;
305
306                 if (clicked && !mouseDown)
307                 {
308                         clicked = false, activated = true;
309
310                         // Send a message that we're activated (if there's someone to tell, that is)
311                         if (elementToTell)
312                                 elementToTell->Notify(this);
313                 }*/
314                 *state = !(*state);
315         }
316 //      else
317 //              clicked = activated = false;
318 }
319
320 void PushButton::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
321 {
322 /*      uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
323
324         for(uint32 y=0; y<extents.h; y++)
325         {
326                 for(uint32 x=0; x<extents.w; x++)
327                 {
328                         // Doesn't clip in y axis! !!! FIX !!!
329                         if (extents.x + x < pitch)
330                                 screenBuffer[addr + x + (y * pitch)] 
331                                         = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
332                 }
333         }*/
334
335         if (*state)
336                 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, &pbDown);
337         else
338                 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, &pbUp);
339
340         if (text.length() > 0)
341                 DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY, false, "%s", text.c_str());
342 }
343
344
345 //
346 // SlideSwitch class
347 //
348
349 class SlideSwitch: public Element
350 {
351 // How to handle?
352 // Save state externally?
353 //Seems to be handled the same as PushButton, but without sanity checks. !!! FIX !!!
354
355         public:
356                 SlideSwitch(uint32 x, uint32 y, bool * st, string s1, string s2): Element(x, y, 16, 32), state(st),
357                         inside(false), text1(s1), text2(s2) {}
358                 virtual void HandleKey(SDLKey key) {}
359                 virtual void HandleMouseMove(uint32 x, uint32 y);
360                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
361                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
362                 virtual void Notify(Element *) {}
363 //              bool ButtonClicked(void) { return activated; }
364 //              void SetNotificationElement(Element * e) { elementToTell = e; }
365
366         protected:
367                 bool * state;
368                 bool inside;
369 //              bool activated, clicked, inside;
370 //              uint16 fgColor, bgColor;
371 //              uint32 * pic;
372                 string text1, text2;
373 //              Element * elementToTell;
374 };
375
376 void SlideSwitch::HandleMouseMove(uint32 x, uint32 y)
377 {
378         inside = Inside(x, y);
379 }
380
381 void SlideSwitch::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
382 {
383         if (inside && mouseDown)
384         {
385 /*              if (mouseDown)
386                         clicked = true;
387
388                 if (clicked && !mouseDown)
389                 {
390                         clicked = false, activated = true;
391
392                         // Send a message that we're activated (if there's someone to tell, that is)
393                         if (elementToTell)
394                                 elementToTell->Notify(this);
395                 }*/
396                 *state = !(*state);
397         }
398 //      else
399 //              clicked = activated = false;
400 }
401
402 void SlideSwitch::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
403 {
404         DrawTransparentBitmapDeprecated(screenBuffer, extents.x + offsetX, extents.y + offsetY, (*state ? slideSwitchDown : slideSwitchUp));
405
406         if (text1.length() > 0)
407                 DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY, false, "%s", text1.c_str());
408
409         if (text2.length() > 0)
410                 DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY + 16, false, "%s", text2.c_str());
411 }
412
413
414 //
415 // Window class
416 //
417
418 class Window: public Element
419 {
420         public:
421 /*              Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
422                         fgColor(0x4FF0), bgColor(0xFE10)
423                         { close = new Button(w - 8, 1, closeBox); list.push_back(close); }*/
424                 Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0,
425                         void (* f)(Element *) = NULL): Element(x, y, w, h),
426 //                      /*clicked(false), inside(false),*/ fgColor(0x4FF0), bgColor(0x1E10),
427 //4FF0 -> 010011 11111 10000 -> 0100 1101 1111 1111 1000 0100 -> 4D FF 84
428 //1E10 -> 000111 10000 10000 -> 0001 1111 1000 0100 1000 0100 -> 1F 84 84
429                         /*clicked(false), inside(false),*/ fgColor(0xFF84FF4D), bgColor(0xFF84841F),
430                         handler(f)
431                         { close = new Button(w - (CLOSEBOX_WIDTH + 1), 1, closeBox, closeBoxHover, closeBoxDown);
432                           list.push_back(close);
433                           close->SetNotificationElement(this); }
434                 virtual ~Window();
435                 virtual void HandleKey(SDLKey key);
436                 virtual void HandleMouseMove(uint32 x, uint32 y);
437                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
438                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
439                 virtual void Notify(Element * e);
440                 void AddElement(Element * e);
441 //              bool WindowActive(void) { return true; }//return !close->ButtonClicked(); }
442
443         protected:
444 //              bool clicked, inside;
445                 uint32 fgColor, bgColor;
446                 void (* handler)(Element *);
447                 Button * close;
448 //We have to use a list of Element *pointers* because we can't make a list that will hold
449 //all the different object types in the same list...
450                 vector<Element *> list;
451 };
452
453 Window::~Window()
454 {
455         for(uint32 i=0; i<list.size(); i++)
456                 if (list[i])
457                         delete list[i];
458 }
459
460 void Window::HandleKey(SDLKey key)
461 {
462         if (key == SDLK_ESCAPE)
463         {
464                 SDL_Event event;
465                 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
466                 SDL_PushEvent(&event);
467         }
468
469         // Handle the items this window contains...
470         for(uint32 i=0; i<list.size(); i++)
471                 // Make coords relative to upper right corner of this window...
472                 list[i]->HandleKey(key);
473 }
474
475 void Window::HandleMouseMove(uint32 x, uint32 y)
476 {
477         // Handle the items this window contains...
478         for(uint32 i=0; i<list.size(); i++)
479                 // Make coords relative to upper right corner of this window...
480                 list[i]->HandleMouseMove(x - extents.x, y - extents.y);
481 }
482
483 void Window::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
484 {
485         // Handle the items this window contains...
486         for(uint32 i=0; i<list.size(); i++)
487                 // Make coords relative to upper right corner of this window...
488                 list[i]->HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
489 }
490
491 void Window::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
492 {
493         uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
494
495         for(uint32 y=0; y<extents.h; y++)
496         {
497                 for(uint32 x=0; x<extents.w; x++)
498                 {
499                         // Doesn't clip in y axis! !!! FIX !!!
500                         if (extents.x + x < pitch)
501                                 screenBuffer[addr + x + (y * pitch)] = bgColor;
502                 }
503         }
504
505         // Handle the items this window contains...
506         for(uint32 i=0; i<list.size(); i++)
507                 list[i]->Draw(extents.x, extents.y);
508 }
509
510 void Window::AddElement(Element * e)
511 {
512         list.push_back(e);
513 }
514
515 void Window::Notify(Element * e)
516 {
517         if (e == close)
518         {
519                 SDL_Event event;
520                 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
521                 SDL_PushEvent(&event);
522         }
523 }
524
525
526 //
527 // Static text class
528 //
529
530 class Text: public Element
531 {
532         public:
533 //              Text(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
534 //                      fgColor(0x4FF0), bgColor(0xFE10) {}
535 //              Text(uint32 x, uint32 y, string s, uint16 fg = 0x4FF0, uint16 bg = 0xFE10): Element(x, y, 0, 0),
536 //                      fgColor(fg), bgColor(bg), text(s) {}
537 //4FF0 -> 010011 11111 10000 -> 0100 1101 1111 1111 1000 0100 -> 4D FF 84
538 //FE10 -> 111111 10000 10000 -> 1111 1111 1000 0100 1000 0100 -> FF 84 84
539                 Text(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
540                         fgColor(0xFF8484FF), bgColor(0xFF84FF4D) {}
541                 Text(uint32 x, uint32 y, string s, uint32 fg = 0xFF8484FF, uint32 bg = 0xFF84FF4D):
542                         Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s) {}
543                 virtual void HandleKey(SDLKey key) {}
544                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
545                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
546                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
547                 virtual void Notify(Element *) {}
548
549         protected:
550                 uint32 fgColor, bgColor;
551                 string text;
552 };
553
554 void Text::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
555 {
556         if (text.length() > 0)
557 //              DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
558                 DrawStringOpaque(screenBuffer, extents.x + offsetX, extents.y + offsetY, fgColor, bgColor, "%s", text.c_str());
559 }
560
561
562 //
563 // Static image class
564 //
565
566 class Image: public Element
567 {
568         public:
569                 Image(uint32 x, uint32 y, const void * img): Element(x, y, 0, 0), image(img) {}
570                 virtual void HandleKey(SDLKey key) {}
571                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
572                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
573                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
574                 virtual void Notify(Element *) {}
575
576         protected:
577                 uint32 fgColor, bgColor;
578                 const void * image;
579 };
580
581 void Image::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
582 {
583         if (image != NULL)
584                 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, image);
585 }
586
587
588 //
589 // TextEdit class
590 //
591
592 class TextEdit: public Element
593 {
594         public:
595                 TextEdit(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
596                         fgColor(0xFF8484FF), bgColor(0xFF84FF4D), text(""), caretPos(0) {}
597                 TextEdit(uint32 x, uint32 y, string s, uint32 fg = 0xFF8484FF, uint32 bg = 0xFF84FF4D):
598                         Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s), caretPos(0) {}
599                 virtual void HandleKey(SDLKey key);
600                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
601                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
602                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
603                 virtual void Notify(Element *) {}
604
605         protected:
606                 uint32 fgColor, bgColor;
607                 string text;
608                 uint32 caretPos;
609 };
610
611 //Set different filters depending on type passed in on construction, e.g., filename, amount, etc...?
612 void TextEdit::HandleKey(SDLKey key)
613 {
614         if ((key >= SDLK_a && key <= SDLK_z) || (key >= SDLK_0 && key <= SDLK_9) || key == SDLK_PERIOD
615                 || key == SDLK_SLASH)
616         {
617                 //Need to handle shift key as well...
618                 text[caretPos++] = key;
619         }
620         else if (key == SDLK_BACKSPACE)
621         {
622         }
623         else if (key == SDLK_DELETE)
624         {
625         }
626 //left, right arrow
627 }
628
629 void TextEdit::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
630 {
631         if (text.length() > 0)
632 //              DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
633                 DrawStringOpaque(screenBuffer, extents.x + offsetX, extents.y + offsetY, fgColor, bgColor, "%s", text.c_str());
634 }
635
636
637 //
638 // ListBox class
639 //
640
641 class ListBox: public Element
642 //class ListBox: public Window
643 {
644         public:
645 //              ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
646                 ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);//: Window(x, y, w, h),
647 //              windowPtr(0), cursor(0), limit(0), charWidth((w / 8) - 1), charHeight(h / 8),
648 //              elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
649 //              downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox) {}
650                 virtual void HandleKey(SDLKey key);
651                 virtual void HandleMouseMove(uint32 x, uint32 y);
652                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
653                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
654                 virtual void Notify(Element * e);
655                 void SetNotificationElement(Element * e) { elementToTell = e; }
656                 void AddItem(string s);
657                 string GetSelectedItem(void);
658
659         protected:
660                 bool thumbClicked;
661                 uint32 windowPtr, cursor, limit;
662                 uint32 charWidth, charHeight;                           // Box width/height in characters
663                 Element * elementToTell;
664                 Button upArrow, downArrow, upArrow2;
665                 vector<string> item;
666
667         private:
668                 uint32 yRelativePoint;
669 };
670
671 ListBox::ListBox(uint32 x, uint32 y, uint32 w, uint32 h): Element(x, y, w, h),
672         thumbClicked(false), windowPtr(0), cursor(0), limit(0), charWidth((w / FONT_WIDTH) - 1),
673         charHeight(h / FONT_HEIGHT), elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
674         downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox)
675 {
676         upArrow.SetNotificationElement(this);
677         downArrow.SetNotificationElement(this);
678         upArrow2.SetNotificationElement(this);
679         extents.w -= 8;                                                                 // Make room for scrollbar...
680 }
681
682 void ListBox::HandleKey(SDLKey key)
683 {
684         if (key == SDLK_DOWN)
685         {
686                 if (cursor != limit - 1)        // Cursor is within its window
687                         cursor++;
688                 else                                            // Otherwise, scroll the window...
689                 {
690                         if (cursor + windowPtr != item.size() - 1)
691                                 windowPtr++;
692                 }
693         }
694         else if (key == SDLK_UP)
695         {
696                 if (cursor != 0)
697                         cursor--;
698                 else
699                 {
700                         if (windowPtr != 0)
701                                 windowPtr--;
702                 }
703         }
704         else if (key == SDLK_PAGEDOWN)
705         {
706                 if (cursor != limit - 1)
707                         cursor = limit - 1;
708                 else
709                 {
710                         windowPtr += limit;
711                         if (windowPtr > item.size() - limit)
712                                 windowPtr = item.size() - limit;
713                 }
714         }
715         else if (key == SDLK_PAGEUP)
716         {
717                 if (cursor != 0)
718                         cursor = 0;
719                 else
720                 {
721                         if (windowPtr < limit)
722                                 windowPtr = 0;
723                         else
724                                 windowPtr -= limit;
725                 }
726         }
727         else if (key >= SDLK_a && key <= SDLK_z)
728         {
729                 // Advance cursor to filename with first letter pressed...
730                 uint8 which = (key - SDLK_a) + 65;      // Convert key to A-Z char
731
732                 for(uint32 i=0; i<item.size(); i++)
733                 {
734                         if ((item[i][0] & 0xDF) == which)
735                         {
736                                 cursor = i - windowPtr;
737                                 if (i > windowPtr + limit - 1)
738                                         windowPtr = i - limit + 1, cursor = limit - 1;
739                                 if (i < windowPtr)
740                                         windowPtr = i, cursor = 0;
741                                 break;
742                         }
743                 }
744         }
745 }
746
747 void ListBox::HandleMouseMove(uint32 x, uint32 y)
748 {
749         upArrow.HandleMouseMove(x - extents.x, y - extents.y);
750         downArrow.HandleMouseMove(x - extents.x, y - extents.y);
751         upArrow2.HandleMouseMove(x - extents.x, y - extents.y);
752
753         if (thumbClicked)
754         {
755                 uint32 sbHeight = extents.h - 24,
756                         thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight);
757
758 //yRelativePoint is the spot on the thumb where we clicked...
759                 int32 newThumbStart = y - yRelativePoint;
760
761                 if (newThumbStart < 0)
762                         newThumbStart = 0;
763
764                 if ((uint32)newThumbStart > sbHeight - thumb)
765                         newThumbStart = sbHeight - thumb;
766
767                 windowPtr = (uint32)(((float)newThumbStart / (float)sbHeight) * (float)item.size());
768 //Check for cursor bounds as well... Or do we need to???
769 //Actually, we don't...!
770         }
771 }
772
773 void ListBox::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
774 {
775         if (Inside(x, y) && mouseDown)
776         {
777                 // Why do we have to do this??? (- extents.y?)
778                 // I guess it's because only the Window class has offsetting implemented... !!! FIX !!!
779 //              cursor = (y - extents.y) / 8;
780                 cursor = (y - extents.y) / FONT_HEIGHT;
781         }
782
783         // Check for a hit on the scrollbar...
784         if (x > (uint32)(extents.x + extents.w) && x <= (uint32)(extents.x + extents.w + 8)
785                 && y > (uint32)(extents.y + 8) && y <= (uint32)(extents.y + extents.h - 16))
786         {
787                 if (mouseDown)
788                 {
789 // This shiaut should be calculated in AddItem(), not here... (or in Draw() for that matter)
790                         uint32 sbHeight = extents.h - 24,
791                                 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
792                                 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
793
794                         // Did we hit the thumb?
795                         if (y >= (extents.y + 8 + thumbStart) && y < (extents.y + 8 + thumbStart + thumb))
796                                 thumbClicked = true, yRelativePoint = y - thumbStart;
797                 }
798 //Seems that this is useless--never reached except in rare cases and that the code outside is
799 //more effective...
800 //              else
801 //                      thumbClicked = false;
802         }
803
804         if (!mouseDown)
805                 thumbClicked = false;
806
807         upArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
808         downArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
809         upArrow2.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
810 }
811
812 void ListBox::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
813 {
814         for(uint32 i=0; i<limit; i++)
815         {
816                 // Strip off the extension
817                 // (extension stripping should be an option, not default!)
818                 string s(item[windowPtr + i], 0, item[windowPtr + i].length() - 4);
819 //              DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*8,
820                 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*FONT_HEIGHT,
821                         (cursor == i ? true : false), "%-*.*s", charWidth, charWidth, s.c_str());
822         }
823
824         upArrow.Draw(extents.x + offsetX, extents.y + offsetY);
825         downArrow.Draw(extents.x + offsetX, extents.y + offsetY);
826         upArrow2.Draw(extents.x + offsetX, extents.y + offsetY);
827
828         uint32 sbHeight = extents.h - 24,
829                 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
830                 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
831
832         for(uint32 y=extents.y+offsetY+8; y<extents.y+offsetY+extents.h-16; y++)
833         {
834 //              for(uint32 x=extents.x+offsetX+extents.w-8; x<extents.x+offsetX+extents.w; x++)
835                 for(uint32 x=extents.x+offsetX+extents.w; x<extents.x+offsetX+extents.w+8; x++)
836                 {
837                         if (y >= thumbStart + (extents.y+offsetY+8) && y < thumbStart + thumb + (extents.y+offsetY+8))
838 //                              screenBuffer[x + (y * pitch)] = (thumbClicked ? 0x458E : 0xFFFF);
839 //458E -> 01 0001  0 1100  0 1110 -> 0100 0101  0110 0011  0111 0011 -> 45 63 73
840                                 screenBuffer[x + (y * pitch)] = (thumbClicked ? 0xFF736345 : 0xFFFFFFFF);
841                         else
842 //                              screenBuffer[x + (y * pitch)] = 0x0200;
843 //0200 -> 000000 10000 00000 -> 00 1000 0100 00
844                                 screenBuffer[x + (y * pitch)] = 0xFF008400;
845                 }
846         }
847 }
848
849 void ListBox::Notify(Element * e)
850 {
851         if (e == &upArrow || e == &upArrow2)
852         {
853                 if (windowPtr != 0)
854                 {
855                         windowPtr--;
856
857                         if (cursor < limit - 1)
858                                 cursor++;
859                 }
860         }
861         else if (e == &downArrow)
862         {
863                 if (windowPtr < item.size() - limit)
864                 {
865                         windowPtr++;
866
867                         if (cursor != 0)
868                                 cursor--;
869                 }
870         }
871 }
872
873 void ListBox::AddItem(string s)
874 {
875         // Do a simple insertion sort
876         bool inserted = false;
877
878         for(vector<string>::iterator i=item.begin(); i<item.end(); i++)
879         {
880                 if (stringCmpi(s, *i) == -1)
881                 {
882                         item.insert(i, s);
883                         inserted = true;
884                         break;
885                 }
886         }
887
888         if (!inserted)
889                 item.push_back(s);
890
891         limit = (item.size() > charHeight ? charHeight : item.size());
892 }
893
894 string ListBox::GetSelectedItem(void)
895 {
896         return item[windowPtr + cursor];
897 }
898
899
900 //
901 // FileList class
902 //
903
904 class FileList: public Window
905 {
906         public:
907                 FileList(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);
908                 virtual ~FileList() {}
909                 virtual void HandleKey(SDLKey key);
910                 virtual void HandleMouseMove(uint32 x, uint32 y) { Window::HandleMouseMove(x, y); }
911                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) { Window::HandleMouseButton(x, y, mouseDown); }
912                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) { Window::Draw(offsetX, offsetY); }
913                 virtual void Notify(Element * e);
914
915         protected:
916                 ListBox * files;
917                 Button * load;
918 };
919
920 //Need 4 buttons, one scrollbar...
921 FileList::FileList(uint32 x, uint32 y, uint32 w, uint32 h): Window(x, y, w, h)
922 {
923         files = new ListBox(8, 8, w - 16, h - 32);
924         AddElement(files);
925         load = new Button(8, h - 16, " Load ");
926         AddElement(load);
927         load->SetNotificationElement(this);
928
929 //!!! FIX !!! Directory might not exist--this shouldn't cause VJ to crash!
930         DIR * dp = opendir(vjs.ROMPath);
931         dirent * de;
932
933         if (dp != NULL)
934         {
935                 while ((de = readdir(dp)) != NULL)
936                 {
937                         char * ext = strrchr(de->d_name, '.');
938         
939                         if (ext != NULL)
940                                 if (strcasecmp(ext, ".zip") == 0 || strcasecmp(ext, ".j64") == 0
941                                         || strcasecmp(ext, ".abs") == 0 || strcasecmp(ext, ".jag") == 0
942                                         || strcasecmp(ext, ".rom") == 0)
943                                         files->AddItem(string(de->d_name));
944                 }
945         
946                 closedir(dp);
947         }
948         else
949         {
950 //Give a diagnostic message here so that the (l)user can figure out what went wrong. !!! FIX !!!
951         }
952 }
953
954 void FileList::HandleKey(SDLKey key)
955 {
956         if (key == SDLK_RETURN)
957                 Notify(load);
958         else
959                 Window::HandleKey(key);
960 }
961
962 void FileList::Notify(Element * e)
963 {
964         if (e == load)
965         {
966                 char filename[MAX_PATH];
967                 strcpy(filename, vjs.ROMPath);
968
969                 if (strlen(filename) > 0)
970                         if (filename[strlen(filename) - 1] != '/')
971                                 strcat(filename, "/");
972
973                 strcat(filename, files->GetSelectedItem().c_str());
974
975 //              uint32 romSize = JaguarLoadROM(jaguar_mainRom, filename);
976 //              JaguarLoadCart(jaguar_mainRom, filename);
977                 if (JaguarLoadFile(filename))
978                 {
979                         SDL_Event event;
980                         event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
981                         SDL_PushEvent(&event);
982
983                         event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
984                         event.user.data1 = (void *)ResetJaguar;
985                 SDL_PushEvent(&event);
986                 }
987                 else
988                 {
989                         SDL_Event event;
990                         event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
991                         SDL_PushEvent(&event);
992
993                         // Handle the error, but don't run...
994                         // Tell the user that we couldn't run their file for some reason... !!! FIX !!!
995 //how to kludge: Make a function like ResetJaguar which creates the dialog window
996                 }
997         }
998         else
999                 Window::Notify(e);
1000 }
1001
1002
1003 //
1004 // Menu class & supporting structs/classes
1005 //
1006
1007 struct NameAction
1008 {
1009         string name;
1010         Window * (* action)(void);
1011         SDLKey hotKey;
1012
1013         NameAction(string n, Window * (* a)(void) = NULL, SDLKey k = SDLK_UNKNOWN): name(n),
1014                 action(a), hotKey(k) {}
1015 };
1016
1017 class MenuItems
1018 {
1019         public:
1020                 MenuItems(): charLength(0) {}
1021                 bool Inside(uint32 x, uint32 y)
1022                 { return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
1023                 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false); }
1024
1025                 string title;
1026                 vector<NameAction> item;
1027                 uint32 charLength;
1028                 SDL_Rect extents;
1029 };
1030
1031 class Menu: public Element
1032 {
1033         public:
1034 // 1CFF -> 0 001 11 00  111 1 1111 
1035 // 421F -> 0 100 00 10  000 1 1111
1036                 Menu(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = FONT_HEIGHT,
1037 /*                      uint16 fgc = 0x1CFF, uint16 bgc = 0x000F, uint16 fgch = 0x421F,
1038                         uint16 bgch = 0x1CFF): Element(x, y, w, h), activated(false), clicked(false),*/
1039 /*                      uint32 fgc = 0xFF3F3F00, uint32 bgc = 0x7F000000, uint32 fgch = 0xFF878700,
1040                         uint32 bgch = 0xFF3F3F00): Element(x, y, w, h), activated(false), clicked(false),*/
1041 /*                      uint32 fgc = 0xFFFF3F3F, uint32 bgc = 0xFF7F0000, uint32 fgch = 0xFFFF8787,
1042                         uint32 bgch = 0xFFFF3F3F): Element(x, y, w, h), activated(false), clicked(false),*/
1043                         uint32 fgc = 0xFF7F0000, uint32 bgc = 0xFFFF3F3F, uint32 fgch = 0xFFFF3F3F,
1044                         uint32 bgch = 0xFFFF8787): Element(x, y, w, h), activated(false), clicked(false),
1045                         inside(0), insidePopup(0), fgColor(fgc), bgColor(bgc), fgColorHL(fgch),
1046                         bgColorHL(bgch), menuChosen(-1), menuItemChosen(-1) {}
1047                 virtual void HandleKey(SDLKey key);
1048                 virtual void HandleMouseMove(uint32 x, uint32 y);
1049                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
1050                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
1051                 virtual void Notify(Element *) {}
1052                 void Add(MenuItems mi);
1053
1054         protected:
1055                 bool activated, clicked;
1056                 uint32 inside, insidePopup;
1057 //              uint16 fgColor, bgColor, fgColorHL, bgColorHL;
1058                 uint32 fgColor, bgColor, fgColorHL, bgColorHL;
1059                 int menuChosen, menuItemChosen;
1060
1061         private:
1062                 vector<MenuItems> itemList;
1063 };
1064
1065 void Menu::HandleKey(SDLKey key)
1066 {
1067         for(uint32 i=0; i<itemList.size(); i++)
1068         {
1069                 for(uint32 j=0; j<itemList[i].item.size(); j++)
1070                 {
1071                         if (itemList[i].item[j].hotKey == key)
1072                         {
1073                                 SDL_Event event;
1074                                 event.type = SDL_USEREVENT;
1075                                 event.user.code = MENU_ITEM_CHOSEN;
1076                                 event.user.data1 = (void *)itemList[i].item[j].action;
1077                         SDL_PushEvent(&event);
1078
1079                                 clicked = false, menuChosen = menuItemChosen = -1;
1080                                 break;
1081                         }
1082                 }
1083         }
1084 }
1085
1086 void Menu::HandleMouseMove(uint32 x, uint32 y)
1087 {
1088         inside = insidePopup = 0;
1089
1090         if (Inside(x, y))
1091         {
1092                 // Find out *where* we are inside the menu bar
1093                 uint32 xpos = extents.x;
1094
1095                 for(uint32 i=0; i<itemList.size(); i++)
1096                 {
1097                         uint32 width = (itemList[i].title.length() + 2) * FONT_WIDTH;
1098
1099                         if (x >= xpos && x < xpos + width)
1100                         {
1101                                 inside = i + 1;
1102                                 menuChosen = i;
1103                                 break;
1104                         }
1105
1106                         xpos += width;
1107                 }
1108         }
1109
1110         if (!Inside(x, y) && !clicked)
1111         {
1112                 menuChosen = -1;
1113         }
1114
1115         if (itemList[menuChosen].Inside(x, y) && clicked)
1116         {
1117                 insidePopup = ((y - itemList[menuChosen].extents.y) / FONT_HEIGHT) + 1;
1118                 menuItemChosen = insidePopup - 1;
1119         }
1120 }
1121
1122 void Menu::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
1123 {
1124         if (!clicked)
1125         {
1126                 if (mouseDown)
1127                 {
1128                         if (inside)
1129                                 clicked = true;
1130                         else
1131                                 menuChosen = -1;                                        // clicked is already false...!
1132                 }
1133         }
1134         else                                                                                    // clicked == true
1135         {
1136                 if (insidePopup && !mouseDown)                          // I.e., mouse-button-up
1137                 {
1138                         activated = true;
1139                         if (itemList[menuChosen].item[menuItemChosen].action != NULL)
1140                         {
1141 //                              itemList[menuChosen].item[menuItemChosen].action();
1142                                 SDL_Event event;
1143                                 event.type = SDL_USEREVENT;
1144                                 event.user.code = MENU_ITEM_CHOSEN;
1145                                 event.user.data1 = (void *)itemList[menuChosen].item[menuItemChosen].action;
1146                             SDL_PushEvent(&event);
1147
1148                                 clicked = false, menuChosen = menuItemChosen = -1;
1149
1150 /*                              SDL_Event event;
1151                                 while (SDL_PollEvent(&event));          // Flush the event queue...
1152                                 event.type = SDL_MOUSEMOTION;
1153                                 int mx, my;
1154                                 SDL_GetMouseState(&mx, &my);
1155                                 event.motion.x = mx, event.motion.y = my;
1156                             SDL_PushEvent(&event);                              // & update mouse position...!
1157 */                      }
1158                 }
1159
1160                 if (!inside && !insidePopup && mouseDown)
1161                         clicked = false, menuChosen = menuItemChosen = -1;
1162         }
1163 }
1164
1165 void Menu::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
1166 {
1167         uint32 xpos = extents.x + offsetX;
1168
1169         for(uint32 i=0; i<itemList.size(); i++)
1170         {
1171 //              uint16 color1 = fgColor, color2 = bgColor;
1172                 uint32 color1 = fgColor, color2 = bgColor;
1173                 if (inside == (i + 1) || (menuChosen != -1 && (uint32)menuChosen == i))
1174                         color1 = fgColorHL, color2 = bgColorHL;
1175
1176                 DrawStringOpaque(screenBuffer, xpos, extents.y + offsetY, color1, color2,
1177                         " %s ", itemList[i].title.c_str());
1178                 xpos += (itemList[i].title.length() + 2) * FONT_WIDTH;
1179         }
1180
1181         // Draw sub menu (but only if active)
1182         if (clicked)
1183         {
1184                 uint32 ypos = extents.y + FONT_HEIGHT + 1;
1185
1186                 for(uint32 i=0; i<itemList[menuChosen].item.size(); i++)
1187                 {
1188 //                      uint16 color1 = fgColor, color2 = bgColor;
1189                         uint32 color1 = fgColor, color2 = bgColor;
1190
1191                         if (insidePopup == i + 1)
1192                                 color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
1193
1194                         if (itemList[menuChosen].item[i].name.length() > 0)
1195                                 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
1196                                         color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
1197                                         itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
1198                         else
1199                                 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
1200                                         fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
1201
1202                         ypos += FONT_HEIGHT;
1203                 }
1204         }
1205 }
1206
1207 void Menu::Add(MenuItems mi)
1208 {
1209         for(uint32 i=0; i<mi.item.size(); i++)
1210                 if (mi.item[i].name.length() > mi.charLength)
1211                         mi.charLength = mi.item[i].name.length();
1212
1213         // Set extents here as well...
1214         mi.extents.x = extents.x + extents.w, mi.extents.y = extents.y + FONT_HEIGHT + 1;
1215         mi.extents.w = (mi.charLength + 2) * FONT_WIDTH, mi.extents.h = mi.item.size() * FONT_HEIGHT;
1216
1217         itemList.push_back(mi);
1218         extents.w += (mi.title.length() + 2) * FONT_WIDTH;
1219 }
1220
1221
1222 //Do we even *need* this?
1223 //Doesn't seem like it...
1224 /*class RootWindow: public Window
1225 {
1226         public:
1227                 RootWindow(Menu * m, Window * w = NULL): menu(m), window(w) {}
1228 //Do we even need to care about this crap?
1229 //                      { extents.x = extents.y = 0, extents.w = 320, extents.h = 240; }
1230                 virtual void HandleKey(SDLKey key) {}
1231                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
1232                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
1233                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) {}
1234                 virtual void Notify(Element *) {}
1235
1236         private:
1237                 Menu * menu;
1238                 Window * window;
1239                 int16 * rootImage[1280 * 240 * 2];
1240 };//*/
1241
1242
1243 //
1244 // Draw text at the given x/y coordinates. Can invert text as well.
1245 //
1246 void DrawString(uint32 * screen, uint32 x, uint32 y, bool invert, const char * text, ...)
1247 {
1248         char string[4096];
1249         va_list arg;
1250
1251         va_start(arg, text);
1252         vsprintf(string, text, arg);
1253         va_end(arg);
1254
1255         uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1256         uint32 length = strlen(string), address = x + (y * pitch);
1257
1258         uint32 color1 = 0x0080FF;
1259         uint8 nBlue = (color1 >> 16) & 0xFF, nGreen = (color1 >> 8) & 0xFF, nRed = color1 & 0xFF;
1260         uint8 xorMask = (invert ? 0xFF : 0x00);
1261
1262         for(uint32 i=0; i<length; i++)
1263         {
1264                 uint8 c = string[i];
1265                 uint32 fontAddr = (uint32)(c < 32 ? 0 : c - 32) * FONT_WIDTH * FONT_HEIGHT;
1266
1267                 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
1268                 {
1269                         for(uint32 xx=0; xx<FONT_WIDTH; xx++)
1270                         {
1271                                 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1272
1273                                 uint8 eBlue = (existingColor >> 16) & 0xFF,
1274                                         eGreen = (existingColor >> 8) & 0xFF,
1275                                         eRed = existingColor & 0xFF;
1276
1277                                 uint8 trans = font2[fontAddr] ^ xorMask;
1278                                 uint8 invTrans = trans ^ 0xFF;
1279
1280                                 uint32 bRed = (eRed * invTrans + nRed * trans) / 255,
1281                                         bGreen = (eGreen * invTrans + nGreen * trans) / 255,
1282                                         bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1283
1284                                 *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
1285                                 fontAddr++;
1286                         }
1287                 }
1288
1289                 address += FONT_WIDTH;
1290         }
1291 }
1292
1293 //
1294 // Draw text at the given x/y coordinates, using FG/BG colors.
1295 //
1296 void DrawStringOpaque(uint32 * screen, uint32 x, uint32 y, uint32 color1, uint32 color2, const char * text, ...)
1297 {
1298         char string[4096];
1299         va_list arg;
1300
1301         va_start(arg, text);
1302         vsprintf(string, text, arg);
1303         va_end(arg);
1304
1305         uint32 pitch = sdlemuGetOverlayWidthInPixels();
1306         uint32 length = strlen(string), address = x + (y * pitch);
1307
1308         uint8 eBlue = (color2 >> 16) & 0xFF, eGreen = (color2 >> 8) & 0xFF, eRed = color2 & 0xFF,
1309                 nBlue = (color1 >> 16) & 0xFF, nGreen = (color1 >> 8) & 0xFF, nRed = color1 & 0xFF;
1310
1311         for(uint32 i=0; i<length; i++)
1312         {
1313                 uint8 c = string[i];
1314                 c = (c < 32 ? 0 : c - 32);
1315                 uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;
1316
1317                 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
1318                 {
1319                         for(uint32 xx=0; xx<FONT_WIDTH; xx++)
1320                         {
1321                                 uint8 trans = font2[fontAddr++];
1322                                 uint8 invTrans = trans ^ 0xFF;
1323
1324                                 uint32 bRed   = (eRed   * invTrans + nRed   * trans) / 255;
1325                                 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
1326                                 uint32 bBlue  = (eBlue  * invTrans + nBlue  * trans) / 255;
1327
1328                                 *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
1329                         }
1330                 }
1331
1332                 address += FONT_WIDTH;
1333         }
1334 }
1335
1336 //
1337 // Draw text at the given x/y coordinates with transparency (0 is fully opaque, 32 is fully transparent).
1338 //
1339 void DrawStringTrans(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 trans, const char * text, ...)
1340 {
1341         char string[4096];
1342         va_list arg;
1343
1344         va_start(arg, text);
1345         vsprintf(string, text, arg);
1346         va_end(arg);
1347
1348         uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1349         uint32 length = strlen(string), address = x + (y * pitch);
1350
1351         for(uint32 i=0; i<length; i++)
1352         {
1353                 uint32 fontAddr = (uint32)string[i] * 64;
1354
1355                 for(uint32 yy=0; yy<8; yy++)
1356                 {
1357                         for(uint32 xx=0; xx<8; xx++)
1358                         {
1359                                 if (font1[fontAddr])
1360                                 {
1361                                         uint32 existingColor = *(screen + address + xx + (yy * pitch));
1362
1363                                         uint8 eBlue = (existingColor >> 16) & 0xFF,
1364                                                 eGreen = (existingColor >> 8) & 0xFF,
1365                                                 eRed = existingColor & 0xFF,
1366 //This could be done ahead of time, instead of on each pixel...
1367                                                 nBlue = (color >> 16) & 0xFF,
1368                                                 nGreen = (color >> 8) & 0xFF,
1369                                                 nRed = color & 0xFF;
1370
1371 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
1372 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
1373 //because dividing by 32 is faster than dividing by 31...!
1374                                         uint8 invTrans = 32 - trans;
1375
1376                                         uint32 bRed = (eRed * trans + nRed * invTrans) / 32;
1377                                         uint32 bGreen = (eGreen * trans + nGreen * invTrans) / 32;
1378                                         uint32 bBlue = (eBlue * trans + nBlue * invTrans) / 32;
1379
1380                                         *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
1381                                 }
1382
1383                                 fontAddr++;
1384                         }
1385                 }
1386
1387                 address += 8;
1388         }
1389 }
1390
1391 //
1392 // Draw text at the given x/y coordinates, using FG color and overlay alpha blending.
1393 //
1394 void DrawString2(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 transparency, const char * text, ...)
1395 {
1396         char string[4096];
1397         va_list arg;
1398
1399         va_start(arg, text);
1400         vsprintf(string, text, arg);
1401         va_end(arg);
1402
1403         uint32 pitch = sdlemuGetOverlayWidthInPixels();
1404         uint32 length = strlen(string), address = x + (y * pitch);
1405
1406         color &= 0x00FFFFFF;                                            // Just in case alpha was passed in...
1407
1408         for(uint32 i=0; i<length; i++)
1409         {
1410                 uint8 c = string[i];
1411                 c = (c < 32 ? 0 : c - 32);
1412                 uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;
1413
1414                 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
1415                 {
1416                         for(uint32 xx=0; xx<FONT_WIDTH; xx++)
1417                         {
1418                                 uint8 fontTrans = font2[fontAddr++];
1419                                 uint32 newTrans = (fontTrans * transparency / 255) << 24;
1420                                 uint32 pixel = newTrans | color;
1421
1422                                 *(screen + address + xx + (yy * pitch)) = pixel;
1423                         }
1424                 }
1425
1426                 address += FONT_WIDTH;
1427         }
1428 }
1429
1430 //
1431 // Draw "picture"
1432 // Uses zero as transparent color
1433 // Can also use an optional alpha channel
1434 // Alpha channel is now mandatory! ;-)
1435 //
1436 //void DrawTransparentBitmap(int16 * screen, uint32 x, uint32 y, uint16 * bitmap, uint8 * alpha/*=NULL*/)
1437 /*void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap, uint8 * alpha)
1438 {
1439         uint32 width = bitmap[0], height = bitmap[1];
1440         bitmap += 2;
1441
1442 //      uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
1443         uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1444         uint32 address = x + (y * pitch);
1445
1446         for(uint32 yy=0; yy<height; yy++)
1447         {
1448                 for(uint32 xx=0; xx<width; xx++)
1449                 {
1450                         if (alpha == NULL)
1451                         {
1452                                 if (*bitmap && x + xx < pitch)                  // NOTE: Still doesn't clip the Y val...
1453                                         *(screen + address + xx + (yy * pitch)) = *bitmap;
1454                         }
1455                         else
1456                         {
1457                                 uint8 trans = *alpha;
1458                                 uint32 color = *bitmap;
1459                                 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1460
1461                                 uint8 eRed = existingColor & 0xFF,
1462                                         eGreen = (existingColor >> 8) & 0xFF,
1463                                         eBlue = (existingColor >> 16) & 0xFF,
1464
1465                                         nRed = color & 0xFF,
1466                                         nGreen = (color >> 8) & 0xFF,
1467                                         nBlue = (color >> 16) & 0xFF;
1468
1469                                 uint8 invTrans = 255 - trans;
1470                                 uint32 bRed = (eRed * trans + nRed * invTrans) / 255;
1471                                 uint32 bGreen = (eGreen * trans + nGreen * invTrans) / 255;
1472                                 uint32 bBlue = (eBlue * trans + nBlue * invTrans) / 255;
1473
1474                                 uint32 blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
1475
1476                                 *(screen + address + xx + (yy * pitch)) = blendedColor;
1477
1478                                 alpha++;
1479                         }
1480
1481                         bitmap++;
1482                 }
1483         }
1484 }*/
1485 void DrawTransparentBitmapDeprecated(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap)
1486 {
1487         uint32 width = bitmap[0], height = bitmap[1];
1488         bitmap += 2;
1489
1490         uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1491         uint32 address = x + (y * pitch);
1492
1493         for(uint32 yy=0; yy<height; yy++)
1494         {
1495                 for(uint32 xx=0; xx<width; xx++)
1496                 {
1497                         uint32 color = *bitmap;
1498                         uint32 blendedColor = color;
1499                         uint32 existingColor = *(screen + address + xx + (yy * pitch));
1500
1501                         if (existingColor >> 24 != 0x00)                // Pixel needs blending
1502                         {
1503                                 uint8 trans = color >> 24;
1504                                 uint8 invTrans = trans ^ 0xFF;//255 - trans;
1505
1506                                 uint8 eRed = existingColor & 0xFF,
1507                                         eGreen = (existingColor >> 8) & 0xFF,
1508                                         eBlue = (existingColor >> 16) & 0xFF,
1509
1510                                         nRed = color & 0xFF,
1511                                         nGreen = (color >> 8) & 0xFF,
1512                                         nBlue = (color >> 16) & 0xFF;
1513
1514                                 uint32 bRed = (eRed * invTrans + nRed * trans) / 255;
1515                                 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
1516                                 uint32 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1517
1518                                 blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
1519                         }
1520
1521                         *(screen + address + xx + (yy * pitch)) = blendedColor;
1522                         bitmap++;
1523                 }
1524         }
1525 }
1526
1527 void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap)
1528 {
1529         uint32 pitch = sdlemuGetOverlayWidthInPixels();
1530         uint32 address = x + (y * pitch);
1531         uint32 count = 0;
1532
1533         for(uint32 yy=0; yy<((Bitmap *)bitmap)->height; yy++)
1534         {
1535                 for(uint32 xx=0; xx<((Bitmap *)bitmap)->width; xx++)
1536                 {
1537                         uint32 color = ((uint32 *)((Bitmap *)bitmap)->pixelData)[count];
1538                         uint32 blendedColor = color;
1539                         uint32 existingColor = *(screen + address + xx + (yy * pitch));
1540
1541                         if (existingColor >> 24 != 0x00)        // Pixel needs blending
1542                         {
1543                                 uint8 trans = color >> 24;
1544                                 uint8 invTrans = trans ^ 0xFF;
1545
1546                                 uint8 eRed = existingColor & 0xFF,
1547                                         eGreen = (existingColor >> 8) & 0xFF,
1548                                         eBlue = (existingColor >> 16) & 0xFF,
1549
1550                                         nRed = color & 0xFF,
1551                                         nGreen = (color >> 8) & 0xFF,
1552                                         nBlue = (color >> 16) & 0xFF;
1553
1554                                 uint32 bRed = (eRed * invTrans + nRed * trans) / 255;
1555                                 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
1556                                 uint32 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1557
1558 // Instead of $FF, should use the alpha from the destination pixel as the final alpha value...
1559                                 blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
1560                         }
1561
1562                         *(screen + address + xx + (yy * pitch)) = blendedColor;
1563                         count++;
1564                 }
1565         }
1566 }
1567
1568 //
1569 // Draw a bitmap without using blending
1570 //
1571 void DrawBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap)
1572 {
1573         uint32 pitch = sdlemuGetOverlayWidthInPixels();
1574         uint32 address = x + (y * pitch);
1575         uint32 count = 0;
1576
1577         for(uint32 yy=0; yy<((Bitmap *)bitmap)->height; yy++)
1578         {
1579                 for(uint32 xx=0; xx<((Bitmap *)bitmap)->width; xx++)
1580                 {
1581                         *(screen + address + xx + (yy * pitch)) = ((uint32 *)((Bitmap *)bitmap)->pixelData)[count];
1582                         count++;
1583                 }
1584         }
1585 }
1586
1587 //
1588 // Clear a portion of the screen
1589 //
1590 void ClearScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h)
1591 {
1592         uint32 pitch = sdlemuGetOverlayWidthInPixels();
1593         uint32 address = x + (y * pitch);
1594
1595         for(uint32 yy=0; yy<h; yy++)
1596                 for(uint32 xx=0; xx<w; xx++)
1597                         *(screen + address + xx + (yy * pitch)) = 0;
1598 }
1599
1600
1601 //
1602 // GUI stuff--it's not crunchy, it's GUI! ;-)
1603 //
1604
1605 void InitGUI(void)
1606 {
1607         SDL_ShowCursor(SDL_DISABLE);
1608         SDL_GetMouseState(&mouseX, &mouseY);
1609 }
1610
1611 void GUIDone(void)
1612 {
1613 }
1614
1615 //
1616 // GUI main loop
1617 //
1618 //bool GUIMain(void)
1619 bool GUIMain(char * filename)
1620 {
1621 WriteLog("GUI: Inside GUIMain...\n");
1622
1623         uint32 pointerBGSave[6 * 8 + 2];
1624         pointerBGSave[0] = 6;
1625         pointerBGSave[1] = 8;
1626
1627 // Need to set things up so that it loads and runs a file if given on the command line. !!! FIX !!! [DONE]
1628         extern uint32 * backbuffer;
1629 //      bool done = false;
1630         SDL_Event event;
1631         Window * mainWindow = NULL;
1632
1633         // Set up the GUI classes...
1634 //      Element::SetScreenAndPitch(backbuffer, GetSDLScreenWidthInPixels());
1635         Element::SetScreenAndPitch((uint32 *)sdlemuGetOverlayPixels(), sdlemuGetOverlayWidthInPixels());
1636         sdlemuEnableOverlay();
1637
1638         Menu mainMenu;
1639         MenuItems mi;
1640         mi.title = "Jaguar";
1641         mi.item.push_back(NameAction("Load...", LoadROM, SDLK_l));
1642         mi.item.push_back(NameAction("Reset", ResetJaguar, SDLK_r));
1643         if (CDBIOSLoaded)
1644                 mi.item.push_back(NameAction("Reset CD", ResetJaguarCD, SDLK_c));
1645         mi.item.push_back(NameAction("Run", RunEmu, SDLK_ESCAPE));
1646         mi.item.push_back(NameAction(""));
1647         mi.item.push_back(NameAction("Quit", Quit, SDLK_q));
1648         mainMenu.Add(mi);
1649         mi.title = "Settings";
1650         mi.item.clear();
1651         mi.item.push_back(NameAction("Video..."));
1652         mi.item.push_back(NameAction("Audio..."));
1653         mi.item.push_back(NameAction("Misc...", MiscOptions, SDLK_m));
1654         mainMenu.Add(mi);
1655         mi.title = "Info";
1656         mi.item.clear();
1657         mi.item.push_back(NameAction("About...", About));
1658         mainMenu.Add(mi);
1659
1660         bool showMouse = true;
1661
1662         // Grab the BG where the mouse will be painted (prime the backstore)
1663
1664 /*
1665 DISNOWOK
1666 Bitmap ptr = { 6, 8, 4,
1667 ""//"000011112222333344445555"
1668 //"000011112222333344445555"
1669 //"000011112222333344445555"
1670 //"000011112222333344445555"
1671 //"000011112222333344445555"
1672 //"000011112222333344445555"
1673 //"000011112222333344445555"
1674 //"000011112222333344445555"
1675 };//*/  
1676         uint32 * overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
1677         uint32 count = 2;
1678
1679         for(uint32 y=0; y<pointerBGSave[1]; y++)
1680                 for(uint32 x=0; x<pointerBGSave[0]; x++)
1681                         pointerBGSave[count++] = overlayPixels[((mouseY + y) * sdlemuGetOverlayWidthInPixels()) + (mouseX + x)];
1682
1683         uint32 oldMouseX = mouseX, oldMouseY = mouseY;
1684
1685 //This is crappy!!! !!! FIX !!!
1686 //Is this even needed any more? Hmm. Maybe. Dunno.
1687 WriteLog("GUI: Resetting Jaguar...\n");
1688         jaguar_reset();
1689
1690 WriteLog("GUI: Clearing BG save...\n");
1691         // Set up our background save...
1692 //      memset(background, 0x11, tom_getVideoModeWidth() * 240 * 2);
1693 //1111 -> 000100 01000 10001 -> 0001 0000 0100 0010 1000 1100 -> 10 42 8C
1694         for(uint32 i=0; i<tom_getVideoModeWidth()*240; i++)
1695 //              background[i] = 0xFF8C4210;
1696                 backbuffer[i] = 0xFF8C4210;
1697
1698 /*      uint32 * overlayPix = (uint32 *)sdlemuGetOverlayPixels();
1699         for(uint32 i=0; i<sdlemuGetOverlayWidthInPixels()*480; i++)
1700                 overlayPix[i] = 0x00000000;*/
1701
1702         // Handle loading file passed in on the command line...! [DONE]
1703
1704         if (filename)
1705         {
1706                 if (JaguarLoadFile(filename))
1707                 {
1708 //                      event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
1709 //                      event.user.data1 = (void *)ResetJaguar;
1710 //              SDL_PushEvent(&event);
1711                         // Make it so that if passed in on the command line, we quit right
1712                         // away when pressing ESC
1713 WriteLog("GUI: Bypassing GUI since ROM passed in on command line...\n");
1714                         ResetJaguar();
1715                         return true;
1716                 }
1717                 else
1718                 {
1719                         // Create error dialog...
1720                         char errText[1024];
1721                         sprintf(errText, "The file %40s could not be loaded.", filename);
1722
1723                         mainWindow = new Window(8, 16, 304, 160);
1724                         mainWindow->AddElement(new Text(8, 8, "Error!"));
1725                         mainWindow->AddElement(new Text(8, 24, errText));
1726                 }
1727         }
1728
1729 WriteLog("GUI: Entering main loop...\n");
1730         while (!exitGUI)
1731         {
1732                 if (SDL_PollEvent(&event))
1733                 {
1734                         if (event.type == SDL_USEREVENT)
1735                         {
1736                                 if (event.user.code == WINDOW_CLOSE)
1737                                 {
1738                                         delete mainWindow;
1739                                         mainWindow = NULL;
1740                                 }
1741                                 else if (event.user.code == MENU_ITEM_CHOSEN)
1742                                 {
1743                                         // Confused? Let me enlighten... What we're doing here is casting
1744                                         // data1 as a pointer to a function which returns a Window pointer and
1745                                         // which takes no parameters (the "(Window *(*)(void))" part), then
1746                                         // derefencing it (the "*" in front of that) in order to call the
1747                                         // function that it points to. Clear as mud? Yeah, I hate function
1748                                         // pointers too, but what else are you gonna do?
1749                                         mainWindow = (*(Window *(*)(void))event.user.data1)();
1750
1751                                         while (SDL_PollEvent(&event));  // Flush the event queue...
1752                                         event.type = SDL_MOUSEMOTION;
1753                                         int mx, my;
1754                                         SDL_GetMouseState(&mx, &my);
1755                                         event.motion.x = mx, event.motion.y = my;
1756                                     SDL_PushEvent(&event);                      // & update mouse position...!
1757
1758                                         oldMouseX = mouseX, oldMouseY = mouseY;
1759                                         mouseX = mx, mouseY = my;               // This prevents "mouse flash"...
1760                                 }
1761                         }
1762                         else if (event.type == SDL_ACTIVEEVENT)
1763                         {
1764                                 if (event.active.state == SDL_APPMOUSEFOCUS)
1765                                         showMouse = (event.active.gain ? true : false);
1766                         }
1767                         else if (event.type == SDL_KEYDOWN)
1768                         {
1769                                 if (mainWindow)
1770                                         mainWindow->HandleKey(event.key.keysym.sym);
1771                                 else
1772                                         mainMenu.HandleKey(event.key.keysym.sym);
1773                         }
1774                         else if (event.type == SDL_MOUSEMOTION)
1775                         {
1776                                 oldMouseX = mouseX, oldMouseY = mouseY;
1777                                 mouseX = event.motion.x, mouseY = event.motion.y;
1778
1779                                 if (mainWindow)
1780                                         mainWindow->HandleMouseMove(mouseX, mouseY);
1781                                 else
1782                                         mainMenu.HandleMouseMove(mouseX, mouseY);
1783                         }
1784                         else if (event.type == SDL_MOUSEBUTTONDOWN)
1785                         {
1786                                 uint32 mx = event.button.x, my = event.button.y;
1787
1788                                 if (mainWindow)
1789                                         mainWindow->HandleMouseButton(mx, my, true);
1790                                 else
1791                                         mainMenu.HandleMouseButton(mx, my, true);
1792                         }
1793                         else if (event.type == SDL_MOUSEBUTTONUP)
1794                         {
1795                                 uint32 mx = event.button.x, my = event.button.y;
1796
1797                                 if (mainWindow)
1798                                         mainWindow->HandleMouseButton(mx, my, false);
1799                                 else
1800                                         mainMenu.HandleMouseButton(mx, my, false);
1801                         }
1802
1803 //PROBLEM: In order to use the dirty rectangle approach here, we need some way of
1804 //         handling it in mainMenu.Draw() and mainWindow->Draw(). !!! FIX !!!
1805 //POSSIBLE SOLUTION:
1806 // When mouse is moving and not on menu or window, can do straight dirty rect.
1807 // When mouse is on menu, need to update screen. Same for buttons on windows...
1808 // What the menu & windows should do is only redraw on a state change. IOW, they
1809 // should call their own/child window's Draw() function instead of doing it top
1810 // level.
1811 //#define NEW_BACKSTORE_METHOD
1812
1813                         // Draw the GUI...
1814 // The way we do things here is kinda stupid (redrawing the screen every frame), but
1815 // it's simple. Perhaps there may be a reason down the road to be more selective with
1816 // our clearing, but for now, this will suffice.
1817 //                      memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
1818 //                      memcpy(backbuffer, background, tom_getVideoModeWidth() * 256 * 2);
1819 //                      memcpy(backbuffer, background, tom_getVideoModeWidth() * 256 * 4);
1820 #ifndef NEW_BACKSTORE_METHOD
1821                         memset(sdlemuGetOverlayPixels(), 0, sdlemuGetOverlayWidthInPixels() * 480 * 4);
1822
1823                         mainMenu.Draw();
1824 //Could do multiple windows here by using a vector + priority info...
1825 //Though the way ZSNES does it seems to be by a bool (i.e., they're always active, just not shown)
1826                         if (mainWindow)
1827                                 mainWindow->Draw();
1828 #endif
1829
1830 /*uint32 pBGS[6 * 8 + 3] = { 6, 8, 4,
1831         0, 0, 0, 0, 0, 0,
1832         0, 0, 0, 0, 0, 0,
1833         0, 0, 0, 0, 0, 0,
1834         0, 0, 0, 0, 0, 0,
1835         0, 0, 0, 0, 0, 0,
1836         0, 0, 0, 0, 0, 0,
1837         0, 0, 0, 0, 0, 0,
1838         0, 0, 0, 0, 0, 0
1839 };*/
1840 //This isn't working... Why????
1841 //It's because DrawTransparentBitmap does alpha blending if it detects zero in the alpha channel.
1842 //So why do it that way? Hm.
1843                         overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
1844
1845 #ifdef NEW_BACKSTORE_METHOD
1846 //                      DrawTransparentBitmapDeprecated(overlayPixels, oldMouseX, oldMouseY, pointerBGSave);
1847 //                      DrawTransparentBitmap(overlayPixels, oldMouseX, oldMouseY, pBGS);
1848                         for(uint32 y=0; y<pointerBGSave[1]; y++)
1849                                 for(uint32 x=0; x<pointerBGSave[0]; x++)
1850                                         overlayPixels[((oldMouseY + y) * sdlemuGetOverlayWidthInPixels()) + (oldMouseX + x)] = 0x00000000;
1851
1852                         count = 2;
1853
1854                         for(uint32 y=0; y<pointerBGSave[1]; y++)
1855                                 for(uint32 x=0; x<pointerBGSave[0]; x++)
1856                                         pointerBGSave[count++] = overlayPixels[((mouseY + y) * sdlemuGetOverlayWidthInPixels()) + (mouseX + x)];
1857 #endif
1858
1859                         if (showMouse)
1860 //                              DrawTransparentBitmapDeprecated(backbuffer, mouseX, mouseY, mousePic);
1861                                 DrawTransparentBitmapDeprecated(overlayPixels, mouseX, mouseY, mousePic);
1862
1863                         RenderBackbuffer();
1864                 }
1865         }
1866
1867         return true;
1868 }
1869
1870 //
1871 // GUI "action" functions
1872 //
1873
1874 Window * LoadROM(void)
1875 {
1876         FileList * fileList = new FileList(20, 20, 600, 440);
1877
1878         return (Window *)fileList;
1879 }
1880
1881 Window * ResetJaguar(void)
1882 {
1883         jaguar_reset();
1884
1885         return RunEmu();
1886 }
1887
1888 Window * ResetJaguarCD(void)
1889 {
1890         memcpy(jaguar_mainRom, jaguar_CDBootROM, 0x40000);
1891         jaguarRunAddress = 0x802000;
1892         jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, 0x40000);
1893         jaguar_reset();
1894 //This is a quick kludge to get the CDBIOS to boot properly...
1895 //Wild speculation: It could be that this memory location is wired into the CD unit
1896 //somehow, which lets it know whether or not a cart is present in the unit...
1897         jaguar_mainRom[0x0040B] = 0x03;
1898
1899         return RunEmu();
1900 }
1901
1902
1903 #if 0
1904
1905 bool debounceRunKey = true;
1906 Window * RunEmu(void)
1907 {
1908         extern uint32 * backbuffer;
1909 //Temporary, to test the new timer based code...
1910 sdlemuDisableOverlay();
1911 JaguarExecuteNew();
1912 sdlemuEnableOverlay();
1913         // Save the background for the GUI...
1914         // In this case, we squash the color to monochrome, then force it to blue + green...
1915         for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
1916         {
1917                 uint32 pixel = backbuffer[i];
1918                 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
1919                 pixel = ((r + g + b) / 3) & 0x00FF;
1920                 backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
1921         }
1922 return NULL;//*/
1923
1924 //This is crappy... !!! FIX !!!
1925         extern bool finished, showGUI;
1926
1927 //      uint32 nFrame = 0, nFrameskip = 0;
1928         uint32 totalFrames = 0;
1929         finished = false;
1930         bool showMessage = true;
1931         uint32 showMsgFrames = 120;
1932         uint8 transparency = 0;
1933         // Pass a message to the "joystick" code to debounce the ESC key...
1934         debounceRunKey = true;
1935
1936         uint32 cartType = 4;
1937         if (jaguarRomSize == 0x200000)
1938                 cartType = 0;
1939         else if (jaguarRomSize == 0x400000)
1940                 cartType = 1;
1941         else if (jaguar_mainRom_crc32 == 0x687068D5)
1942                 cartType = 2;
1943         else if (jaguar_mainRom_crc32 == 0x55A0669C)
1944                 cartType = 3;
1945
1946         char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
1947         uint32 elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
1948
1949         while (true)
1950         {
1951                 // Set up new backbuffer with new pixels and data
1952                 JaguarExecute(backbuffer, true);
1953 //              JaguarExecuteNew();
1954                 totalFrames++;
1955 //WriteLog("Frame #%u...\n", totalFrames);
1956 //extern bool doDSPDis;
1957 //if (totalFrames == 373)
1958 //      doDSPDis = true;
1959
1960 //This sucks... !!! FIX !!!
1961                 joystick_exec();
1962 //This is done here so that the crud below doesn't get on our GUI background...
1963                 if (finished)
1964                         break;
1965
1966                 // Some QnD GUI stuff here...
1967                 if (showGUI)
1968                 {
1969                         extern uint32 gpu_pc, dsp_pc;
1970                         DrawString(backbuffer, 8, 8, false, "GPU PC: %08X", gpu_pc);
1971                         DrawString(backbuffer, 8, 16, false, "DSP PC: %08X", dsp_pc);
1972                         DrawString(backbuffer, 8, 32, false, "%u FPS", framesPerSecond);
1973                 }
1974
1975                 if (showMessage)
1976                 {
1977 // FF0F -> 1111 11 11  000 0 1111 -> 3F 18 0F
1978 // 3FE3 -> 0011 11 11  111 0 0011 -> 0F 3F 03
1979 /*                      DrawStringTrans((uint32 *)backbuffer, 8, 24*8, 0xFF0F, transparency, "Running...");
1980                         DrawStringTrans((uint32 *)backbuffer, 8, 26*8, 0x3FE3, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
1981                         DrawStringTrans((uint32 *)backbuffer, 8, 27*8, 0x3FE3, transparency, "CRC: %08X", jaguar_mainRom_crc32);//*/
1982 //first has wrong color. !!! FIX !!!
1983                         DrawStringTrans(backbuffer, 8, 24*8, 0xFF7F63FF, transparency, "Running...");
1984                         DrawStringTrans(backbuffer, 8, 26*8, 0xFF1FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
1985                         DrawStringTrans(backbuffer, 8, 27*8, 0xFF1FFF3F, transparency, "CRC: %08X", jaguar_mainRom_crc32);
1986
1987                         if (showMsgFrames == 0)
1988                         {                       
1989                                 transparency++;
1990
1991                                 if (transparency == 33)
1992 {
1993                                         showMessage = false;
1994 /*extern bool doGPUDis;
1995 doGPUDis = true;//*/
1996 }
1997
1998                         }
1999                         else
2000                                 showMsgFrames--;
2001                 }
2002
2003                 RenderBackbuffer();
2004                 frameCount++;
2005
2006                 if (SDL_GetTicks() - elapsedTicks > 250)
2007                         elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
2008         }
2009
2010         // Reset the pitch, since it may have been changed in-game...
2011         Element::SetScreenAndPitch((uint32 *)backbuffer, GetSDLScreenWidthInPixels());
2012
2013         // Save the background for the GUI...
2014 //      memcpy(background, backbuffer, tom_getVideoModeWidth() * 240 * 2);
2015         // In this case, we squash the color to monochrome, then force it to blue + green...
2016         for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
2017         {
2018                 uint32 pixel = backbuffer[i];
2019                 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
2020                 pixel = ((r + g + b) / 3) & 0x00FF;
2021                 background[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
2022         }
2023
2024         return NULL;
2025 }
2026
2027 #else
2028
2029 bool debounceRunKey = true;
2030 Window * RunEmu(void)
2031 {
2032         extern uint32 * backbuffer;
2033         uint32 * overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
2034         memset(overlayPixels, 0x00, 640 * 480 * 4);                     // Clear out overlay...
2035
2036 //This is crappy... !!! FIX !!!
2037         extern bool finished, showGUI;
2038
2039         sdlemuDisableOverlay();
2040
2041 //      uint32 nFrame = 0, nFrameskip = 0;
2042         uint32 totalFrames = 0;
2043         finished = false;
2044         bool showMessage = true;
2045         uint32 showMsgFrames = 120;
2046         uint8 transparency = 0xFF;
2047         // Pass a message to the "joystick" code to debounce the ESC key...
2048         debounceRunKey = true;
2049
2050         uint32 cartType = 4;
2051         if (jaguarRomSize == 0x200000)
2052                 cartType = 0;
2053         else if (jaguarRomSize == 0x400000)
2054                 cartType = 1;
2055         else if (jaguar_mainRom_crc32 == 0x687068D5)
2056                 cartType = 2;
2057         else if (jaguar_mainRom_crc32 == 0x55A0669C)
2058                 cartType = 3;
2059
2060         char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
2061         uint32 elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
2062
2063         while (!finished)
2064         {
2065                 // Set up new backbuffer with new pixels and data
2066                 JaguarExecuteNew();
2067                 totalFrames++;
2068 //WriteLog("Frame #%u...\n", totalFrames);
2069 //extern bool doDSPDis;
2070 //if (totalFrames == 373)
2071 //      doDSPDis = true;
2072
2073 //Problem: Need to do this *only* when the state changes from visible to not...
2074 if (showGUI || showMessage)
2075         sdlemuEnableOverlay();
2076 else
2077         sdlemuDisableOverlay();
2078
2079 //Add in a new function for clearing patches of screen (ClearOverlayRect)
2080
2081                 // Some QnD GUI stuff here...
2082                 if (showGUI)
2083                 {
2084                         ClearScreenRectangle(overlayPixels, 8, 1*FONT_HEIGHT, 128, 4*FONT_HEIGHT);
2085                         extern uint32 gpu_pc, dsp_pc;
2086                         DrawString(overlayPixels, 8, 1*FONT_HEIGHT, false, "GPU PC: %08X", gpu_pc);
2087                         DrawString(overlayPixels, 8, 2*FONT_HEIGHT, false, "DSP PC: %08X", dsp_pc);
2088                         DrawString(overlayPixels, 8, 4*FONT_HEIGHT, false, "%u FPS", framesPerSecond);
2089                 }
2090
2091                 if (showMessage)
2092                 {
2093                         DrawString2(overlayPixels, 8, 24*FONT_HEIGHT, 0x007F63FF, transparency, "Running...");
2094                         DrawString2(overlayPixels, 8, 26*FONT_HEIGHT, 0x001FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
2095                         DrawString2(overlayPixels, 8, 27*FONT_HEIGHT, 0x001FFF3F, transparency, "CRC: %08X", jaguar_mainRom_crc32);
2096
2097                         if (showMsgFrames == 0)
2098                         {                       
2099                                 transparency--;
2100
2101                                 if (transparency == 0)
2102 {
2103                                         showMessage = false;
2104 /*extern bool doGPUDis;
2105 doGPUDis = true;//*/
2106 }
2107
2108                         }
2109                         else
2110                                 showMsgFrames--;
2111                 }
2112
2113                 frameCount++;
2114
2115                 if (SDL_GetTicks() - elapsedTicks > 250)
2116                         elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
2117         }
2118
2119         // Save the background for the GUI...
2120         // In this case, we squash the color to monochrome, then force it to blue + green...
2121         for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
2122         {
2123                 uint32 pixel = backbuffer[i];
2124                 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
2125                 pixel = ((r + g + b) / 3) & 0x00FF;
2126                 backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
2127         }
2128
2129         sdlemuEnableOverlay();
2130
2131         return NULL;
2132 }
2133
2134 #endif
2135
2136
2137 Window * Quit(void)
2138 {
2139         WriteLog("GUI: Quitting due to user request.\n");
2140         exitGUI = true;
2141
2142         return NULL;
2143 }
2144
2145 Window * About(void)
2146 {
2147         char buf[512];
2148 //      sprintf(buf, "Virtual Jaguar CVS %s", __DATE__);
2149         sprintf(buf, "CVS %s", __DATE__);
2150 //fprintf(fp, "VirtualJaguar v1.0.8 (Last full build was on %s %s)\n", __DATE__, __TIME__);
2151 //VirtualJaguar v1.0.8 (Last full build was on Dec 30 2004 20:01:31)
2152 //Hardwired, bleh... !!! FIX !!!
2153 uint32 width = 55 * FONT_WIDTH, height = 18 * FONT_HEIGHT;
2154 uint32 xpos = (640 - width) / 2, ypos = (480 - height) / 2;
2155 //      Window * window = new Window(8, 16, 50 * FONT_WIDTH, 21 * FONT_HEIGHT);
2156         Window * window = new Window(xpos, ypos, width, height);
2157 //      window->AddElement(new Text(8, 8, "Virtual Jaguar 1.0.8"));
2158 //      window->AddElement(new Text(8, 8, "Virtual Jaguar CVS 20050110", 0xFF3030FF, 0xFF000000));
2159 //      window->AddElement(new Text(208, 8+0*FONT_HEIGHT, buf, 0xFF3030FF, 0xFF000000));
2160         window->AddElement(new Text(248, 8+4*FONT_HEIGHT+5, buf, 0xFF3030FF, 0xFF000000));
2161         window->AddElement(new Text(8, 8+0*FONT_HEIGHT, "Coders:"));
2162         window->AddElement(new Text(16, 8+1*FONT_HEIGHT, "James L. Hammons (shamus)"));
2163         window->AddElement(new Text(16, 8+2*FONT_HEIGHT, "Niels Wagenaar (nwagenaar)"));
2164         window->AddElement(new Text(16, 8+3*FONT_HEIGHT, "Carwin Jones (Caz)"));
2165         window->AddElement(new Text(16, 8+4*FONT_HEIGHT, "Adam Green"));
2166         window->AddElement(new Text(8, 8+6*FONT_HEIGHT, "Testers:"));
2167         window->AddElement(new Text(16, 8+7*FONT_HEIGHT, "Guruma"));
2168         window->AddElement(new Text(8, 8+9*FONT_HEIGHT, "Thanks go out to:"));
2169         window->AddElement(new Text(16, 8+10*FONT_HEIGHT, "Aaron Giles for the original CoJag"));
2170         window->AddElement(new Text(16, 8+11*FONT_HEIGHT, "David Raingeard for the original VJ"));
2171         window->AddElement(new Text(16, 8+12*FONT_HEIGHT, "Karl Stenerud for his Musashi 68K emu"));
2172         window->AddElement(new Text(16, 8+13*FONT_HEIGHT, "Sam Lantinga for his amazing SDL libs"));
2173         window->AddElement(new Text(16, 8+14*FONT_HEIGHT, "Ryan C. Gordon for VJ's web presence"));
2174         window->AddElement(new Text(16, 8+15*FONT_HEIGHT, "Curt Vendel for various Jaguar goodies"));
2175         window->AddElement(new Text(16, 8+16*FONT_HEIGHT, "The guys over at Atari Age ;-)"));
2176 //      window->AddElement(new Image(8, 8, &vj_title_small));
2177         window->AddElement(new Image(width - (vj_title_small.width + 8), 8, &vj_title_small));
2178
2179         return window;
2180 }
2181
2182 Window * MiscOptions(void)
2183 {
2184         Window * window = new Window(8, 16, 304, 192);
2185         window->AddElement(new PushButton(8, 8, &vjs.useJaguarBIOS, "BIOS"));
2186         window->AddElement(new SlideSwitch(8, 32, &vjs.hardwareTypeNTSC, "PAL", "NTSC"));
2187         window->AddElement(new PushButton(8, 64, &vjs.DSPEnabled, "DSP"));
2188         window->AddElement(new SlideSwitch(24, 88, &vjs.usePipelinedDSP, "Original", "Pipelined"));
2189         window->AddElement(new SlideSwitch(8, 120, (bool *)&vjs.glFilter, "Sharp", "Blurry"));
2190         window->AddElement(new SlideSwitch(8, 152, (bool *)&vjs.renderType, "Normal render", "TV style"));
2191
2192 // Missing:
2193 // * BIOS path
2194 // * ROM path
2195 // * EEPROM path
2196 // * joystick
2197 // * joystick port
2198 // * OpenGL?
2199 // * GL Filter type
2200 // * Window/fullscreen
2201 // * Key definitions
2202
2203         return window;
2204 }
2205
2206
2207 //
2208 // Generic ROM loading
2209 //
2210 uint32 JaguarLoadROM(uint8 * rom, char * path)
2211 {
2212 // We really should have some kind of sanity checking for the ROM size here to prevent
2213 // a buffer overflow... !!! FIX !!!
2214         uint32 romSize = 0;
2215
2216 WriteLog("JaguarLoadROM: Attempting to load file '%s'...", path);
2217         char * ext = strrchr(path, '.');
2218 if (ext == NULL)
2219         WriteLog("FAILED!\n");
2220 else
2221         WriteLog("Succeeded in finding extension (%s)!\n", ext);
2222
2223         if (ext != NULL)
2224         {
2225                 WriteLog("VJ: Loading \"%s\"...", path);
2226
2227                 if (strcasecmp(ext, ".zip") == 0)
2228                 {
2229                         // Handle ZIP file loading here...
2230                         WriteLog("(ZIPped)...");
2231
2232                         if (load_zipped_file(0, 0, path, NULL, &rom, &romSize) == -1)
2233                         {
2234                                 WriteLog("Failed!\n");
2235                                 return 0;
2236                         }
2237                 }
2238                 else
2239                 {
2240 /*                      FILE * fp = fopen(path, "rb");
2241
2242                         if (fp == NULL)
2243                         {
2244                                 WriteLog("Failed!\n");
2245                                 return 0;
2246                         }
2247
2248                         fseek(fp, 0, SEEK_END);
2249                         romSize = ftell(fp);
2250                         fseek(fp, 0, SEEK_SET);
2251                         fread(rom, 1, romSize, fp);
2252                         fclose(fp);*/
2253
2254                         // Handle gzipped files transparently [Adam Green]...
2255
2256                         gzFile fp = gzopen(path, "rb");
2257
2258                         if (fp == NULL)
2259                         {
2260                                 WriteLog("Failed!\n");
2261                                 return 0;
2262                         }
2263
2264                         romSize = gzfilelength(fp);
2265                         gzseek(fp, 0, SEEK_SET);
2266                         gzread(fp, rom, romSize);
2267                         gzclose(fp);
2268                 }
2269
2270                 WriteLog("OK (%i bytes)\n", romSize);
2271         }
2272
2273         return romSize;
2274 }
2275
2276 //
2277 // Jaguar file loading
2278 //
2279 bool JaguarLoadFile(char * path)
2280 {
2281 //      jaguarRomSize = JaguarLoadROM(mem, path);
2282         jaguarRomSize = JaguarLoadROM(jaguar_mainRom, path);
2283
2284 /*//This is not *nix friendly for some reason...
2285 //              if (!UserSelectFile(path, newPath))
2286         if (!UserSelectFile((strlen(path) == 0 ? (char *)"." : path), newPath))
2287         {
2288                 WriteLog("VJ: Could not find valid ROM in directory \"%s\"...\nAborting!\n", path);
2289                 log_done();
2290                 exit(0);
2291         }*/
2292
2293         if (jaguarRomSize == 0)
2294         {
2295 //                      WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
2296                 WriteLog("GUI: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
2297 // Need to do something else here, like throw up an error dialog instead of aborting. !!! FIX !!!
2298 //              log_done();
2299 //              exit(0);
2300                 return false;                                                           // This is a start...
2301         }
2302
2303         jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, jaguarRomSize);
2304         WriteLog("CRC: %08X\n", (unsigned int)jaguar_mainRom_crc32);
2305         eeprom_init();
2306
2307         jaguarRunAddress = 0x802000;
2308
2309         char * ext = strrchr(path, '.');                                // Get the file's extension for non-cartridge checking
2310
2311 //NOTE: Should fix JaguarLoadROM() to replace .zip with what's *in* the zip (.abs, .j64, etc.)
2312         if (strcasecmp(ext, ".rom") == 0)
2313         {
2314                 // File extension ".ROM": Alpine image that loads/runs at $802000
2315                 WriteLog("GUI: Setting up homebrew (ROM)... Run address: 00802000, length: %08X\n", jaguarRomSize);
2316
2317                 for(int i=jaguarRomSize-1; i>=0; i--)
2318                         jaguar_mainRom[0x2000 + i] = jaguar_mainRom[i];
2319
2320                 memset(jaguar_mainRom, 0xFF, 0x2000);
2321 /*              memcpy(jaguar_mainRam, jaguar_mainRom, jaguarRomSize);
2322                 memset(jaguar_mainRom, 0xFF, 0x600000);
2323                 memcpy(jaguar_mainRom + 0x2000, jaguar_mainRam, jaguarRomSize);
2324                 memset(jaguar_mainRam, 0x00, 0x400000);*/
2325
2326 /*
2327 Stubulator ROM vectors...
2328 handler 001 at $00E00008
2329 handler 002 at $00E008DE
2330 handler 003 at $00E008E2
2331 handler 004 at $00E008E6
2332 handler 005 at $00E008EA
2333 handler 006 at $00E008EE
2334 handler 007 at $00E008F2
2335 handler 008 at $00E0054A
2336 handler 009 at $00E008FA
2337 handler 010 at $00000000
2338 handler 011 at $00000000
2339 handler 012 at $00E008FE
2340 handler 013 at $00E00902
2341 handler 014 at $00E00906
2342 handler 015 at $00E0090A
2343 handler 016 at $00E0090E
2344 handler 017 at $00E00912
2345 handler 018 at $00E00916
2346 handler 019 at $00E0091A
2347 handler 020 at $00E0091E
2348 handler 021 at $00E00922
2349 handler 022 at $00E00926
2350 handler 023 at $00E0092A
2351 handler 024 at $00E0092E
2352 handler 025 at $00E0107A
2353 handler 026 at $00E0107A
2354 handler 027 at $00E0107A
2355 handler 028 at $00E008DA
2356 handler 029 at $00E0107A
2357 handler 030 at $00E0107A
2358 handler 031 at $00E0107A
2359 handler 032 at $00000000
2360
2361 Let's try setting up the illegal instruction vector for a stubulated jaguar...
2362 */
2363 /*              SET32(jaguar_mainRam, 0x08, 0x00E008DE);
2364                 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
2365                 SET32(jaguar_mainRam, 0x10, 0x00E008E6);        // <-- Should be here (it is)...
2366                 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
2367
2368                 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
2369                 // This kludge works! Yeah!
2370                 SET32(jaguar_mainRam, 0x10, 0x00001000);
2371                 SET16(jaguar_mainRam, 0x1000, 0x60FE);          // Here: bra Here
2372         }
2373         else if (strcasecmp(ext, ".abs") == 0)
2374         {
2375                 // File extension ".ABS": Atari linker output file with header (w/o is useless to us here)
2376
2377 /*
2378 ABS Format sleuthing (LBUGDEMO.ABS):
2379
2380 000000  60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
2381 000010  12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
2382 000020  00 00 40 00
2383
2384 DRI-format file detected...
2385 Text segment size = 0x0000050c bytes
2386 Data segment size = 0x000462c0 bytes
2387 BSS Segment size = 0x00000428 bytes
2388 Symbol Table size = 0x000012a6 bytes
2389 Absolute Address for text segment = 0x00802000
2390 Absolute Address for data segment = 0x0080250c
2391 Absolute Address for BSS segment = 0x00004000
2392
2393 (CRZDEMO.ABS):
2394 000000  01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
2395 000010  00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
2396 000020  00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
2397
2398 000030  2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
2399 000040  00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
2400 000050  00 00 00 00 00 00 00 20
2401 000058  2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
2402 000068  00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
2403 000078  00 00 00 00 00 00 00 40
2404 000080  2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
2405 000090  00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
2406 0000a0  00 00 00 00 00 00 00 80
2407
2408 Header size is $A8 bytes...
2409
2410 BSD/COFF format file detected...
2411 3 sections specified
2412 Symbol Table offset = 230160                            ($00038310)
2413 Symbol Table contains 1339 symbol entries       ($0000053B)
2414 The additional header size is 28 bytes          ($001C)
2415 Magic Number for RUN_HDR = 0x00000107
2416 Text Segment Size = 7632                                        ($00001DD0)
2417 Data Segment Size = 222360                                      ($00036498)
2418 BSS Segment Size = 428928                                       ($00068B80)
2419 Starting Address for executable = 0x00802000
2420 Start of Text Segment = 0x00802000
2421 Start of Data Segment = 0x00803dd0
2422 */
2423                 if (jaguar_mainRom[0] == 0x60 && jaguar_mainRom[1] == 0x1B)
2424                 {
2425                         uint32 loadAddress = GET32(jaguar_mainRom, 0x16), //runAddress = GET32(jaguar_mainRom, 0x2A),
2426                                 codeSize = GET32(jaguar_mainRom, 0x02) + GET32(jaguar_mainRom, 0x06);
2427                         WriteLog("GUI: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
2428
2429                         if (loadAddress < 0x800000)
2430                                 memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x24, codeSize);
2431                         else
2432                         {
2433                                 for(int i=codeSize-1; i>=0; i--)
2434                                         jaguar_mainRom[(loadAddress - 0x800000) + i] = jaguar_mainRom[i + 0x24];
2435 /*                              memcpy(jaguar_mainRam, jaguar_mainRom + 0x24, codeSize);
2436                                 memset(jaguar_mainRom, 0xFF, 0x600000);
2437                                 memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
2438                                 memset(jaguar_mainRam, 0x00, 0x400000);*/
2439                         }
2440
2441                         jaguarRunAddress = loadAddress;
2442                 }
2443                 else if (jaguar_mainRom[0] == 0x01 && jaguar_mainRom[1] == 0x50)
2444                 {
2445                         uint32 loadAddress = GET32(jaguar_mainRom, 0x28), runAddress = GET32(jaguar_mainRom, 0x24),
2446                                 codeSize = GET32(jaguar_mainRom, 0x18) + GET32(jaguar_mainRom, 0x1C);
2447                         WriteLog("GUI: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
2448
2449                         if (loadAddress < 0x800000)
2450                                 memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0xA8, codeSize);
2451                         else
2452                         {
2453                                 for(int i=codeSize-1; i>=0; i--)
2454                                         jaguar_mainRom[(loadAddress - 0x800000) + i] = jaguar_mainRom[i + 0xA8];
2455 /*                              memcpy(jaguar_mainRam, jaguar_mainRom + 0xA8, codeSize);
2456                                 memset(jaguar_mainRom, 0xFF, 0x600000);
2457                                 memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
2458                                 memset(jaguar_mainRam, 0x00, 0x400000);*/
2459                         }
2460
2461                         jaguarRunAddress = runAddress;
2462                 }
2463                 else
2464                 {
2465                         WriteLog("GUI: Couldn't find correct ABS format: %02X %02X\n", jaguar_mainRom[0], jaguar_mainRom[1]);
2466                         return false;
2467                 }
2468         }
2469         else if (strcasecmp(ext, ".jag") == 0)
2470         {
2471                 // File extension ".JAG": Atari server file with header
2472 //NOTE: The bytes 'JAGR' should also be at position $1C...
2473 //      Also, there's *always* a $601A header at position $00...
2474                 if (jaguar_mainRom[0] == 0x60 && jaguar_mainRom[1] == 0x1A)
2475                 {
2476                         uint32 loadAddress = GET32(jaguar_mainRom, 0x22), runAddress = GET32(jaguar_mainRom, 0x2A);
2477 //This is not always right! Especially when converted via bin2jag1!!!
2478 //We should have access to the length of the furshlumiger file that was loaded anyway!
2479 //Now, we do! ;-)
2480 //                      uint32 progLength = GET32(jaguar_mainRom, 0x02);
2481 //jaguarRomSize
2482 //jaguarRunAddress
2483 //                      WriteLog("Jaguar: Setting up PD ROM... Run address: %08X, length: %08X\n", runAddress, progLength);
2484 //                      memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, progLength);
2485                         WriteLog("GUI: Setting up homebrew (JAG)... Run address: %08X, length: %08X\n", runAddress, jaguarRomSize - 0x2E);
2486                         memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, jaguarRomSize - 0x2E);
2487 //              SET32(jaguar_mainRam, 4, runAddress);
2488                         jaguarRunAddress = runAddress;
2489                 }
2490                 else
2491                         return false;
2492         }
2493         // .J64 (Jaguar cartridge ROM image) is implied by the FileList object...
2494
2495         return true;
2496 }
2497
2498 //
2499 // Get the length of a (possibly) gzipped file
2500 //
2501 int gzfilelength(gzFile gd)
2502 {
2503    int size = 0, length = 0;
2504    unsigned char buffer[0x10000];
2505
2506    gzrewind(gd);
2507
2508    do
2509    {
2510       // Read in chunks until EOF
2511       size = gzread(gd, buffer, 0x10000);
2512
2513       if (size <= 0)
2514         break;
2515
2516       length += size;
2517    }
2518    while (!gzeof(gd));
2519
2520    gzrewind(gd);
2521    return length;
2522 }