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