]> Shamusworld >> Repos - virtualjaguar/blob - src/gui.cpp
e8947ddd1117f7167e179158647549282c130a6f
[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                 uint32 windowPtr, cursor, limit;
389                 uint32 charWidth, charHeight;                           // Box width/height in characters
390                 Element * elementToTell;
391                 Button upArrow, downArrow, upArrow2;
392                 vector<string> item;
393 };
394
395 ListBox::ListBox(uint32 x, uint32 y, uint32 w, uint32 h): Element(x, y, w, h),
396         windowPtr(0), cursor(0), limit(0), charWidth((w / 8) - 1), charHeight(h / 8),
397         elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
398         downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox)
399 {
400         upArrow.SetNotificationElement(this);
401         downArrow.SetNotificationElement(this);
402         upArrow2.SetNotificationElement(this);
403         extents.w -= 8;                                                                 // Make room for scrollbar...
404 }
405
406 void ListBox::HandleKey(SDLKey key)
407 {
408         if (key == SDLK_DOWN)
409         {
410                 if (cursor != limit - 1)        // Cursor is within its window
411                         cursor++;
412                 else                                            // Otherwise, scroll the window...
413                 {
414                         if (cursor + windowPtr != item.size() - 1)
415                                 windowPtr++;
416                 }
417         }
418         else if (key == SDLK_UP)
419         {
420                 if (cursor != 0)
421                         cursor--;
422                 else
423                 {
424                         if (windowPtr != 0)
425                                 windowPtr--;
426                 }
427         }
428         else if (key == SDLK_PAGEDOWN)
429         {
430                 if (cursor != limit - 1)
431                         cursor = limit - 1;
432                 else
433                 {
434                         windowPtr += limit;
435                         if (windowPtr > item.size() - limit)
436                                 windowPtr = item.size() - limit;
437                 }
438         }
439         else if (key == SDLK_PAGEUP)
440         {
441                 if (cursor != 0)
442                         cursor = 0;
443                 else
444                 {
445                         if (windowPtr < limit)
446                                 windowPtr = 0;
447                         else
448                                 windowPtr -= limit;
449                 }
450         }
451 //How to handle these???
452 /*      if (key == SDLK_RETURN)
453                 done = true;
454         if (key == SDLK_ESCAPE)
455         {
456                 WriteLog("GUI: Aborting VJ by user request.\n");
457                 return false;                                           // Bail out!
458         }*/
459         else if (key >= SDLK_a && key <= SDLK_z)
460         {
461                 // Advance cursor to filename with first letter pressed...
462                 uint8 which = (key - SDLK_a) + 65;      // Convert key to A-Z char
463
464                 for(uint32 i=0; i<item.size(); i++)
465                 {
466                         if ((item[i][0] & 0xDF) == which)
467                         {
468                                 cursor = i - windowPtr;
469                                 if (i > windowPtr + limit - 1)
470                                         windowPtr = i - limit + 1,
471                                         cursor = limit - 1;
472                                 if (i < windowPtr)
473                                         windowPtr = i,
474                                         cursor = 0;
475                                 break;
476                         }
477                 }
478         }
479 }
480
481 void ListBox::HandleMouseMove(uint32 x, uint32 y)
482 {
483         upArrow.HandleMouseMove(x - extents.x, y - extents.y);
484         downArrow.HandleMouseMove(x - extents.x, y - extents.y);
485         upArrow2.HandleMouseMove(x - extents.x, y - extents.y);
486 }
487
488 void ListBox::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
489 {
490         if (Inside(x, y) && mouseDown)
491         {
492                 // Why do we have to do this??? (- extents.y?)
493                 // I guess it's because only the Window class has offsetting implemented...
494                 cursor = (y - extents.y) / 8;
495         }
496
497         upArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
498         downArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
499         upArrow2.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
500 }
501
502 void ListBox::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
503 {
504         for(uint32 i=0; i<limit; i++)
505         {
506                 // Strip off the extension
507                 // (extension stripping should be an option, not default!)
508                 string s(item[windowPtr + i], 0, item[windowPtr + i].length() - 4);
509                 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*8,
510                         (cursor == i ? true : false), "%-*.*s", charWidth, charWidth, s.c_str());
511         }
512
513         upArrow.Draw(extents.x + offsetX, extents.y + offsetY);
514         downArrow.Draw(extents.x + offsetX, extents.y + offsetY);
515         upArrow2.Draw(extents.x + offsetX, extents.y + offsetY);
516
517         uint32 sbHeight = extents.h - 24,
518                 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
519                 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
520
521         for(uint32 y=extents.y+offsetY+8; y<extents.y+offsetY+extents.h-16; y++)
522         {
523 //              for(uint32 x=extents.x+offsetX+extents.w-8; x<extents.x+offsetX+extents.w; x++)
524                 for(uint32 x=extents.x+offsetX+extents.w; x<extents.x+offsetX+extents.w+8; x++)
525                 {
526                         if (y >= thumbStart + (extents.y+offsetY+8) && y < thumbStart + thumb + (extents.y+offsetY+8))
527                                 screenBuffer[x + (y * pitch)] = 0xFFFF;
528                         else
529                                 screenBuffer[x + (y * pitch)] = 0x0000;
530                 }
531         }
532 }
533
534 void ListBox::Notify(Element * e)
535 {
536         if (e == &upArrow || e == &upArrow2)
537         {
538                 if (windowPtr != 0)
539                 {
540                         windowPtr--;
541
542                         if (cursor < limit - 1)
543                                 cursor++;
544                 }
545         }
546         else if (e == &downArrow)
547         {
548                 if (windowPtr < item.size() - limit)
549                 {
550                         windowPtr++;
551
552                         if (cursor != 0)
553                                 cursor--;
554                 }
555         }
556 }
557
558 void ListBox::AddItem(string s)
559 {
560         item.push_back(s);
561         limit = (item.size() > charHeight ? charHeight : item.size());
562 //WriteLog("ListBox: Adding item [%s], limit = %u...\n", s.c_str(), limit);
563
564         //Do this *every* time?
565         sort(item.begin(), item.end());
566 }
567
568 string ListBox::GetSelectedItem(void)
569 {
570         return item[windowPtr + cursor];
571 }
572
573 class FileList: public Window
574 {
575         public:
576                 FileList(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);
577                 virtual ~FileList() {}
578                 virtual void HandleKey(SDLKey key);
579                 virtual void HandleMouseMove(uint32 x, uint32 y) { Window::HandleMouseMove(x, y); }
580                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) { Window::HandleMouseButton(x, y, mouseDown); }
581                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) { Window::Draw(offsetX, offsetY); }
582                 virtual void Notify(Element * e);
583
584         protected:
585                 ListBox * files;
586                 Button * load;
587 };
588
589 //Need 4 buttons, one scrollbar...
590 FileList::FileList(uint32 x, uint32 y, uint32 w, uint32 h): Window(x, y, w, h)
591 {
592         files = new ListBox(8, 8, w - 16, h - 32);
593         AddElement(files);
594         load = new Button(8, h - 16, " Load ");
595         AddElement(load);
596         load->SetNotificationElement(this);
597
598 //      DIR * dp = opendir(path);
599         DIR * dp = opendir(vjs.ROMPath);
600         dirent * de;
601
602         while ((de = readdir(dp)) != NULL)
603         {
604                 char * ext = strrchr(de->d_name, '.');
605
606                 if (ext != NULL)
607                         if (stricmp(ext, ".zip") == 0 || stricmp(ext, ".jag") == 0)
608                                 files->AddItem(string(de->d_name));
609         }
610
611         closedir(dp);
612 }
613
614 void FileList::HandleKey(SDLKey key)
615 {
616         if (key == SDLK_RETURN)
617                 Notify(load);
618         else
619                 Window::HandleKey(key);
620 }
621
622 void FileList::Notify(Element * e)
623 {
624         if (e == load)
625         {
626                 char filename[MAX_PATH];
627                 strcpy(filename, vjs.ROMPath);
628
629                 if (strlen(filename) > 0)
630                         if (filename[strlen(filename) - 1] != '/')
631                                 strcat(filename, "/");
632
633                 strcat(filename, files->GetSelectedItem().c_str());
634
635                 uint32 romSize = JaguarLoadROM(jaguar_mainRom, filename);
636
637                 if (romSize == 0)
638 //We need better error checking here... !!! FIX !!!
639                         WriteLog("VJ: Could not load ROM from file \"%s\"...", files->GetSelectedItem().c_str());
640                 else
641                 {
642                         jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, romSize);
643                         WriteLog("CRC: %08X\n", (unsigned int)jaguar_mainRom_crc32);
644                         eeprom_init();
645
646                         SDL_Event event;
647                         event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
648                         SDL_PushEvent(&event);
649
650                         event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
651                         event.user.data1 = (void *)ResetJaguar;
652                     SDL_PushEvent(&event);
653                 }
654         }
655         else
656                 Window::Notify(e);
657 }
658
659
660 struct NameAction
661 {
662         string name;
663         Window * (* action)(void);
664         SDLKey hotKey;
665
666         NameAction(string n, Window * (* a)(void) = NULL, SDLKey k = SDLK_UNKNOWN): name(n),
667                 action(a), hotKey(k) {}
668 };
669
670 class MenuItems
671 {
672         public:
673                 MenuItems(): charLength(0) {}
674                 bool Inside(uint32 x, uint32 y)
675                 { return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
676                 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false); }
677
678                 string title;
679                 vector<NameAction> item;
680                 uint32 charLength;
681                 SDL_Rect extents;
682 };
683
684 class Menu: public Element
685 {
686         public:
687                 Menu(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 8,
688                         uint16 fgc = 0x1CFF, uint16 bgc = 0x000F, uint16 fgch = 0x421F,
689                         uint16 bgch = 0x1CFF): Element(x, y, w, h), activated(false), clicked(false),
690                         inside(0), insidePopup(0), fgColor(fgc), bgColor(bgc), fgColorHL(fgch),
691                         bgColorHL(bgch), menuChosen(-1), menuItemChosen(-1) {}
692                 virtual void HandleKey(SDLKey key);
693                 virtual void HandleMouseMove(uint32 x, uint32 y);
694                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
695                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
696                 virtual void Notify(Element *) {}
697                 void Add(MenuItems mi);
698
699         protected:
700                 bool activated, clicked;
701                 uint32 inside, insidePopup;
702                 uint16 fgColor, bgColor, fgColorHL, bgColorHL;
703                 int menuChosen, menuItemChosen;
704
705         private:
706                 vector<MenuItems> itemList;
707 };
708
709 void Menu::HandleKey(SDLKey key)
710 {
711         for(uint32 i=0; i<itemList.size(); i++)
712         {
713                 for(uint32 j=0; j<itemList[i].item.size(); j++)
714                 {
715                         if (itemList[i].item[j].hotKey == key)
716                         {
717                                 SDL_Event event;
718                                 event.type = SDL_USEREVENT;
719                                 event.user.code = MENU_ITEM_CHOSEN;
720                                 event.user.data1 = (void *)itemList[i].item[j].action;
721                         SDL_PushEvent(&event);
722
723                                 clicked = false, menuChosen = menuItemChosen = -1;
724                                 break;
725                         }
726                 }
727         }
728 }
729
730 void Menu::HandleMouseMove(uint32 x, uint32 y)
731 {
732         inside = insidePopup = 0;
733
734         if (Inside(x, y))
735         {
736                 // Find out *where* we are inside the menu bar
737                 uint32 xpos = extents.x;
738
739                 for(uint32 i=0; i<itemList.size(); i++)
740                 {
741                         uint32 width = (itemList[i].title.length() + 2) * 8;
742
743                         if (x >= xpos && x < xpos + width)
744                         {
745                                 inside = i + 1;
746                                 menuChosen = i;
747                                 break;
748                         }
749
750                         xpos += width;
751                 }
752         }
753
754         if (!Inside(x, y) && !clicked)
755         {
756                 menuChosen = -1;
757         }
758
759         if (itemList[menuChosen].Inside(x, y) && clicked)
760         {
761                 insidePopup = ((y - itemList[menuChosen].extents.y) / 8) + 1;
762                 menuItemChosen = insidePopup - 1;
763         }
764 }
765
766 void Menu::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
767 {
768         if (!clicked)
769         {
770                 if (mouseDown)
771                 {
772                         if (inside)
773                                 clicked = true;
774                         else
775                                 menuChosen = -1;                                        // clicked is already false...!
776                 }
777         }
778         else                                                                                    // clicked == true
779         {
780                 if (insidePopup && !mouseDown)                          // I.e., mouse-button-up
781                 {
782                         activated = true;
783                         if (itemList[menuChosen].item[menuItemChosen].action != NULL)
784                         {
785 //                              itemList[menuChosen].item[menuItemChosen].action();
786                                 SDL_Event event;
787                                 event.type = SDL_USEREVENT;
788                                 event.user.code = MENU_ITEM_CHOSEN;
789                                 event.user.data1 = (void *)itemList[menuChosen].item[menuItemChosen].action;
790                             SDL_PushEvent(&event);
791
792                                 clicked = false, menuChosen = menuItemChosen = -1;
793
794 /*                              SDL_Event event;
795                                 while (SDL_PollEvent(&event));          // Flush the event queue...
796                                 event.type = SDL_MOUSEMOTION;
797                                 int mx, my;
798                                 SDL_GetMouseState(&mx, &my);
799                                 event.motion.x = mx, event.motion.y = my;
800                             SDL_PushEvent(&event);                              // & update mouse position...!
801 */                      }
802                 }
803
804                 if (!inside && !insidePopup && mouseDown)
805                         clicked = false, menuChosen = menuItemChosen = -1;
806         }
807 }
808
809 void Menu::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
810 {
811         uint32 xpos = extents.x + offsetX;
812
813         for(uint32 i=0; i<itemList.size(); i++)
814         {
815                 uint16 color1 = fgColor, color2 = bgColor;
816                 if (inside == (i + 1) || (menuChosen != -1 && (uint32)menuChosen == i))
817                         color1 = fgColorHL, color2 = bgColorHL;
818
819                 DrawStringOpaque(screenBuffer, xpos, extents.y + offsetY, color1, color2,
820                         " %s ", itemList[i].title.c_str());
821                 xpos += (itemList[i].title.length() + 2) * 8;
822         }
823
824         // Draw sub menu (but only if active)
825         if (clicked)
826         {
827                 uint32 ypos = extents.y + 9;
828
829                 for(uint32 i=0; i<itemList[menuChosen].item.size(); i++)
830                 {
831                         uint16 color1 = fgColor, color2 = bgColor;
832
833                         if (insidePopup == i + 1)
834                                 color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
835
836                         if (itemList[menuChosen].item[i].name.length() > 0)
837                                 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
838                                         color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
839                                         itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
840                         else
841                                 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
842                                         fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
843
844                         ypos += 8;
845                 }
846         }
847 }
848
849 void Menu::Add(MenuItems mi)
850 {
851         for(uint32 i=0; i<mi.item.size(); i++)
852                 if (mi.item[i].name.length() > mi.charLength)
853                         mi.charLength = mi.item[i].name.length();
854
855         // Set extents here as well...
856         mi.extents.x = extents.x + extents.w, mi.extents.y = extents.y + 9;
857         mi.extents.w = (mi.charLength + 2) * 8, mi.extents.h = mi.item.size() * 8;
858
859         itemList.push_back(mi);
860         extents.w += (mi.title.length() + 2) * 8;
861 }
862
863 //Do we even *need* this?
864 class RootWindow: public Window
865 {
866         public:
867                 RootWindow(Menu * m, Window * w = NULL): menu(m), window(w) {}
868 //Do we even need to care about this crap?
869 //                      { extents.x = extents.y = 0, extents.w = 320, extents.h = 240; }
870                 virtual void HandleKey(SDLKey key) {}
871                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
872                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
873                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) {}
874                 virtual void Notify(Element *) {}
875
876         private:
877                 Menu * menu;
878                 Window * window;
879                 int16 * rootImage[1280 * 240 * 2];
880 };
881
882
883
884 //
885 // GUI stuff--it's not crunchy, it's GUI! ;-)
886 //
887
888 void InitGUI(void)
889 {
890         SDL_ShowCursor(SDL_DISABLE);
891         SDL_GetMouseState(&mouseX, &mouseY);
892 }
893
894 void GUIDone(void)
895 {
896 }
897
898 //
899 // Draw text at the given x/y coordinates. Can invert text as well.
900 //
901 void DrawString(int16 * screen, uint32 x, uint32 y, bool invert, const char * text, ...)
902 {
903         char string[4096];
904         va_list arg;
905
906         va_start(arg, text);
907         vsprintf(string, text, arg);
908         va_end(arg);
909
910         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
911         uint32 length = strlen(string), address = x + (y * pitch);
912
913         for(uint32 i=0; i<length; i++)
914         {
915                 uint32 fontAddr = (uint32)string[i] * 64;
916
917                 for(uint32 yy=0; yy<8; yy++)
918                 {
919                         for(uint32 xx=0; xx<8; xx++)
920                         {
921                                 if ((font1[fontAddr] && !invert) || (!font1[fontAddr] && invert))
922                                         *(screen + address + xx + (yy * pitch)) = 0xFE00;
923                                 fontAddr++;
924                         }
925                 }
926
927                 address += 8;
928         }
929 }
930
931 //
932 // Draw text at the given x/y coordinates, using FG/BG colors.
933 //
934 void DrawStringOpaque(int16 * screen, uint32 x, uint32 y, uint16 color1, uint16 color2, const char * text, ...)
935 {
936         char string[4096];
937         va_list arg;
938
939         va_start(arg, text);
940         vsprintf(string, text, arg);
941         va_end(arg);
942
943         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
944         uint32 length = strlen(string), address = x + (y * pitch);
945
946         for(uint32 i=0; i<length; i++)
947         {
948                 uint32 fontAddr = (uint32)string[i] * 64;
949
950                 for(uint32 yy=0; yy<8; yy++)
951                 {
952                         for(uint32 xx=0; xx<8; xx++)
953                         {
954                                 *(screen + address + xx + (yy * pitch)) = (font1[fontAddr] ? color1 : color2);
955                                 fontAddr++;
956                         }
957                 }
958
959                 address += 8;
960         }
961 }
962
963 //
964 // Draw text at the given x/y coordinates. Can invert text as well.
965 //
966 void DrawStringTrans(int16 * screen, uint32 x, uint32 y, uint16 color, uint8 trans, const char * text, ...)
967 {
968         char string[4096];
969         va_list arg;
970
971         va_start(arg, text);
972         vsprintf(string, text, arg);
973         va_end(arg);
974
975         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
976         uint32 length = strlen(string), address = x + (y * pitch);
977
978         for(uint32 i=0; i<length; i++)
979         {
980                 uint32 fontAddr = (uint32)string[i] * 64;
981
982                 for(uint32 yy=0; yy<8; yy++)
983                 {
984                         for(uint32 xx=0; xx<8; xx++)
985                         {
986                                 if (font1[fontAddr])
987                                 {
988                                         uint16 existingColor = *(screen + address + xx + (yy * pitch));
989
990                                         uint8 eRed = (existingColor >> 10) & 0x1F,
991                                                 eGreen = (existingColor >> 5) & 0x1F,
992                                                 eBlue = existingColor & 0x1F,
993 //This could be done ahead of time, instead of on each pixel...
994                                                 nRed = (color >> 10) & 0x1F,
995                                                 nGreen = (color >> 5) & 0x1F,
996                                                 nBlue = color & 0x1F;
997
998 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
999 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
1000 //because dividing by 32 is faster than dividing by 31...!
1001                                         uint8 invTrans = 32 - trans;
1002                                         uint16 bRed = (eRed * trans + nRed * invTrans) / 32;
1003                                         uint16 bGreen = (eGreen * trans + nGreen * invTrans) / 32;
1004                                         uint16 bBlue = (eBlue * trans + nBlue * invTrans) / 32;
1005
1006                                         uint16 blendedColor = (bRed << 10) | (bGreen << 5) | bBlue;
1007
1008                                         *(screen + address + xx + (yy * pitch)) = blendedColor;
1009                                 }
1010
1011                                 fontAddr++;
1012                         }
1013                 }
1014
1015                 address += 8;
1016         }
1017 }
1018
1019 //
1020 // GUI Main loop
1021 //
1022 bool GUIMain(void)
1023 {
1024         extern int16 * backbuffer;
1025         bool done = false;
1026         SDL_Event event;
1027         Window * mainWindow = NULL;
1028
1029         // Set up the GUI classes...
1030         Element::SetScreenAndPitch(backbuffer, GetSDLScreenPitch() / 2);
1031
1032         Menu mainMenu;
1033         MenuItems mi;
1034         mi.title = "File";
1035         mi.item.push_back(NameAction("Load...", LoadROM));
1036         mi.item.push_back(NameAction("Reset", ResetJaguar));
1037         mi.item.push_back(NameAction("Run", RunEmu, SDLK_ESCAPE));
1038         mi.item.push_back(NameAction(""));
1039         mi.item.push_back(NameAction("Quit", Quit, SDLK_q));
1040         mainMenu.Add(mi);
1041         mi.title = "Settings";
1042         mi.item.clear();
1043         mi.item.push_back(NameAction("Video..."));
1044         mi.item.push_back(NameAction("Audio..."));
1045         mi.item.push_back(NameAction("Misc..."));
1046         mainMenu.Add(mi);
1047         mi.title = "Info";
1048         mi.item.clear();
1049         mi.item.push_back(NameAction("About...", About));
1050         mainMenu.Add(mi);
1051
1052         bool showMouse = true;
1053
1054 //This is crappy!!! !!! FIX !!!
1055         jaguar_reset();
1056
1057         // Set up our background save...
1058         memset(background, 0x11, tom_getVideoModeWidth() * 240 * 2);
1059
1060         while (!done)
1061         {
1062                 while (SDL_PollEvent(&event))
1063                 {
1064                         if (event.type == SDL_USEREVENT)
1065                         {
1066                                 if (event.user.code == WINDOW_CLOSE)
1067                                 {
1068                                         delete mainWindow;
1069                                         mainWindow = NULL;
1070                                 }
1071                                 else if (event.user.code == MENU_ITEM_CHOSEN)
1072                                 {
1073                                         // Confused? Let me enlighten... What we're doing here is casting
1074                                         // data1 as a pointer to a function which returns a Window pointer and
1075                                         // which takes no parameters (the "(Window *(*)(void))" part), then
1076                                         // derefencing it (the "*" in front of that) in order to call the
1077                                         // function that it points to. Clear as mud? Yeah, I hate function
1078                                         // pointers too, but what else are you gonna do?
1079                                         mainWindow = (*(Window *(*)(void))event.user.data1)();
1080
1081                                         while (SDL_PollEvent(&event));  // Flush the event queue...
1082                                         event.type = SDL_MOUSEMOTION;
1083                                         int mx, my;
1084                                         SDL_GetMouseState(&mx, &my);
1085                                         event.motion.x = mx, event.motion.y = my;
1086                                     SDL_PushEvent(&event);                      // & update mouse position...!
1087
1088                                         mouseX = mx, mouseY = my;               // This prevents "mouse flash"...
1089                                         if (vjs.useOpenGL)
1090                                                 mouseX /= 2, mouseY /= 2;
1091                                 }
1092                         }
1093                         else if (event.type == SDL_ACTIVEEVENT)
1094                         {
1095                                 if (event.active.state == SDL_APPMOUSEFOCUS)
1096                                         showMouse = (event.active.gain ? true : false);
1097                         }
1098                         else if (event.type == SDL_KEYDOWN)
1099                         {
1100                                 if (mainWindow)
1101                                         mainWindow->HandleKey(event.key.keysym.sym);
1102                                 else
1103                                         mainMenu.HandleKey(event.key.keysym.sym);
1104                         }
1105                         else if (event.type == SDL_MOUSEMOTION)
1106                         {
1107                                 mouseX = event.motion.x, mouseY = event.motion.y;
1108
1109                                 if (vjs.useOpenGL)
1110                                         mouseX /= 2, mouseY /= 2;
1111
1112                                 if (mainWindow)
1113                                         mainWindow->HandleMouseMove(mouseX, mouseY);
1114                                 else
1115                                         mainMenu.HandleMouseMove(mouseX, mouseY);
1116                         }
1117                         else if (event.type == SDL_MOUSEBUTTONDOWN)
1118                         {
1119                                 uint32 mx = event.button.x, my = event.button.y;
1120
1121                                 if (vjs.useOpenGL)
1122                                         mx /= 2, my /= 2;
1123
1124                                 if (mainWindow)
1125                                         mainWindow->HandleMouseButton(mx, my, true);
1126                                 else
1127                                         mainMenu.HandleMouseButton(mx, my, true);
1128                         }
1129                         else if (event.type == SDL_MOUSEBUTTONUP)
1130                         {
1131                                 uint32 mx = event.button.x, my = event.button.y;
1132
1133                                 if (vjs.useOpenGL)
1134                                         mx /= 2, my /= 2;
1135
1136                                 if (mainWindow)
1137                                         mainWindow->HandleMouseButton(mx, my, false);
1138                                 else
1139                                         mainMenu.HandleMouseButton(mx, my, false);
1140                         }
1141
1142                         // Draw the GUI...
1143 // The way we do things here is kinda stupid (redrawing the screen every frame), but
1144 // it's simple. Perhaps there may be a reason down the road to be more selective with
1145 // our clearing, but for now, this will suffice.
1146 //                      memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
1147                         memcpy(backbuffer, background, tom_getVideoModeWidth() * 240 * 2);
1148
1149                         mainMenu.Draw();
1150                         if (mainWindow)
1151                                 mainWindow->Draw();
1152
1153                         if (showMouse)
1154                                 DrawTransparentBitmap(backbuffer, mouseX, mouseY, mousePic);
1155
1156                         RenderBackbuffer();
1157                 }
1158         }
1159
1160         return true;
1161 }
1162
1163 //
1164 // GUI "action" functions
1165 //
1166 Window * LoadROM(void)
1167 {
1168         FileList * fileList = new FileList(8, 16, 304, 216);
1169
1170         return (Window *)fileList;
1171 }
1172
1173 Window * ResetJaguar(void)
1174 {
1175         jaguar_reset();
1176         return RunEmu();
1177 }
1178
1179 bool debounceRunKey = true;
1180 Window * RunEmu(void)
1181 {
1182 //This is crappy... !!! FIX !!!
1183         extern int16 * backbuffer;
1184         extern bool finished;
1185         extern bool showGUI;
1186         uint32 nFrame = 0, nFrameskip = 0;
1187         uint32 totalFrames = 0;
1188         finished = false;
1189         bool showMessage = true;
1190         uint32 showMsgFrames = 60;
1191         uint8 transparency = 0;
1192         // Pass a message to the "joystick" code to debounce the ESC key...
1193         debounceRunKey = true;
1194
1195         while (!finished)
1196         {
1197                 // Set up new backbuffer with new pixels and data
1198                 JaguarExecute(backbuffer, true);
1199                 totalFrames++;
1200 //WriteLog("Frame #%u...\n", totalFrames);
1201 //extern bool doDSPDis;
1202 //if (totalFrames == 373)
1203 //      doDSPDis = true;
1204
1205 //This sucks... !!! FIX !!!
1206                 joystick_exec();
1207                 if (finished)
1208                         break;
1209
1210                 // Some QnD GUI stuff here...
1211                 if (showGUI)
1212                 {
1213                         extern uint32 gpu_pc, dsp_pc;
1214                         DrawString(backbuffer, 8, 8, false, "GPU PC: %08X", gpu_pc);
1215                         DrawString(backbuffer, 8, 16, false, "DSP PC: %08X", dsp_pc);
1216                 }
1217
1218                 if (showMessage)
1219                 {
1220                         DrawStringTrans(backbuffer, 8, 24*8, 0xFF0F, transparency, "Running...");
1221
1222                         if (showMsgFrames == 0)
1223                         {                       
1224                                 transparency++;
1225
1226                                 if (transparency == 33)
1227                                         showMessage = false;
1228                         }
1229                         else
1230                                 showMsgFrames--;
1231                 }
1232
1233                 // Simple frameskip
1234                 if (nFrame == nFrameskip)
1235                 {
1236                         RenderBackbuffer();
1237                         nFrame = 0;
1238                 }
1239                 else
1240                         nFrame++;
1241         }
1242
1243         // Reset the pitch, since it may have been changed in-game...
1244         Element::SetScreenAndPitch(backbuffer, GetSDLScreenPitch() / 2);
1245
1246         // Save the background for the GUI...
1247 //      memcpy(background, backbuffer, tom_getVideoModeWidth() * 240 * 2);
1248         // In this case, we squash the color to monochrome, then force it to blue + green...
1249         for(uint32 i=0; i<tom_getVideoModeWidth() * 240; i++)
1250         {
1251                 uint16 word = backbuffer[i];
1252                 uint8 r = (word >> 10) & 0x1F, g = (word >> 5) & 0x1F, b = word & 0x1F;
1253                 word = ((r + g + b) / 3) & 0x001F;
1254                 word = (word << 5) | word;
1255                 background[i] = word;
1256         }
1257
1258         return NULL;
1259 }
1260
1261 Window * Quit(void)
1262 {
1263         WriteLog("GUI: Quitting due to user request.\n");
1264         log_done();
1265         exit(0);
1266
1267         return NULL;                                                                    // We never get here...
1268 }
1269
1270 Window * About(void)
1271 {
1272         Window * window = new Window(8, 16, 304, 160);
1273         window->AddElement(new Text(8, 8, "Virtual Jaguar 1.0.7"));
1274         window->AddElement(new Text(8, 24, "Coders:"));
1275         window->AddElement(new Text(16, 32, "Niels Wagenaar (nwagenaar)"));
1276         window->AddElement(new Text(16, 40, "Caz"));
1277         window->AddElement(new Text(16, 48, "James L. Hammons (shamus)"));
1278         window->AddElement(new Text(16, 56, "Adam Green"));
1279
1280         return window;
1281 }
1282
1283 //
1284 // Draw "picture"
1285 // Uses zero as transparent color
1286 //
1287 void DrawTransparentBitmap(int16 * screen, uint32 x, uint32 y, uint16 * bitmap)
1288 {
1289         uint16 width = bitmap[0], height = bitmap[1];
1290         bitmap += 2;
1291
1292         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
1293         uint32 address = x + (y * pitch);
1294
1295         for(int yy=0; yy<height; yy++)
1296         {
1297                 for(int xx=0; xx<width; xx++)
1298                 {
1299                                 if (*bitmap && x + xx < pitch)          // NOTE: Still doesn't clip the Y val...
1300                                         *(screen + address + xx + (yy * pitch)) = *bitmap;
1301                                 bitmap++;
1302                 }
1303         }
1304 }
1305
1306 //
1307 // Very very crude GUI file selector
1308 //
1309 /*bool UserSelectFile(char * path, char * filename)
1310 {
1311 //Testing...
1312 GUIMain();
1313         
1314         extern int16 * backbuffer;
1315         vector<string> fileList;
1316
1317         // Read in the candidate files from the directory pointed to by "path"
1318
1319         DIR * dp = opendir(path);
1320         dirent * de;
1321
1322         while ((de = readdir(dp)) != NULL)
1323         {
1324                 char * ext = strrchr(de->d_name, '.');
1325
1326                 if (ext != NULL)
1327                         if (stricmp(ext, ".zip") == 0 || stricmp(ext, ".jag") == 0)
1328                                 fileList.push_back(string(de->d_name));
1329         }
1330
1331         closedir(dp);
1332
1333         if (fileList.size() == 0)                                               // Any files found?
1334                 return false;                                                           // Nope. Bail!
1335
1336         // Main GUI selection loop
1337
1338         uint32 cursor = 0, startFile = 0;
1339
1340         if (fileList.size() > 1)        // Only go GUI if more than one possibility!
1341         {
1342                 sort(fileList.begin(), fileList.end());
1343
1344                 bool done = false;
1345                 uint32 limit = (fileList.size() > 30 ? 30 : fileList.size());
1346                 SDL_Event event;
1347
1348                 // Ensure that the GUI is drawn before any user input...
1349                 event.type = SDL_USEREVENT;
1350                 SDL_PushEvent(&event);
1351
1352                 while (!done)
1353                 {
1354                         while (SDL_PollEvent(&event))
1355                         {
1356                                 if (event.type == SDL_KEYDOWN)
1357                                 {
1358                                         SDLKey key = event.key.keysym.sym;
1359
1360                                         if (key == SDLK_DOWN)
1361                                         {
1362                                                 if (cursor != limit - 1)        // Cursor is within its window
1363                                                         cursor++;
1364                                                 else                                            // Otherwise, scroll the window...
1365                                                 {
1366                                                         if (cursor + startFile != fileList.size() - 1)
1367                                                                 startFile++;
1368                                                 }
1369                                         }
1370                                         if (key == SDLK_UP)
1371                                         {
1372                                                 if (cursor != 0)
1373                                                         cursor--;
1374                                                 else
1375                                                 {
1376                                                         if (startFile != 0)
1377                                                                 startFile--;
1378                                                 }
1379                                         }
1380                                         if (key == SDLK_PAGEDOWN)
1381                                         {
1382                                                 if (cursor != limit - 1)
1383                                                         cursor = limit - 1;
1384                                                 else
1385                                                 {
1386                                                         startFile += limit;
1387                                                         if (startFile > fileList.size() - limit)
1388                                                                 startFile = fileList.size() - limit;
1389                                                 }
1390                                         }
1391                                         if (key == SDLK_PAGEUP)
1392                                         {
1393                                                 if (cursor != 0)
1394                                                         cursor = 0;
1395                                                 else
1396                                                 {
1397                                                         if (startFile < limit)
1398                                                                 startFile = 0;
1399                                                         else
1400                                                                 startFile -= limit;
1401                                                 }
1402                                         }
1403                                         if (key == SDLK_RETURN)
1404                                                 done = true;
1405                                         if (key == SDLK_ESCAPE)
1406                                         {
1407                                                 WriteLog("GUI: Aborting VJ by user request.\n");
1408                                                 return false;                                           // Bail out!
1409                                         }
1410                                         if (key >= SDLK_a && key <= SDLK_z)
1411                                         {
1412                                                 // Advance cursor to filename with first letter pressed...
1413                                                 uint8 which = (key - SDLK_a) + 65;      // Convert key to A-Z char
1414
1415                                                 for(uint32 i=0; i<fileList.size(); i++)
1416                                                 {
1417                                                         if ((fileList[i][0] & 0xDF) == which)
1418                                                         {
1419                                                                 cursor = i - startFile;
1420                                                                 if (i > startFile + limit - 1)
1421                                                                         startFile = i - limit + 1,
1422                                                                         cursor = limit - 1;
1423                                                                 if (i < startFile)
1424                                                                         startFile = i,
1425                                                                         cursor = 0;
1426                                                                 break;
1427                                                         }
1428                                                 }
1429                                         }
1430                                 }
1431                                 else if (event.type == SDL_MOUSEMOTION)
1432                                 {
1433                                         mouseX = event.motion.x, mouseY = event.motion.y;
1434                                         if (vjs.useOpenGL)
1435                                                 mouseX /= 2, mouseY /= 2;
1436                                 }
1437                                 else if (event.type == SDL_MOUSEBUTTONDOWN)
1438                                 {
1439                                         uint32 mx = event.button.x, my = event.button.y;
1440                                         if (vjs.useOpenGL)
1441                                                 mx /= 2, my /= 2;
1442                                         cursor = my / 8;
1443                                 }
1444
1445                                 // Draw the GUI...
1446 //                              memset(backbuffer, 0x11, tom_getVideoModeWidth() * tom_getVideoModeHeight() * 2);
1447                                 memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
1448
1449                                 for(uint32 i=0; i<limit; i++)
1450                                 {
1451                                         // Clip our strings to guarantee that they fit on the screen...
1452                                         // (and strip off the extension too)
1453                                         string s(fileList[startFile + i], 0, fileList[startFile + i].length() - 4);
1454                                         if (s.length() > 38)
1455                                                 s[38] = 0;
1456                                         DrawString(backbuffer, 0, i*8, (cursor == i ? true : false), " %s ", s.c_str());
1457                                 }
1458
1459                                 DrawTransparentBitmap(backbuffer, mouseX, mouseY, mousePic);
1460
1461                                 RenderBackbuffer();
1462                         }
1463                 }
1464         }
1465
1466         strcpy(filename, path);
1467
1468         if (strlen(path) > 0)
1469                 if (path[strlen(path) - 1] != '/')
1470                         strcat(filename, "/");
1471
1472         strcat(filename, fileList[startFile + cursor].c_str());
1473
1474         return true;
1475 }*/
1476
1477 //
1478 // Generic ROM loading
1479 //
1480 uint32 JaguarLoadROM(uint8 * rom, char * path)
1481 {
1482         uint32 romSize = 0;
1483
1484         char * ext = strrchr(path, '.');
1485         if (ext != NULL)
1486         {
1487                 WriteLog("VJ: Loading \"%s\"...", path);
1488
1489                 if (stricmp(ext, ".zip") == 0)
1490                 {
1491                         // Handle ZIP file loading here...
1492                         WriteLog("(ZIPped)...");
1493
1494                         if (load_zipped_file(0, 0, path, NULL, &rom, &romSize) == -1)
1495                         {
1496                                 WriteLog("Failed!\n");
1497                                 return 0;
1498                         }
1499                 }
1500                 else
1501                 {
1502 /*                      FILE * fp = fopen(path, "rb");
1503
1504                         if (fp == NULL)
1505                         {
1506                                 WriteLog("Failed!\n");
1507                                 return 0;
1508                         }
1509
1510                         fseek(fp, 0, SEEK_END);
1511                         romSize = ftell(fp);
1512                         fseek(fp, 0, SEEK_SET);
1513                         fread(rom, 1, romSize, fp);
1514                         fclose(fp);*/
1515
1516                         gzFile fp = gzopen(path, "rb");
1517
1518                         if (fp == NULL)
1519                         {
1520                                 WriteLog("Failed!\n");
1521                                 return 0;
1522                         }
1523
1524                         romSize = gzfilelength(fp);
1525                         gzseek(fp, 0, SEEK_SET);
1526                         gzread(fp, rom, romSize);
1527                         gzclose(fp);
1528                 }
1529
1530                 WriteLog("OK (%i bytes)\n", romSize);
1531         }
1532
1533         return romSize;
1534 }
1535
1536 //
1537 // Jaguar cartridge ROM loading
1538 //
1539 void JaguarLoadCart(uint8 * mem, char * path)
1540 {
1541         uint32 romSize = JaguarLoadROM(mem, path);
1542
1543         if (romSize == 0)
1544         {
1545 /*              char newPath[2048];
1546                 WriteLog("VJ: Trying GUI...\n");
1547
1548 //This is not *nix friendly for some reason...
1549 //              if (!UserSelectFile(path, newPath))
1550                 if (!UserSelectFile((strlen(path) == 0 ? (char *)"." : path), newPath))
1551                 {
1552                         WriteLog("VJ: Could not find valid ROM in directory \"%s\"...\nAborting!\n", path);
1553                         log_done();
1554                         exit(0);
1555                 }
1556
1557                 romSize = JaguarLoadROM(mem, newPath);
1558 */
1559                 if (romSize == 0)
1560                 {
1561 //                      WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
1562                         WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", path);
1563                         log_done();
1564                         exit(0);
1565                 }
1566         }
1567
1568         jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, romSize);
1569         WriteLog("CRC: %08X\n", (unsigned int)jaguar_mainRom_crc32);
1570         eeprom_init();
1571 }
1572
1573 //
1574 // Get the length of a (possibly) gzipped file
1575 //
1576 int gzfilelength(gzFile gd)
1577 {
1578    int size = 0, length = 0;
1579    unsigned char buffer[0x10000];
1580
1581    gzrewind(gd);
1582
1583    do
1584    {
1585       // Read in chunks until EOF
1586       size = gzread(gd, buffer, 0x10000);
1587
1588       if (size <= 0)
1589         break;
1590
1591       length += size;
1592    }
1593    while (!gzeof(gd));
1594
1595    gzrewind(gd);
1596    return length;
1597 }