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