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