]> Shamusworld >> Repos - virtualjaguar/blob - src/gui.cpp
4797e600aec57acfb661e2ab8f49cdda5e402295
[virtualjaguar] / src / gui.cpp
1 //
2 // GUI.CPP
3 //
4 // Graphical User Interface support
5 // by James L. Hammons
6 //
7
8 #include <dirent.h>
9 #include <SDL.h>
10 #include <string>
11 #include <vector>
12 #include <algorithm>
13 #include "types.h"
14 #include "settings.h"
15 #include "tom.h"
16 #include "video.h"
17 #include "font1.h"
18 #include "crc32.h"
19 #include "zlib.h"
20 #include "unzip.h"
21 #include "gui.h"
22
23 using namespace std;                                                            // For STL stuff
24
25 // Private function prototypes
26
27 class Window;                                                                           // Forward declaration...
28
29 void DrawTransparentBitmap(int16 * screen, uint32 x, uint32 y, uint16 * bitmap);
30 void DrawStringTrans(int16 * screen, uint32 x, uint32 y, uint16 color, uint8 opacity, const char * text, ...);
31 void DrawStringOpaque(int16 * screen, uint32 x, uint32 y, uint16 color1, uint16 color2, const char * text, ...);
32 Window * LoadROM(void);
33 Window * ResetJaguar(void);
34 Window * RunEmu(void);
35 Window * Quit(void);
36 Window * About(void);
37
38 int gzfilelength(gzFile gd);
39
40 // External variables
41
42 extern uint8 * jaguar_mainRam;
43 extern uint8 * jaguar_bootRom;
44 extern uint8 * jaguar_mainRom;
45
46 // Local global variables
47
48 int mouseX, mouseY;
49
50 uint16 mousePic[] = {
51         6, 8,
52
53         0x03E0,0x0000,0x0000,0x0000,0x0000,0x0000,              // +
54         0x0300,0x03E0,0x0000,0x0000,0x0000,0x0000,              // @+
55         0x0300,0x03E0,0x03E0,0x0000,0x0000,0x0000,              // @++
56         0x0300,0x0300,0x03E0,0x03E0,0x0000,0x0000,              // @@++
57         0x0300,0x0300,0x03E0,0x03E0,0x03E0,0x0000,              // @@+++
58         0x0300,0x0300,0x0300,0x03E0,0x03E0,0x03E0,              // @@@+++
59         0x0300,0x0300,0x0300,0x0000,0x0000,0x0000,              // @@@
60         0x0300,0x0000,0x0000,0x0000,0x0000,0x0000               // @
61 /*
62         0xFFFF,0x0000,0x0000,0x0000,0x0000,0x0000,              // +
63         0xE318,0xFFFF,0x0000,0x0000,0x0000,0x0000,              // @+
64         0xE318,0xFFFF,0xFFFF,0x0000,0x0000,0x0000,              // @++
65         0xE318,0xE318,0xFFFF,0xFFFF,0x0000,0x0000,              // @@++
66         0xE318,0xE318,0xFFFF,0xFFFF,0xFFFF,0x0000,              // @@+++
67         0xE318,0xE318,0xE318,0xFFFF,0xFFFF,0xFFFF,              // @@@+++
68         0xE318,0xE318,0xE318,0x0000,0x0000,0x0000,              // @@@
69         0xE318,0x0000,0x0000,0x0000,0x0000,0x0000               // @
70 */
71 };
72 // 1 111 00 11 100 1 1100 -> F39C
73 // 1 100 00 10 000 1 0000 -> C210
74 // 1 110 00 11 000 1 1000 -> E318
75 // 0 000 00 11 111 0 0000 -> 03E0
76 // 0 000 00 11 000 0 0000 -> 0300
77
78 uint16 closeBox[] = {
79         7, 7,
80
81         0x0000,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x0000,               //  +++++
82         0x4B5E,0xFFFF,0x0000,0x0000,0x0000,0xFFFF,0x0217,               // +@   @.
83         0x4B5E,0x0000,0xFFFF,0x0000,0xFFFF,0x0000,0x0217,               // + @ @ .
84         0x4B5E,0x0000,0x0000,0xFFFF,0x0000,0x0000,0x0217,               // +  @  .
85         0x4B5E,0x0000,0xFFFF,0x0000,0xFFFF,0x0000,0x0217,               // + @ @ .
86         0x4B5E,0xFFFF,0x0000,0x0000,0x0000,0xFFFF,0x0217,               // +@   @.
87         0x0000,0x0217,0x0217,0x0217,0x0217,0x0217,0x0000                //  .....
88 };
89
90 uint16 upArrowBox[] = {
91         8, 8,
92
93         0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,                // ++++++++
94         0x4B5E,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0217,                // +  @@  .
95         0x4B5E,0x0000,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x0000,0x0217,                // + @@@@ .
96         0x4B5E,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x0217,                // +@@@@@@.
97         0x4B5E,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0217,                // +  @@  .
98         0x4B5E,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0217,                // +  @@  .
99         0x4B5E,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0217,                // +  @@  .
100         0x0217,0x0217,0x0217,0x0217,0x0217,0x0217,0x0217,0x0217                 // ........
101 };
102
103 uint16 downArrowBox[] = {
104         8, 8,
105
106         0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,                // ++++++++
107         0x4B5E,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0217,                // +  @@  .
108         0x4B5E,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0217,                // +  @@  .
109         0x4B5E,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0217,                // +  @@  .
110         0x4B5E,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x0217,                // +@@@@@@.
111         0x4B5E,0x0000,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x0000,0x0217,                // + @@@@ .
112         0x4B5E,0x0000,0x0000,0xFFFF,0xFFFF,0x0000,0x0000,0x0217,                // +  @@  .
113         0x0217,0x0217,0x0217,0x0217,0x0217,0x0217,0x0217,0x0217                 // ........
114 };
115
116 char separator[] = "--------------------------------------------------------";
117
118 uint16 background[1280 * 240];
119
120 //
121 // Local GUI classes
122 //
123
124 enum { WINDOW_CLOSE, MENU_ITEM_CHOSEN };
125
126 class Element
127 {
128         public:
129                 Element(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0)
130                         { extents.x = x, extents.y = y, extents.w = w, extents.h = h; }
131                 virtual void HandleKey(SDLKey key) = 0;
132                 virtual void HandleMouseMove(uint32 x, uint32 y) = 0;
133                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) = 0;
134                 virtual void Draw(uint32, uint32) = 0;
135                 virtual void Notify(Element *) = 0;
136 //Needed?               virtual ~Element() = 0;
137 //We're not allocating anything in the base class, so the answer would be NO.
138                 bool Inside(uint32 x, uint32 y);
139                 // Class method
140                 static void SetScreenAndPitch(int16 * s, uint32 p) { screenBuffer = s, pitch = p; }
141
142         protected:
143                 SDL_Rect extents;
144                 uint32 state;
145                 // Class variables...
146                 static int16 * screenBuffer;
147                 static uint32 pitch;
148 };
149
150 int16 * Element::screenBuffer = NULL;
151 uint32 Element::pitch = 0;
152
153 bool Element::Inside(uint32 x, uint32 y)
154 {
155         return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
156                 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false);
157 }
158
159 class Button: public Element
160 {
161         public:
162                 Button(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
163                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
164                         bgColor(0x03E0), pic(NULL), elementToTell(NULL) {}
165                 Button(uint32 x, uint32 y, uint32 w, uint32 h, uint16 * p): Element(x, y, w, h),
166                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
167                         bgColor(0x03E0), pic(p), elementToTell(NULL) {}
168                 Button(uint32 x, uint32 y, uint16 * p): Element(x, y, 0, 0),
169                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
170                         bgColor(0x03E0), pic(p), elementToTell(NULL)
171                         { if (pic) extents.w = pic[0], extents.h = pic[1]; }
172                 Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
173                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
174                         bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL) {}
175                 Button(uint32 x, uint32 y, string s): Element(x, y, 0, 8),
176                         activated(false), clicked(false), inside(false), fgColor(0xFFFF),
177                         bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL)
178                         { extents.w = s.length() * 8; }
179                 virtual void HandleKey(SDLKey key) {}
180                 virtual void HandleMouseMove(uint32 x, uint32 y);
181                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
182                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
183                 virtual void Notify(Element *) {}
184                 bool ButtonClicked(void) { return activated; }
185                 void SetNotificationElement(Element * e) { elementToTell = e; }
186
187         protected:
188                 bool activated, clicked, inside;
189                 uint16 fgColor, bgColor;
190                 uint16 * pic;
191                 string text;
192                 Element * elementToTell;
193 };
194
195 void Button::HandleMouseMove(uint32 x, uint32 y)
196 {
197         inside = Inside(x, y);
198 }
199
200 void Button::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
201 {
202         if (inside)
203         {
204                 if (mouseDown)
205                         clicked = true;
206
207                 if (clicked && !mouseDown)
208                 {
209                         clicked = false, activated = true;
210
211                         // Send a message that we're activated (if there's someone to tell, that is)
212                         if (elementToTell)
213                                 elementToTell->Notify(this);
214                 }
215         }
216         else
217                 clicked = activated = false;
218 }
219
220 void Button::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
221 {
222         uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
223
224         for(uint32 y=0; y<extents.h; y++)
225         {
226                 for(uint32 x=0; x<extents.w; x++)
227                 {
228                         // Doesn't clip in y axis! !!! FIX !!!
229                         if (extents.x + x < pitch)
230                                 screenBuffer[addr + x + (y * pitch)] 
231                                         = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
232                 }
233         }
234
235         if (pic != NULL)
236                 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, pic);
237
238         if (text.length() > 0)
239                 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
240 }
241
242 class Window: public Element
243 {
244         public:
245 /*              Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
246                         fgColor(0x4FF0), bgColor(0xFE10)
247                         { close = new Button(w - 8, 1, closeBox); list.push_back(close); }*/
248                 Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0,
249                         void (* f)(Element *) = NULL): Element(x, y, w, h),
250                         /*clicked(false), inside(false),*/ fgColor(0x4FF0), bgColor(0x1E10),
251                         handler(f)
252                         { close = new Button(w - 8, 1, closeBox); list.push_back(close);
253                           close->SetNotificationElement(this); }
254                 virtual ~Window();
255                 virtual void HandleKey(SDLKey key);
256                 virtual void HandleMouseMove(uint32 x, uint32 y);
257                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
258                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
259                 virtual void Notify(Element * e);
260                 void AddElement(Element * e);
261 //              bool WindowActive(void) { return true; }//return !close->ButtonClicked(); }
262
263         protected:
264 //              bool clicked, inside;
265                 uint16 fgColor, bgColor;
266                 void (* handler)(Element *);
267                 Button * close;
268 //We have to use a list of Element *pointers* because we can't make a list that will hold
269 //all the different object types in the same list...
270                 vector<Element *> list;
271 };
272
273 Window::~Window()
274 {
275         for(uint32 i=0; i<list.size(); i++)
276                 if (list[i])
277                         delete list[i];
278 }
279
280 void Window::HandleKey(SDLKey key)
281 {
282         if (key == SDLK_ESCAPE)
283         {
284                 SDL_Event event;
285                 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
286                 SDL_PushEvent(&event);
287         }
288
289         // Handle the items this window contains...
290         for(uint32 i=0; i<list.size(); i++)
291                 // Make coords relative to upper right corner of this window...
292                 list[i]->HandleKey(key);
293 }
294
295 void Window::HandleMouseMove(uint32 x, uint32 y)
296 {
297         // Handle the items this window contains...
298         for(uint32 i=0; i<list.size(); i++)
299                 // Make coords relative to upper right corner of this window...
300                 list[i]->HandleMouseMove(x - extents.x, y - extents.y);
301 }
302
303 void Window::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
304 {
305         // Handle the items this window contains...
306         for(uint32 i=0; i<list.size(); i++)
307                 // Make coords relative to upper right corner of this window...
308                 list[i]->HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
309 }
310
311 void Window::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
312 {
313         uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
314
315         for(uint32 y=0; y<extents.h; y++)
316         {
317                 for(uint32 x=0; x<extents.w; x++)
318                 {
319                         // Doesn't clip in y axis! !!! FIX !!!
320                         if (extents.x + x < pitch)
321                                 screenBuffer[addr + x + (y * pitch)] = bgColor;
322                 }
323         }
324
325         // Handle the items this window contains...
326         for(uint32 i=0; i<list.size(); i++)
327                 list[i]->Draw(extents.x, extents.y);
328 }
329
330 void Window::AddElement(Element * e)
331 {
332         list.push_back(e);
333 }
334
335 void Window::Notify(Element * e)
336 {
337         if (e == close)
338         {
339                 SDL_Event event;
340                 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
341                 SDL_PushEvent(&event);
342         }
343 }
344
345 class Text: public Element
346 {
347         public:
348                 Text(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
349                         fgColor(0x4FF0), bgColor(0xFE10) {}
350                 Text(uint32 x, uint32 y, string s): Element(x, y, 0, 0),
351                         fgColor(0x4FF0), bgColor(0xFE10), text(s) {}
352                 virtual void HandleKey(SDLKey key) {}
353                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
354                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
355                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
356                 virtual void Notify(Element *) {}
357
358         protected:
359                 uint16 fgColor, bgColor;
360                 string text;
361 };
362
363 void Text::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
364 {
365         if (text.length() > 0)
366                 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
367 }
368
369 class ListBox: public Element
370 //class ListBox: public Window
371 {
372         public:
373 //              ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
374                 ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);//: Window(x, y, w, h),
375 //              windowPtr(0), cursor(0), limit(0), charWidth((w / 8) - 1), charHeight(h / 8),
376 //              elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
377 //              downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox) {}
378                 virtual void HandleKey(SDLKey key);
379                 virtual void HandleMouseMove(uint32 x, uint32 y);
380                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
381                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
382                 virtual void Notify(Element * e);
383                 void SetNotificationElement(Element * e) { elementToTell = e; }
384                 void AddItem(string s);
385                 string GetSelectedItem(void);
386
387         protected:
388                 bool thumbClicked;
389                 uint32 windowPtr, cursor, limit;
390                 uint32 charWidth, charHeight;                           // Box width/height in characters
391                 Element * elementToTell;
392                 Button upArrow, downArrow, upArrow2;
393                 vector<string> item;
394
395         private:
396                 uint32 yRelativePoint;
397 };
398
399 ListBox::ListBox(uint32 x, uint32 y, uint32 w, uint32 h): Element(x, y, w, h),
400         thumbClicked(false), windowPtr(0), cursor(0), limit(0), charWidth((w / 8) - 1),
401         charHeight(h / 8), elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
402         downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox)
403 {
404         upArrow.SetNotificationElement(this);
405         downArrow.SetNotificationElement(this);
406         upArrow2.SetNotificationElement(this);
407         extents.w -= 8;                                                                 // Make room for scrollbar...
408 }
409
410 void ListBox::HandleKey(SDLKey key)
411 {
412         if (key == SDLK_DOWN)
413         {
414                 if (cursor != limit - 1)        // Cursor is within its window
415                         cursor++;
416                 else                                            // Otherwise, scroll the window...
417                 {
418                         if (cursor + windowPtr != item.size() - 1)
419                                 windowPtr++;
420                 }
421         }
422         else if (key == SDLK_UP)
423         {
424                 if (cursor != 0)
425                         cursor--;
426                 else
427                 {
428                         if (windowPtr != 0)
429                                 windowPtr--;
430                 }
431         }
432         else if (key == SDLK_PAGEDOWN)
433         {
434                 if (cursor != limit - 1)
435                         cursor = limit - 1;
436                 else
437                 {
438                         windowPtr += limit;
439                         if (windowPtr > item.size() - limit)
440                                 windowPtr = item.size() - limit;
441                 }
442         }
443         else if (key == SDLK_PAGEUP)
444         {
445                 if (cursor != 0)
446                         cursor = 0;
447                 else
448                 {
449                         if (windowPtr < limit)
450                                 windowPtr = 0;
451                         else
452                                 windowPtr -= limit;
453                 }
454         }
455 //How to handle these???
456 /*      if (key == SDLK_RETURN)
457                 done = true;
458         if (key == SDLK_ESCAPE)
459         {
460                 WriteLog("GUI: Aborting VJ by user request.\n");
461                 return false;                                           // Bail out!
462         }*/
463         else if (key >= SDLK_a && key <= SDLK_z)
464         {
465                 // Advance cursor to filename with first letter pressed...
466                 uint8 which = (key - SDLK_a) + 65;      // Convert key to A-Z char
467
468                 for(uint32 i=0; i<item.size(); i++)
469                 {
470                         if ((item[i][0] & 0xDF) == which)
471                         {
472                                 cursor = i - windowPtr;
473                                 if (i > windowPtr + limit - 1)
474                                         windowPtr = i - limit + 1,
475                                         cursor = limit - 1;
476                                 if (i < windowPtr)
477                                         windowPtr = i,
478                                         cursor = 0;
479                                 break;
480                         }
481                 }
482         }
483 }
484
485 void ListBox::HandleMouseMove(uint32 x, uint32 y)
486 {
487         upArrow.HandleMouseMove(x - extents.x, y - extents.y);
488         downArrow.HandleMouseMove(x - extents.x, y - extents.y);
489         upArrow2.HandleMouseMove(x - extents.x, y - extents.y);
490
491         if (thumbClicked)
492         {
493                 uint32 sbHeight = extents.h - 24,
494                         thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
495                         thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
496
497 //yRelativePoint is the spot on the thumb where we clicked...
498 //              int32 thumbDelta = y - yRelativePoint;
499                 int32 newThumbStart = y - yRelativePoint;
500
501                 if (newThumbStart < 0)
502                         newThumbStart = 0;
503
504                 if (newThumbStart > sbHeight - thumb)
505                         newThumbStart = sbHeight - thumb;
506
507                 windowPtr = (uint32)(((float)newThumbStart / (float)sbHeight) * (float)item.size());
508 //Check for cursor bounds as well... Or do we need to???
509         }
510 }
511
512 void ListBox::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
513 {
514         if (Inside(x, y) && mouseDown)
515         {
516                 // Why do we have to do this??? (- extents.y?)
517                 // I guess it's because only the Window class has offsetting implemented...
518                 cursor = (y - extents.y) / 8;
519         }
520
521         // Check for a hit on the scrollbar...
522         if (x > (extents.x + extents.w) && x <= (extents.x + extents.w + 8)
523                 && y > (extents.y + 8) && y <= (extents.y + extents.h - 16))
524         {
525                 if (mouseDown)
526                 {
527 // This shiaut should be calculated in AddItem(), not here... (or in Draw() for that matter)
528                         uint32 sbHeight = extents.h - 24,
529                                 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
530                                 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
531
532                         // Did we hit the thumb?
533                         if (y >= (extents.y + 8 + thumbStart) && y < (extents.y + 8 + thumbStart + thumb))
534                                 thumbClicked = true, yRelativePoint = y - thumbStart;
535                 }
536                 else
537                         thumbClicked = false;
538         }
539
540         upArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
541         downArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
542         upArrow2.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
543 }
544
545 void ListBox::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
546 {
547         for(uint32 i=0; i<limit; i++)
548         {
549                 // Strip off the extension
550                 // (extension stripping should be an option, not default!)
551                 string s(item[windowPtr + i], 0, item[windowPtr + i].length() - 4);
552                 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*8,
553                         (cursor == i ? true : false), "%-*.*s", charWidth, charWidth, s.c_str());
554         }
555
556         upArrow.Draw(extents.x + offsetX, extents.y + offsetY);
557         downArrow.Draw(extents.x + offsetX, extents.y + offsetY);
558         upArrow2.Draw(extents.x + offsetX, extents.y + offsetY);
559
560         uint32 sbHeight = extents.h - 24,
561                 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
562                 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
563
564         for(uint32 y=extents.y+offsetY+8; y<extents.y+offsetY+extents.h-16; y++)
565         {
566 //              for(uint32 x=extents.x+offsetX+extents.w-8; x<extents.x+offsetX+extents.w; x++)
567                 for(uint32 x=extents.x+offsetX+extents.w; x<extents.x+offsetX+extents.w+8; x++)
568                 {
569                         if (y >= thumbStart + (extents.y+offsetY+8) && y < thumbStart + thumb + (extents.y+offsetY+8))
570                                 screenBuffer[x + (y * pitch)] = (thumbClicked ? 0x458E : 0xFFFF);
571                         else
572                                 screenBuffer[x + (y * pitch)] = 0x0200;
573                 }
574         }
575 }
576
577 void ListBox::Notify(Element * e)
578 {
579         if (e == &upArrow || e == &upArrow2)
580         {
581                 if (windowPtr != 0)
582                 {
583                         windowPtr--;
584
585                         if (cursor < limit - 1)
586                                 cursor++;
587                 }
588         }
589         else if (e == &downArrow)
590         {
591                 if (windowPtr < item.size() - limit)
592                 {
593                         windowPtr++;
594
595                         if (cursor != 0)
596                                 cursor--;
597                 }
598         }
599 }
600
601 void ListBox::AddItem(string s)
602 {
603         item.push_back(s);
604         limit = (item.size() > charHeight ? charHeight : item.size());
605 //WriteLog("ListBox: Adding item [%s], limit = %u...\n", s.c_str(), limit);
606
607         //Do this *every* time?
608         //What other choice is there? :-p
609         sort(item.begin(), item.end());
610 }
611
612 string ListBox::GetSelectedItem(void)
613 {
614         return item[windowPtr + cursor];
615 }
616
617 class FileList: public Window
618 {
619         public:
620                 FileList(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);
621                 virtual ~FileList() {}
622                 virtual void HandleKey(SDLKey key);
623                 virtual void HandleMouseMove(uint32 x, uint32 y) { Window::HandleMouseMove(x, y); }
624                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) { Window::HandleMouseButton(x, y, mouseDown); }
625                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) { Window::Draw(offsetX, offsetY); }
626                 virtual void Notify(Element * e);
627
628         protected:
629                 ListBox * files;
630                 Button * load;
631 };
632
633 //Need 4 buttons, one scrollbar...
634 FileList::FileList(uint32 x, uint32 y, uint32 w, uint32 h): Window(x, y, w, h)
635 {
636         files = new ListBox(8, 8, w - 16, h - 32);
637         AddElement(files);
638         load = new Button(8, h - 16, " Load ");
639         AddElement(load);
640         load->SetNotificationElement(this);
641
642 //      DIR * dp = opendir(path);
643         DIR * dp = opendir(vjs.ROMPath);
644         dirent * de;
645
646         while ((de = readdir(dp)) != NULL)
647         {
648                 char * ext = strrchr(de->d_name, '.');
649
650                 if (ext != NULL)
651                         if (stricmp(ext, ".zip") == 0 || stricmp(ext, ".jag") == 0)
652                                 files->AddItem(string(de->d_name));
653         }
654
655         closedir(dp);
656 }
657
658 void FileList::HandleKey(SDLKey key)
659 {
660         if (key == SDLK_RETURN)
661                 Notify(load);
662         else
663                 Window::HandleKey(key);
664 }
665
666 void FileList::Notify(Element * e)
667 {
668         if (e == load)
669         {
670                 char filename[MAX_PATH];
671                 strcpy(filename, vjs.ROMPath);
672
673                 if (strlen(filename) > 0)
674                         if (filename[strlen(filename) - 1] != '/')
675                                 strcat(filename, "/");
676
677                 strcat(filename, files->GetSelectedItem().c_str());
678
679                 uint32 romSize = JaguarLoadROM(jaguar_mainRom, filename);
680
681                 if (romSize == 0)
682 //We need better error checking here... !!! FIX !!!
683                         WriteLog("VJ: Could not load ROM from file \"%s\"...", files->GetSelectedItem().c_str());
684                 else
685                 {
686                         jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, romSize);
687                         WriteLog("CRC: %08X\n", (unsigned int)jaguar_mainRom_crc32);
688                         eeprom_init();
689
690                         SDL_Event event;
691                         event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
692                         SDL_PushEvent(&event);
693
694                         event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
695                         event.user.data1 = (void *)ResetJaguar;
696                     SDL_PushEvent(&event);
697                 }
698         }
699         else
700                 Window::Notify(e);
701 }
702
703
704 struct NameAction
705 {
706         string name;
707         Window * (* action)(void);
708         SDLKey hotKey;
709
710         NameAction(string n, Window * (* a)(void) = NULL, SDLKey k = SDLK_UNKNOWN): name(n),
711                 action(a), hotKey(k) {}
712 };
713
714 class MenuItems
715 {
716         public:
717                 MenuItems(): charLength(0) {}
718                 bool Inside(uint32 x, uint32 y)
719                 { return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
720                 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false); }
721
722                 string title;
723                 vector<NameAction> item;
724                 uint32 charLength;
725                 SDL_Rect extents;
726 };
727
728 class Menu: public Element
729 {
730         public:
731                 Menu(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 8,
732                         uint16 fgc = 0x1CFF, uint16 bgc = 0x000F, uint16 fgch = 0x421F,
733                         uint16 bgch = 0x1CFF): Element(x, y, w, h), activated(false), clicked(false),
734                         inside(0), insidePopup(0), fgColor(fgc), bgColor(bgc), fgColorHL(fgch),
735                         bgColorHL(bgch), menuChosen(-1), menuItemChosen(-1) {}
736                 virtual void HandleKey(SDLKey key);
737                 virtual void HandleMouseMove(uint32 x, uint32 y);
738                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
739                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
740                 virtual void Notify(Element *) {}
741                 void Add(MenuItems mi);
742
743         protected:
744                 bool activated, clicked;
745                 uint32 inside, insidePopup;
746                 uint16 fgColor, bgColor, fgColorHL, bgColorHL;
747                 int menuChosen, menuItemChosen;
748
749         private:
750                 vector<MenuItems> itemList;
751 };
752
753 void Menu::HandleKey(SDLKey key)
754 {
755         for(uint32 i=0; i<itemList.size(); i++)
756         {
757                 for(uint32 j=0; j<itemList[i].item.size(); j++)
758                 {
759                         if (itemList[i].item[j].hotKey == key)
760                         {
761                                 SDL_Event event;
762                                 event.type = SDL_USEREVENT;
763                                 event.user.code = MENU_ITEM_CHOSEN;
764                                 event.user.data1 = (void *)itemList[i].item[j].action;
765                         SDL_PushEvent(&event);
766
767                                 clicked = false, menuChosen = menuItemChosen = -1;
768                                 break;
769                         }
770                 }
771         }
772 }
773
774 void Menu::HandleMouseMove(uint32 x, uint32 y)
775 {
776         inside = insidePopup = 0;
777
778         if (Inside(x, y))
779         {
780                 // Find out *where* we are inside the menu bar
781                 uint32 xpos = extents.x;
782
783                 for(uint32 i=0; i<itemList.size(); i++)
784                 {
785                         uint32 width = (itemList[i].title.length() + 2) * 8;
786
787                         if (x >= xpos && x < xpos + width)
788                         {
789                                 inside = i + 1;
790                                 menuChosen = i;
791                                 break;
792                         }
793
794                         xpos += width;
795                 }
796         }
797
798         if (!Inside(x, y) && !clicked)
799         {
800                 menuChosen = -1;
801         }
802
803         if (itemList[menuChosen].Inside(x, y) && clicked)
804         {
805                 insidePopup = ((y - itemList[menuChosen].extents.y) / 8) + 1;
806                 menuItemChosen = insidePopup - 1;
807         }
808 }
809
810 void Menu::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
811 {
812         if (!clicked)
813         {
814                 if (mouseDown)
815                 {
816                         if (inside)
817                                 clicked = true;
818                         else
819                                 menuChosen = -1;                                        // clicked is already false...!
820                 }
821         }
822         else                                                                                    // clicked == true
823         {
824                 if (insidePopup && !mouseDown)                          // I.e., mouse-button-up
825                 {
826                         activated = true;
827                         if (itemList[menuChosen].item[menuItemChosen].action != NULL)
828                         {
829 //                              itemList[menuChosen].item[menuItemChosen].action();
830                                 SDL_Event event;
831                                 event.type = SDL_USEREVENT;
832                                 event.user.code = MENU_ITEM_CHOSEN;
833                                 event.user.data1 = (void *)itemList[menuChosen].item[menuItemChosen].action;
834                             SDL_PushEvent(&event);
835
836                                 clicked = false, menuChosen = menuItemChosen = -1;
837
838 /*                              SDL_Event event;
839                                 while (SDL_PollEvent(&event));          // Flush the event queue...
840                                 event.type = SDL_MOUSEMOTION;
841                                 int mx, my;
842                                 SDL_GetMouseState(&mx, &my);
843                                 event.motion.x = mx, event.motion.y = my;
844                             SDL_PushEvent(&event);                              // & update mouse position...!
845 */                      }
846                 }
847
848                 if (!inside && !insidePopup && mouseDown)
849                         clicked = false, menuChosen = menuItemChosen = -1;
850         }
851 }
852
853 void Menu::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
854 {
855         uint32 xpos = extents.x + offsetX;
856
857         for(uint32 i=0; i<itemList.size(); i++)
858         {
859                 uint16 color1 = fgColor, color2 = bgColor;
860                 if (inside == (i + 1) || (menuChosen != -1 && (uint32)menuChosen == i))
861                         color1 = fgColorHL, color2 = bgColorHL;
862
863                 DrawStringOpaque(screenBuffer, xpos, extents.y + offsetY, color1, color2,
864                         " %s ", itemList[i].title.c_str());
865                 xpos += (itemList[i].title.length() + 2) * 8;
866         }
867
868         // Draw sub menu (but only if active)
869         if (clicked)
870         {
871                 uint32 ypos = extents.y + 9;
872
873                 for(uint32 i=0; i<itemList[menuChosen].item.size(); i++)
874                 {
875                         uint16 color1 = fgColor, color2 = bgColor;
876
877                         if (insidePopup == i + 1)
878                                 color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
879
880                         if (itemList[menuChosen].item[i].name.length() > 0)
881                                 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
882                                         color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
883                                         itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
884                         else
885                                 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
886                                         fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
887
888                         ypos += 8;
889                 }
890         }
891 }
892
893 void Menu::Add(MenuItems mi)
894 {
895         for(uint32 i=0; i<mi.item.size(); i++)
896                 if (mi.item[i].name.length() > mi.charLength)
897                         mi.charLength = mi.item[i].name.length();
898
899         // Set extents here as well...
900         mi.extents.x = extents.x + extents.w, mi.extents.y = extents.y + 9;
901         mi.extents.w = (mi.charLength + 2) * 8, mi.extents.h = mi.item.size() * 8;
902
903         itemList.push_back(mi);
904         extents.w += (mi.title.length() + 2) * 8;
905 }
906
907 //Do we even *need* this?
908 class RootWindow: public Window
909 {
910         public:
911                 RootWindow(Menu * m, Window * w = NULL): menu(m), window(w) {}
912 //Do we even need to care about this crap?
913 //                      { extents.x = extents.y = 0, extents.w = 320, extents.h = 240; }
914                 virtual void HandleKey(SDLKey key) {}
915                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
916                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
917                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) {}
918                 virtual void Notify(Element *) {}
919
920         private:
921                 Menu * menu;
922                 Window * window;
923                 int16 * rootImage[1280 * 240 * 2];
924 };
925
926
927
928 //
929 // GUI stuff--it's not crunchy, it's GUI! ;-)
930 //
931
932 void InitGUI(void)
933 {
934         SDL_ShowCursor(SDL_DISABLE);
935         SDL_GetMouseState(&mouseX, &mouseY);
936 }
937
938 void GUIDone(void)
939 {
940 }
941
942 //
943 // Draw text at the given x/y coordinates. Can invert text as well.
944 //
945 void DrawString(int16 * screen, uint32 x, uint32 y, bool invert, const char * text, ...)
946 {
947         char string[4096];
948         va_list arg;
949
950         va_start(arg, text);
951         vsprintf(string, text, arg);
952         va_end(arg);
953
954         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
955         uint32 length = strlen(string), address = x + (y * pitch);
956
957         for(uint32 i=0; i<length; i++)
958         {
959                 uint32 fontAddr = (uint32)string[i] * 64;
960
961                 for(uint32 yy=0; yy<8; yy++)
962                 {
963                         for(uint32 xx=0; xx<8; xx++)
964                         {
965                                 if ((font1[fontAddr] && !invert) || (!font1[fontAddr] && invert))
966                                         *(screen + address + xx + (yy * pitch)) = 0xFE00;
967                                 fontAddr++;
968                         }
969                 }
970
971                 address += 8;
972         }
973 }
974
975 //
976 // Draw text at the given x/y coordinates, using FG/BG colors.
977 //
978 void DrawStringOpaque(int16 * screen, uint32 x, uint32 y, uint16 color1, uint16 color2, const char * text, ...)
979 {
980         char string[4096];
981         va_list arg;
982
983         va_start(arg, text);
984         vsprintf(string, text, arg);
985         va_end(arg);
986
987         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
988         uint32 length = strlen(string), address = x + (y * pitch);
989
990         for(uint32 i=0; i<length; i++)
991         {
992                 uint32 fontAddr = (uint32)string[i] * 64;
993
994                 for(uint32 yy=0; yy<8; yy++)
995                 {
996                         for(uint32 xx=0; xx<8; xx++)
997                         {
998                                 *(screen + address + xx + (yy * pitch)) = (font1[fontAddr] ? color1 : color2);
999                                 fontAddr++;
1000                         }
1001                 }
1002
1003                 address += 8;
1004         }
1005 }
1006
1007 //
1008 // Draw text at the given x/y coordinates with transparency (0 is fully opaque, 32 is fully transparent).
1009 //
1010 void DrawStringTrans(int16 * screen, uint32 x, uint32 y, uint16 color, uint8 trans, const char * text, ...)
1011 {
1012         char string[4096];
1013         va_list arg;
1014
1015         va_start(arg, text);
1016         vsprintf(string, text, arg);
1017         va_end(arg);
1018
1019         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
1020         uint32 length = strlen(string), address = x + (y * pitch);
1021
1022         for(uint32 i=0; i<length; i++)
1023         {
1024                 uint32 fontAddr = (uint32)string[i] * 64;
1025
1026                 for(uint32 yy=0; yy<8; yy++)
1027                 {
1028                         for(uint32 xx=0; xx<8; xx++)
1029                         {
1030                                 if (font1[fontAddr])
1031                                 {
1032                                         uint16 existingColor = *(screen + address + xx + (yy * pitch));
1033
1034                                         uint8 eRed = (existingColor >> 10) & 0x1F,
1035                                                 eGreen = (existingColor >> 5) & 0x1F,
1036                                                 eBlue = existingColor & 0x1F,
1037 //This could be done ahead of time, instead of on each pixel...
1038                                                 nRed = (color >> 10) & 0x1F,
1039                                                 nGreen = (color >> 5) & 0x1F,
1040                                                 nBlue = color & 0x1F;
1041
1042 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
1043 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
1044 //because dividing by 32 is faster than dividing by 31...!
1045                                         uint8 invTrans = 32 - trans;
1046                                         uint16 bRed = (eRed * trans + nRed * invTrans) / 32;
1047                                         uint16 bGreen = (eGreen * trans + nGreen * invTrans) / 32;
1048                                         uint16 bBlue = (eBlue * trans + nBlue * invTrans) / 32;
1049
1050                                         uint16 blendedColor = (bRed << 10) | (bGreen << 5) | bBlue;
1051
1052                                         *(screen + address + xx + (yy * pitch)) = blendedColor;
1053                                 }
1054
1055                                 fontAddr++;
1056                         }
1057                 }
1058
1059                 address += 8;
1060         }
1061 }
1062
1063 //
1064 // GUI Main loop
1065 //
1066 bool GUIMain(void)
1067 {
1068         extern int16 * backbuffer;
1069         bool done = false;
1070         SDL_Event event;
1071         Window * mainWindow = NULL;
1072
1073         // Set up the GUI classes...
1074         Element::SetScreenAndPitch(backbuffer, GetSDLScreenPitch() / 2);
1075
1076         Menu mainMenu;
1077         MenuItems mi;
1078         mi.title = "File";
1079         mi.item.push_back(NameAction("Load...", LoadROM, SDLK_l));
1080         mi.item.push_back(NameAction("Reset", ResetJaguar));
1081         mi.item.push_back(NameAction("Run", RunEmu, SDLK_ESCAPE));
1082         mi.item.push_back(NameAction(""));
1083         mi.item.push_back(NameAction("Quit", Quit, SDLK_q));
1084         mainMenu.Add(mi);
1085         mi.title = "Settings";
1086         mi.item.clear();
1087         mi.item.push_back(NameAction("Video..."));
1088         mi.item.push_back(NameAction("Audio..."));
1089         mi.item.push_back(NameAction("Misc..."));
1090         mainMenu.Add(mi);
1091         mi.title = "Info";
1092         mi.item.clear();
1093         mi.item.push_back(NameAction("About...", About));
1094         mainMenu.Add(mi);
1095
1096         bool showMouse = true;
1097
1098 //This is crappy!!! !!! FIX !!!
1099         jaguar_reset();
1100
1101         // Set up our background save...
1102         memset(background, 0x11, tom_getVideoModeWidth() * 240 * 2);
1103
1104         while (!done)
1105         {
1106                 while (SDL_PollEvent(&event))
1107                 {
1108                         if (event.type == SDL_USEREVENT)
1109                         {
1110                                 if (event.user.code == WINDOW_CLOSE)
1111                                 {
1112                                         delete mainWindow;
1113                                         mainWindow = NULL;
1114                                 }
1115                                 else if (event.user.code == MENU_ITEM_CHOSEN)
1116                                 {
1117                                         // Confused? Let me enlighten... What we're doing here is casting
1118                                         // data1 as a pointer to a function which returns a Window pointer and
1119                                         // which takes no parameters (the "(Window *(*)(void))" part), then
1120                                         // derefencing it (the "*" in front of that) in order to call the
1121                                         // function that it points to. Clear as mud? Yeah, I hate function
1122                                         // pointers too, but what else are you gonna do?
1123                                         mainWindow = (*(Window *(*)(void))event.user.data1)();
1124
1125                                         while (SDL_PollEvent(&event));  // Flush the event queue...
1126                                         event.type = SDL_MOUSEMOTION;
1127                                         int mx, my;
1128                                         SDL_GetMouseState(&mx, &my);
1129                                         event.motion.x = mx, event.motion.y = my;
1130                                     SDL_PushEvent(&event);                      // & update mouse position...!
1131
1132                                         mouseX = mx, mouseY = my;               // This prevents "mouse flash"...
1133                                         if (vjs.useOpenGL)
1134                                                 mouseX /= 2, mouseY /= 2;
1135                                 }
1136                         }
1137                         else if (event.type == SDL_ACTIVEEVENT)
1138                         {
1139                                 if (event.active.state == SDL_APPMOUSEFOCUS)
1140                                         showMouse = (event.active.gain ? true : false);
1141                         }
1142                         else if (event.type == SDL_KEYDOWN)
1143                         {
1144                                 if (mainWindow)
1145                                         mainWindow->HandleKey(event.key.keysym.sym);
1146                                 else
1147                                         mainMenu.HandleKey(event.key.keysym.sym);
1148                         }
1149                         else if (event.type == SDL_MOUSEMOTION)
1150                         {
1151                                 mouseX = event.motion.x, mouseY = event.motion.y;
1152
1153                                 if (vjs.useOpenGL)
1154                                         mouseX /= 2, mouseY /= 2;
1155
1156                                 if (mainWindow)
1157                                         mainWindow->HandleMouseMove(mouseX, mouseY);
1158                                 else
1159                                         mainMenu.HandleMouseMove(mouseX, mouseY);
1160                         }
1161                         else if (event.type == SDL_MOUSEBUTTONDOWN)
1162                         {
1163                                 uint32 mx = event.button.x, my = event.button.y;
1164
1165                                 if (vjs.useOpenGL)
1166                                         mx /= 2, my /= 2;
1167
1168                                 if (mainWindow)
1169                                         mainWindow->HandleMouseButton(mx, my, true);
1170                                 else
1171                                         mainMenu.HandleMouseButton(mx, my, true);
1172                         }
1173                         else if (event.type == SDL_MOUSEBUTTONUP)
1174                         {
1175                                 uint32 mx = event.button.x, my = event.button.y;
1176
1177                                 if (vjs.useOpenGL)
1178                                         mx /= 2, my /= 2;
1179
1180                                 if (mainWindow)
1181                                         mainWindow->HandleMouseButton(mx, my, false);
1182                                 else
1183                                         mainMenu.HandleMouseButton(mx, my, false);
1184                         }
1185
1186                         // Draw the GUI...
1187 // The way we do things here is kinda stupid (redrawing the screen every frame), but
1188 // it's simple. Perhaps there may be a reason down the road to be more selective with
1189 // our clearing, but for now, this will suffice.
1190 //                      memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
1191                         memcpy(backbuffer, background, tom_getVideoModeWidth() * 240 * 2);
1192
1193                         mainMenu.Draw();
1194                         if (mainWindow)
1195                                 mainWindow->Draw();
1196
1197                         if (showMouse)
1198                                 DrawTransparentBitmap(backbuffer, mouseX, mouseY, mousePic);
1199
1200                         RenderBackbuffer();
1201                 }
1202         }
1203
1204         return true;
1205 }
1206
1207 //
1208 // GUI "action" functions
1209 //
1210 Window * LoadROM(void)
1211 {
1212         FileList * fileList = new FileList(8, 16, 304, 216);
1213
1214         return (Window *)fileList;
1215 }
1216
1217 Window * ResetJaguar(void)
1218 {
1219         jaguar_reset();
1220         return RunEmu();
1221 }
1222
1223 bool debounceRunKey = true;
1224 Window * RunEmu(void)
1225 {
1226 //This is crappy... !!! FIX !!!
1227         extern int16 * backbuffer;
1228         extern bool finished;
1229         extern bool showGUI;
1230         uint32 nFrame = 0, nFrameskip = 0;
1231         uint32 totalFrames = 0;
1232         finished = false;
1233         bool showMessage = true;
1234         uint32 showMsgFrames = 60;
1235         uint8 transparency = 0;
1236         // Pass a message to the "joystick" code to debounce the ESC key...
1237         debounceRunKey = true;
1238
1239         while (!finished)
1240         {
1241                 // Set up new backbuffer with new pixels and data
1242                 JaguarExecute(backbuffer, true);
1243                 totalFrames++;
1244 //WriteLog("Frame #%u...\n", totalFrames);
1245 //extern bool doDSPDis;
1246 //if (totalFrames == 373)
1247 //      doDSPDis = true;
1248
1249 //This sucks... !!! FIX !!!
1250                 joystick_exec();
1251                 if (finished)
1252                         break;
1253
1254                 // Some QnD GUI stuff here...
1255                 if (showGUI)
1256                 {
1257                         extern uint32 gpu_pc, dsp_pc;
1258                         DrawString(backbuffer, 8, 8, false, "GPU PC: %08X", gpu_pc);
1259                         DrawString(backbuffer, 8, 16, false, "DSP PC: %08X", dsp_pc);
1260                 }
1261
1262                 if (showMessage)
1263                 {
1264                         DrawStringTrans(backbuffer, 8, 24*8, 0xFF0F, transparency, "Running...");
1265
1266                         if (showMsgFrames == 0)
1267                         {                       
1268                                 transparency++;
1269
1270                                 if (transparency == 33)
1271                                         showMessage = false;
1272                         }
1273                         else
1274                                 showMsgFrames--;
1275                 }
1276
1277                 // Simple frameskip
1278                 if (nFrame == nFrameskip)
1279                 {
1280                         RenderBackbuffer();
1281                         nFrame = 0;
1282                 }
1283                 else
1284                         nFrame++;
1285         }
1286
1287         // Reset the pitch, since it may have been changed in-game...
1288         Element::SetScreenAndPitch(backbuffer, GetSDLScreenPitch() / 2);
1289
1290         // Save the background for the GUI...
1291 //      memcpy(background, backbuffer, tom_getVideoModeWidth() * 240 * 2);
1292         // In this case, we squash the color to monochrome, then force it to blue + green...
1293         for(uint32 i=0; i<tom_getVideoModeWidth() * 240; i++)
1294         {
1295                 uint16 word = backbuffer[i];
1296                 uint8 r = (word >> 10) & 0x1F, g = (word >> 5) & 0x1F, b = word & 0x1F;
1297                 word = ((r + g + b) / 3) & 0x001F;
1298                 word = (word << 5) | word;
1299                 background[i] = word;
1300         }
1301
1302         return NULL;
1303 }
1304
1305 Window * Quit(void)
1306 {
1307         WriteLog("GUI: Quitting due to user request.\n");
1308         log_done();
1309         exit(0);
1310
1311         return NULL;                                                                    // We never get here...
1312 }
1313
1314 Window * About(void)
1315 {
1316         Window * window = new Window(8, 16, 304, 160);
1317         window->AddElement(new Text(8, 8, "Virtual Jaguar 1.0.7"));
1318         window->AddElement(new Text(8, 24, "Coders:"));
1319         window->AddElement(new Text(16, 32, "Niels Wagenaar (nwagenaar)"));
1320         window->AddElement(new Text(16, 40, "Carwin Jones (Caz)"));
1321         window->AddElement(new Text(16, 48, "James L. Hammons (shamus)"));
1322         window->AddElement(new Text(16, 56, "Adam Green"));
1323
1324         return window;
1325 }
1326
1327 //
1328 // Draw "picture"
1329 // Uses zero as transparent color
1330 //
1331 void DrawTransparentBitmap(int16 * screen, uint32 x, uint32 y, uint16 * bitmap)
1332 {
1333         uint16 width = bitmap[0], height = bitmap[1];
1334         bitmap += 2;
1335
1336         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
1337         uint32 address = x + (y * pitch);
1338
1339         for(int yy=0; yy<height; yy++)
1340         {
1341                 for(int xx=0; xx<width; xx++)
1342                 {
1343                                 if (*bitmap && x + xx < pitch)          // NOTE: Still doesn't clip the Y val...
1344                                         *(screen + address + xx + (yy * pitch)) = *bitmap;
1345                                 bitmap++;
1346                 }
1347         }
1348 }
1349
1350 //
1351 // Very very crude GUI file selector
1352 //
1353 /*bool UserSelectFile(char * path, char * filename)
1354 {
1355 //Testing...
1356 GUIMain();
1357         
1358         extern int16 * backbuffer;
1359         vector<string> fileList;
1360
1361         // Read in the candidate files from the directory pointed to by "path"
1362
1363         DIR * dp = opendir(path);
1364         dirent * de;
1365
1366         while ((de = readdir(dp)) != NULL)
1367         {
1368                 char * ext = strrchr(de->d_name, '.');
1369
1370                 if (ext != NULL)
1371                         if (stricmp(ext, ".zip") == 0 || stricmp(ext, ".jag") == 0)
1372                                 fileList.push_back(string(de->d_name));
1373         }
1374
1375         closedir(dp);
1376
1377         if (fileList.size() == 0)                                               // Any files found?
1378                 return false;                                                           // Nope. Bail!
1379
1380         // Main GUI selection loop
1381
1382         uint32 cursor = 0, startFile = 0;
1383
1384         if (fileList.size() > 1)        // Only go GUI if more than one possibility!
1385         {
1386                 sort(fileList.begin(), fileList.end());
1387
1388                 bool done = false;
1389                 uint32 limit = (fileList.size() > 30 ? 30 : fileList.size());
1390                 SDL_Event event;
1391
1392                 // Ensure that the GUI is drawn before any user input...
1393                 event.type = SDL_USEREVENT;
1394                 SDL_PushEvent(&event);
1395
1396                 while (!done)
1397                 {
1398                         while (SDL_PollEvent(&event))
1399                         {
1400                                 if (event.type == SDL_KEYDOWN)
1401                                 {
1402                                         SDLKey key = event.key.keysym.sym;
1403
1404                                         if (key == SDLK_DOWN)
1405                                         {
1406                                                 if (cursor != limit - 1)        // Cursor is within its window
1407                                                         cursor++;
1408                                                 else                                            // Otherwise, scroll the window...
1409                                                 {
1410                                                         if (cursor + startFile != fileList.size() - 1)
1411                                                                 startFile++;
1412                                                 }
1413                                         }
1414                                         if (key == SDLK_UP)
1415                                         {
1416                                                 if (cursor != 0)
1417                                                         cursor--;
1418                                                 else
1419                                                 {
1420                                                         if (startFile != 0)
1421                                                                 startFile--;
1422                                                 }
1423                                         }
1424                                         if (key == SDLK_PAGEDOWN)
1425                                         {
1426                                                 if (cursor != limit - 1)
1427                                                         cursor = limit - 1;
1428                                                 else
1429                                                 {
1430                                                         startFile += limit;
1431                                                         if (startFile > fileList.size() - limit)
1432                                                                 startFile = fileList.size() - limit;
1433                                                 }
1434                                         }
1435                                         if (key == SDLK_PAGEUP)
1436                                         {
1437                                                 if (cursor != 0)
1438                                                         cursor = 0;
1439                                                 else
1440                                                 {
1441                                                         if (startFile < limit)
1442                                                                 startFile = 0;
1443                                                         else
1444                                                                 startFile -= limit;
1445                                                 }
1446                                         }
1447                                         if (key == SDLK_RETURN)
1448                                                 done = true;
1449                                         if (key == SDLK_ESCAPE)
1450                                         {
1451                                                 WriteLog("GUI: Aborting VJ by user request.\n");
1452                                                 return false;                                           // Bail out!
1453                                         }
1454                                         if (key >= SDLK_a && key <= SDLK_z)
1455                                         {
1456                                                 // Advance cursor to filename with first letter pressed...
1457                                                 uint8 which = (key - SDLK_a) + 65;      // Convert key to A-Z char
1458
1459                                                 for(uint32 i=0; i<fileList.size(); i++)
1460                                                 {
1461                                                         if ((fileList[i][0] & 0xDF) == which)
1462                                                         {
1463                                                                 cursor = i - startFile;
1464                                                                 if (i > startFile + limit - 1)
1465                                                                         startFile = i - limit + 1,
1466                                                                         cursor = limit - 1;
1467                                                                 if (i < startFile)
1468                                                                         startFile = i,
1469                                                                         cursor = 0;
1470                                                                 break;
1471                                                         }
1472                                                 }
1473                                         }
1474                                 }
1475                                 else if (event.type == SDL_MOUSEMOTION)
1476                                 {
1477                                         mouseX = event.motion.x, mouseY = event.motion.y;
1478                                         if (vjs.useOpenGL)
1479                                                 mouseX /= 2, mouseY /= 2;
1480                                 }
1481                                 else if (event.type == SDL_MOUSEBUTTONDOWN)
1482                                 {
1483                                         uint32 mx = event.button.x, my = event.button.y;
1484                                         if (vjs.useOpenGL)
1485                                                 mx /= 2, my /= 2;
1486                                         cursor = my / 8;
1487                                 }
1488
1489                                 // Draw the GUI...
1490 //                              memset(backbuffer, 0x11, tom_getVideoModeWidth() * tom_getVideoModeHeight() * 2);
1491                                 memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
1492
1493                                 for(uint32 i=0; i<limit; i++)
1494                                 {
1495                                         // Clip our strings to guarantee that they fit on the screen...
1496                                         // (and strip off the extension too)
1497                                         string s(fileList[startFile + i], 0, fileList[startFile + i].length() - 4);
1498                                         if (s.length() > 38)
1499                                                 s[38] = 0;
1500                                         DrawString(backbuffer, 0, i*8, (cursor == i ? true : false), " %s ", s.c_str());
1501                                 }
1502
1503                                 DrawTransparentBitmap(backbuffer, mouseX, mouseY, mousePic);
1504
1505                                 RenderBackbuffer();
1506                         }
1507                 }
1508         }
1509
1510         strcpy(filename, path);
1511
1512         if (strlen(path) > 0)
1513                 if (path[strlen(path) - 1] != '/')
1514                         strcat(filename, "/");
1515
1516         strcat(filename, fileList[startFile + cursor].c_str());
1517
1518         return true;
1519 }*/
1520
1521 //
1522 // Generic ROM loading
1523 //
1524 uint32 JaguarLoadROM(uint8 * rom, char * path)
1525 {
1526         uint32 romSize = 0;
1527
1528         char * ext = strrchr(path, '.');
1529         if (ext != NULL)
1530         {
1531                 WriteLog("VJ: Loading \"%s\"...", path);
1532
1533                 if (stricmp(ext, ".zip") == 0)
1534                 {
1535                         // Handle ZIP file loading here...
1536                         WriteLog("(ZIPped)...");
1537
1538                         if (load_zipped_file(0, 0, path, NULL, &rom, &romSize) == -1)
1539                         {
1540                                 WriteLog("Failed!\n");
1541                                 return 0;
1542                         }
1543                 }
1544                 else
1545                 {
1546 /*                      FILE * fp = fopen(path, "rb");
1547
1548                         if (fp == NULL)
1549                         {
1550                                 WriteLog("Failed!\n");
1551                                 return 0;
1552                         }
1553
1554                         fseek(fp, 0, SEEK_END);
1555                         romSize = ftell(fp);
1556                         fseek(fp, 0, SEEK_SET);
1557                         fread(rom, 1, romSize, fp);
1558                         fclose(fp);*/
1559
1560                         gzFile fp = gzopen(path, "rb");
1561
1562                         if (fp == NULL)
1563                         {
1564                                 WriteLog("Failed!\n");
1565                                 return 0;
1566                         }
1567
1568                         romSize = gzfilelength(fp);
1569                         gzseek(fp, 0, SEEK_SET);
1570                         gzread(fp, rom, romSize);
1571                         gzclose(fp);
1572                 }
1573
1574                 WriteLog("OK (%i bytes)\n", romSize);
1575         }
1576
1577         return romSize;
1578 }
1579
1580 //
1581 // Jaguar cartridge ROM loading
1582 //
1583 void JaguarLoadCart(uint8 * mem, char * path)
1584 {
1585         uint32 romSize = JaguarLoadROM(mem, path);
1586
1587         if (romSize == 0)
1588         {
1589 /*              char newPath[2048];
1590                 WriteLog("VJ: Trying GUI...\n");
1591
1592 //This is not *nix friendly for some reason...
1593 //              if (!UserSelectFile(path, newPath))
1594                 if (!UserSelectFile((strlen(path) == 0 ? (char *)"." : path), newPath))
1595                 {
1596                         WriteLog("VJ: Could not find valid ROM in directory \"%s\"...\nAborting!\n", path);
1597                         log_done();
1598                         exit(0);
1599                 }
1600
1601                 romSize = JaguarLoadROM(mem, newPath);
1602 */
1603                 if (romSize == 0)
1604                 {
1605 //                      WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
1606                         WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", path);
1607                         log_done();
1608                         exit(0);
1609                 }
1610         }
1611
1612         jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, romSize);
1613         WriteLog("CRC: %08X\n", (unsigned int)jaguar_mainRom_crc32);
1614         eeprom_init();
1615 }
1616
1617 //
1618 // Get the length of a (possibly) gzipped file
1619 //
1620 int gzfilelength(gzFile gd)
1621 {
1622    int size = 0, length = 0;
1623    unsigned char buffer[0x10000];
1624
1625    gzrewind(gd);
1626
1627    do
1628    {
1629       // Read in chunks until EOF
1630       size = gzread(gd, buffer, 0x10000);
1631
1632       if (size <= 0)
1633         break;
1634
1635       length += size;
1636    }
1637    while (!gzeof(gd));
1638
1639    gzrewind(gd);
1640    return length;
1641 }