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