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