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