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