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