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