]> Shamusworld >> Repos - virtualjaguar/blob - src/gui.cpp
Minor graphical update
[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 (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 > (extents.x + extents.w) && x <= (extents.x + extents.w + 8)
798                 && y > (extents.y + 8) && y <= (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, ".jag") == 0)
937                                 files->AddItem(string(de->d_name));
938         }
939
940         closedir(dp);
941 }
942
943 void FileList::HandleKey(SDLKey key)
944 {
945         if (key == SDLK_RETURN)
946                 Notify(load);
947         else
948                 Window::HandleKey(key);
949 }
950
951 void FileList::Notify(Element * e)
952 {
953         if (e == load)
954         {
955                 char filename[MAX_PATH];
956                 strcpy(filename, vjs.ROMPath);
957
958                 if (strlen(filename) > 0)
959                         if (filename[strlen(filename) - 1] != '/')
960                                 strcat(filename, "/");
961
962                 strcat(filename, files->GetSelectedItem().c_str());
963
964 //              uint32 romSize = JaguarLoadROM(jaguar_mainRom, filename);
965                 JaguarLoadCart(jaguar_mainRom, filename);
966
967 //              if (romSize == 0)
968 //We need better error checking here... !!! FIX !!!
969 //                      WriteLog("VJ: Could not load ROM from file \"%s\"...", files->GetSelectedItem().c_str());
970 //              else
971 //              {
972 //                      jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, romSize);
973 //                      WriteLog("CRC: %08X\n", (unsigned int)jaguar_mainRom_crc32);
974 //                      eeprom_init();
975
976                 SDL_Event event;
977                 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
978                 SDL_PushEvent(&event);
979
980                 event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
981                 event.user.data1 = (void *)ResetJaguar;
982             SDL_PushEvent(&event);
983 //              }
984         }
985         else
986                 Window::Notify(e);
987 }
988
989
990 struct NameAction
991 {
992         string name;
993         Window * (* action)(void);
994         SDLKey hotKey;
995
996         NameAction(string n, Window * (* a)(void) = NULL, SDLKey k = SDLK_UNKNOWN): name(n),
997                 action(a), hotKey(k) {}
998 };
999
1000
1001 class MenuItems
1002 {
1003         public:
1004                 MenuItems(): charLength(0) {}
1005                 bool Inside(uint32 x, uint32 y)
1006                 { return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
1007                 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false); }
1008
1009                 string title;
1010                 vector<NameAction> item;
1011                 uint32 charLength;
1012                 SDL_Rect extents;
1013 };
1014
1015 class Menu: public Element
1016 {
1017         public:
1018                 Menu(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 8,
1019                         uint16 fgc = 0x1CFF, uint16 bgc = 0x000F, uint16 fgch = 0x421F,
1020                         uint16 bgch = 0x1CFF): Element(x, y, w, h), activated(false), clicked(false),
1021                         inside(0), insidePopup(0), fgColor(fgc), bgColor(bgc), fgColorHL(fgch),
1022                         bgColorHL(bgch), menuChosen(-1), menuItemChosen(-1) {}
1023                 virtual void HandleKey(SDLKey key);
1024                 virtual void HandleMouseMove(uint32 x, uint32 y);
1025                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
1026                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
1027                 virtual void Notify(Element *) {}
1028                 void Add(MenuItems mi);
1029
1030         protected:
1031                 bool activated, clicked;
1032                 uint32 inside, insidePopup;
1033                 uint16 fgColor, bgColor, fgColorHL, bgColorHL;
1034                 int menuChosen, menuItemChosen;
1035
1036         private:
1037                 vector<MenuItems> itemList;
1038 };
1039
1040 void Menu::HandleKey(SDLKey key)
1041 {
1042         for(uint32 i=0; i<itemList.size(); i++)
1043         {
1044                 for(uint32 j=0; j<itemList[i].item.size(); j++)
1045                 {
1046                         if (itemList[i].item[j].hotKey == key)
1047                         {
1048                                 SDL_Event event;
1049                                 event.type = SDL_USEREVENT;
1050                                 event.user.code = MENU_ITEM_CHOSEN;
1051                                 event.user.data1 = (void *)itemList[i].item[j].action;
1052                         SDL_PushEvent(&event);
1053
1054                                 clicked = false, menuChosen = menuItemChosen = -1;
1055                                 break;
1056                         }
1057                 }
1058         }
1059 }
1060
1061 void Menu::HandleMouseMove(uint32 x, uint32 y)
1062 {
1063         inside = insidePopup = 0;
1064
1065         if (Inside(x, y))
1066         {
1067                 // Find out *where* we are inside the menu bar
1068                 uint32 xpos = extents.x;
1069
1070                 for(uint32 i=0; i<itemList.size(); i++)
1071                 {
1072                         uint32 width = (itemList[i].title.length() + 2) * 8;
1073
1074                         if (x >= xpos && x < xpos + width)
1075                         {
1076                                 inside = i + 1;
1077                                 menuChosen = i;
1078                                 break;
1079                         }
1080
1081                         xpos += width;
1082                 }
1083         }
1084
1085         if (!Inside(x, y) && !clicked)
1086         {
1087                 menuChosen = -1;
1088         }
1089
1090         if (itemList[menuChosen].Inside(x, y) && clicked)
1091         {
1092                 insidePopup = ((y - itemList[menuChosen].extents.y) / 8) + 1;
1093                 menuItemChosen = insidePopup - 1;
1094         }
1095 }
1096
1097 void Menu::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
1098 {
1099         if (!clicked)
1100         {
1101                 if (mouseDown)
1102                 {
1103                         if (inside)
1104                                 clicked = true;
1105                         else
1106                                 menuChosen = -1;                                        // clicked is already false...!
1107                 }
1108         }
1109         else                                                                                    // clicked == true
1110         {
1111                 if (insidePopup && !mouseDown)                          // I.e., mouse-button-up
1112                 {
1113                         activated = true;
1114                         if (itemList[menuChosen].item[menuItemChosen].action != NULL)
1115                         {
1116 //                              itemList[menuChosen].item[menuItemChosen].action();
1117                                 SDL_Event event;
1118                                 event.type = SDL_USEREVENT;
1119                                 event.user.code = MENU_ITEM_CHOSEN;
1120                                 event.user.data1 = (void *)itemList[menuChosen].item[menuItemChosen].action;
1121                             SDL_PushEvent(&event);
1122
1123                                 clicked = false, menuChosen = menuItemChosen = -1;
1124
1125 /*                              SDL_Event event;
1126                                 while (SDL_PollEvent(&event));          // Flush the event queue...
1127                                 event.type = SDL_MOUSEMOTION;
1128                                 int mx, my;
1129                                 SDL_GetMouseState(&mx, &my);
1130                                 event.motion.x = mx, event.motion.y = my;
1131                             SDL_PushEvent(&event);                              // & update mouse position...!
1132 */                      }
1133                 }
1134
1135                 if (!inside && !insidePopup && mouseDown)
1136                         clicked = false, menuChosen = menuItemChosen = -1;
1137         }
1138 }
1139
1140 void Menu::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
1141 {
1142         uint32 xpos = extents.x + offsetX;
1143
1144         for(uint32 i=0; i<itemList.size(); i++)
1145         {
1146                 uint16 color1 = fgColor, color2 = bgColor;
1147                 if (inside == (i + 1) || (menuChosen != -1 && (uint32)menuChosen == i))
1148                         color1 = fgColorHL, color2 = bgColorHL;
1149
1150                 DrawStringOpaque(screenBuffer, xpos, extents.y + offsetY, color1, color2,
1151                         " %s ", itemList[i].title.c_str());
1152                 xpos += (itemList[i].title.length() + 2) * 8;
1153         }
1154
1155         // Draw sub menu (but only if active)
1156         if (clicked)
1157         {
1158                 uint32 ypos = extents.y + 9;
1159
1160                 for(uint32 i=0; i<itemList[menuChosen].item.size(); i++)
1161                 {
1162                         uint16 color1 = fgColor, color2 = bgColor;
1163
1164                         if (insidePopup == i + 1)
1165                                 color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
1166
1167                         if (itemList[menuChosen].item[i].name.length() > 0)
1168                                 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
1169                                         color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
1170                                         itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
1171                         else
1172                                 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
1173                                         fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
1174
1175                         ypos += 8;
1176                 }
1177         }
1178 }
1179
1180 void Menu::Add(MenuItems mi)
1181 {
1182         for(uint32 i=0; i<mi.item.size(); i++)
1183                 if (mi.item[i].name.length() > mi.charLength)
1184                         mi.charLength = mi.item[i].name.length();
1185
1186         // Set extents here as well...
1187         mi.extents.x = extents.x + extents.w, mi.extents.y = extents.y + 9;
1188         mi.extents.w = (mi.charLength + 2) * 8, mi.extents.h = mi.item.size() * 8;
1189
1190         itemList.push_back(mi);
1191         extents.w += (mi.title.length() + 2) * 8;
1192 }
1193
1194
1195 //Do we even *need* this?
1196 class RootWindow: public Window
1197 {
1198         public:
1199                 RootWindow(Menu * m, Window * w = NULL): menu(m), window(w) {}
1200 //Do we even need to care about this crap?
1201 //                      { extents.x = extents.y = 0, extents.w = 320, extents.h = 240; }
1202                 virtual void HandleKey(SDLKey key) {}
1203                 virtual void HandleMouseMove(uint32 x, uint32 y) {}
1204                 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
1205                 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) {}
1206                 virtual void Notify(Element *) {}
1207
1208         private:
1209                 Menu * menu;
1210                 Window * window;
1211                 int16 * rootImage[1280 * 240 * 2];
1212 };
1213
1214
1215
1216 //
1217 // GUI stuff--it's not crunchy, it's GUI! ;-)
1218 //
1219
1220 void InitGUI(void)
1221 {
1222         SDL_ShowCursor(SDL_DISABLE);
1223         SDL_GetMouseState(&mouseX, &mouseY);
1224 }
1225
1226 void GUIDone(void)
1227 {
1228 }
1229
1230 //
1231 // Draw text at the given x/y coordinates. Can invert text as well.
1232 //
1233 void DrawString(int16 * screen, uint32 x, uint32 y, bool invert, const char * text, ...)
1234 {
1235         char string[4096];
1236         va_list arg;
1237
1238         va_start(arg, text);
1239         vsprintf(string, text, arg);
1240         va_end(arg);
1241
1242         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
1243         uint32 length = strlen(string), address = x + (y * pitch);
1244
1245         for(uint32 i=0; i<length; i++)
1246         {
1247                 uint32 fontAddr = (uint32)string[i] * 64;
1248
1249                 for(uint32 yy=0; yy<8; yy++)
1250                 {
1251                         for(uint32 xx=0; xx<8; xx++)
1252                         {
1253                                 if ((font1[fontAddr] && !invert) || (!font1[fontAddr] && invert))
1254                                         *(screen + address + xx + (yy * pitch)) = 0xFE00;
1255                                 fontAddr++;
1256                         }
1257                 }
1258
1259                 address += 8;
1260         }
1261 }
1262
1263 //
1264 // Draw text at the given x/y coordinates, using FG/BG colors.
1265 //
1266 void DrawStringOpaque(int16 * screen, uint32 x, uint32 y, uint16 color1, uint16 color2, const char * text, ...)
1267 {
1268         char string[4096];
1269         va_list arg;
1270
1271         va_start(arg, text);
1272         vsprintf(string, text, arg);
1273         va_end(arg);
1274
1275         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
1276         uint32 length = strlen(string), address = x + (y * pitch);
1277
1278         for(uint32 i=0; i<length; i++)
1279         {
1280                 uint32 fontAddr = (uint32)string[i] * 64;
1281
1282                 for(uint32 yy=0; yy<8; yy++)
1283                 {
1284                         for(uint32 xx=0; xx<8; xx++)
1285                         {
1286                                 *(screen + address + xx + (yy * pitch)) = (font1[fontAddr] ? color1 : color2);
1287                                 fontAddr++;
1288                         }
1289                 }
1290
1291                 address += 8;
1292         }
1293 }
1294
1295 //
1296 // Draw text at the given x/y coordinates with transparency (0 is fully opaque, 32 is fully transparent).
1297 //
1298 void DrawStringTrans(int16 * screen, uint32 x, uint32 y, uint16 color, uint8 trans, const char * text, ...)
1299 {
1300         char string[4096];
1301         va_list arg;
1302
1303         va_start(arg, text);
1304         vsprintf(string, text, arg);
1305         va_end(arg);
1306
1307         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
1308         uint32 length = strlen(string), address = x + (y * pitch);
1309
1310         for(uint32 i=0; i<length; i++)
1311         {
1312                 uint32 fontAddr = (uint32)string[i] * 64;
1313
1314                 for(uint32 yy=0; yy<8; yy++)
1315                 {
1316                         for(uint32 xx=0; xx<8; xx++)
1317                         {
1318                                 if (font1[fontAddr])
1319                                 {
1320                                         uint16 existingColor = *(screen + address + xx + (yy * pitch));
1321
1322                                         uint8 eRed = (existingColor >> 10) & 0x1F,
1323                                                 eGreen = (existingColor >> 5) & 0x1F,
1324                                                 eBlue = existingColor & 0x1F,
1325 //This could be done ahead of time, instead of on each pixel...
1326                                                 nRed = (color >> 10) & 0x1F,
1327                                                 nGreen = (color >> 5) & 0x1F,
1328                                                 nBlue = color & 0x1F;
1329
1330 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
1331 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
1332 //because dividing by 32 is faster than dividing by 31...!
1333                                         uint8 invTrans = 32 - trans;
1334                                         uint16 bRed = (eRed * trans + nRed * invTrans) / 32;
1335                                         uint16 bGreen = (eGreen * trans + nGreen * invTrans) / 32;
1336                                         uint16 bBlue = (eBlue * trans + nBlue * invTrans) / 32;
1337
1338                                         uint16 blendedColor = (bRed << 10) | (bGreen << 5) | bBlue;
1339
1340                                         *(screen + address + xx + (yy * pitch)) = blendedColor;
1341                                 }
1342
1343                                 fontAddr++;
1344                         }
1345                 }
1346
1347                 address += 8;
1348         }
1349 }
1350
1351 //
1352 // GUI Main loop
1353 //
1354 bool GUIMain(void)
1355 {
1356         extern int16 * backbuffer;
1357         bool done = false;
1358         SDL_Event event;
1359         Window * mainWindow = NULL;
1360
1361         // Set up the GUI classes...
1362         Element::SetScreenAndPitch(backbuffer, GetSDLScreenPitch() / 2);
1363
1364         Menu mainMenu;
1365         MenuItems mi;
1366         mi.title = "File";
1367         mi.item.push_back(NameAction("Load...", LoadROM, SDLK_l));
1368         mi.item.push_back(NameAction("Reset", ResetJaguar));
1369         mi.item.push_back(NameAction("Run", RunEmu, SDLK_ESCAPE));
1370         mi.item.push_back(NameAction(""));
1371         mi.item.push_back(NameAction("Quit", Quit, SDLK_q));
1372         mainMenu.Add(mi);
1373         mi.title = "Settings";
1374         mi.item.clear();
1375         mi.item.push_back(NameAction("Video..."));
1376         mi.item.push_back(NameAction("Audio..."));
1377         mi.item.push_back(NameAction("Misc...", MiscOptions, SDLK_m));
1378         mainMenu.Add(mi);
1379         mi.title = "Info";
1380         mi.item.clear();
1381         mi.item.push_back(NameAction("About...", About));
1382         mainMenu.Add(mi);
1383
1384         bool showMouse = true;
1385
1386 //This is crappy!!! !!! FIX !!!
1387         jaguar_reset();
1388
1389         // Set up our background save...
1390         memset(background, 0x11, tom_getVideoModeWidth() * 240 * 2);
1391
1392         while (!done)
1393         {
1394                 while (SDL_PollEvent(&event))
1395                 {
1396                         if (event.type == SDL_USEREVENT)
1397                         {
1398                                 if (event.user.code == WINDOW_CLOSE)
1399                                 {
1400                                         delete mainWindow;
1401                                         mainWindow = NULL;
1402                                 }
1403                                 else if (event.user.code == MENU_ITEM_CHOSEN)
1404                                 {
1405                                         // Confused? Let me enlighten... What we're doing here is casting
1406                                         // data1 as a pointer to a function which returns a Window pointer and
1407                                         // which takes no parameters (the "(Window *(*)(void))" part), then
1408                                         // derefencing it (the "*" in front of that) in order to call the
1409                                         // function that it points to. Clear as mud? Yeah, I hate function
1410                                         // pointers too, but what else are you gonna do?
1411                                         mainWindow = (*(Window *(*)(void))event.user.data1)();
1412
1413                                         while (SDL_PollEvent(&event));  // Flush the event queue...
1414                                         event.type = SDL_MOUSEMOTION;
1415                                         int mx, my;
1416                                         SDL_GetMouseState(&mx, &my);
1417                                         event.motion.x = mx, event.motion.y = my;
1418                                     SDL_PushEvent(&event);                      // & update mouse position...!
1419
1420                                         mouseX = mx, mouseY = my;               // This prevents "mouse flash"...
1421                                         if (vjs.useOpenGL)
1422                                                 mouseX /= 2, mouseY /= 2;
1423                                 }
1424                         }
1425                         else if (event.type == SDL_ACTIVEEVENT)
1426                         {
1427                                 if (event.active.state == SDL_APPMOUSEFOCUS)
1428                                         showMouse = (event.active.gain ? true : false);
1429                         }
1430                         else if (event.type == SDL_KEYDOWN)
1431                         {
1432                                 if (mainWindow)
1433                                         mainWindow->HandleKey(event.key.keysym.sym);
1434                                 else
1435                                         mainMenu.HandleKey(event.key.keysym.sym);
1436                         }
1437                         else if (event.type == SDL_MOUSEMOTION)
1438                         {
1439                                 mouseX = event.motion.x, mouseY = event.motion.y;
1440
1441                                 if (vjs.useOpenGL)
1442                                         mouseX /= 2, mouseY /= 2;
1443
1444                                 if (mainWindow)
1445                                         mainWindow->HandleMouseMove(mouseX, mouseY);
1446                                 else
1447                                         mainMenu.HandleMouseMove(mouseX, mouseY);
1448                         }
1449                         else if (event.type == SDL_MOUSEBUTTONDOWN)
1450                         {
1451                                 uint32 mx = event.button.x, my = event.button.y;
1452
1453                                 if (vjs.useOpenGL)
1454                                         mx /= 2, my /= 2;
1455
1456                                 if (mainWindow)
1457                                         mainWindow->HandleMouseButton(mx, my, true);
1458                                 else
1459                                         mainMenu.HandleMouseButton(mx, my, true);
1460                         }
1461                         else if (event.type == SDL_MOUSEBUTTONUP)
1462                         {
1463                                 uint32 mx = event.button.x, my = event.button.y;
1464
1465                                 if (vjs.useOpenGL)
1466                                         mx /= 2, my /= 2;
1467
1468                                 if (mainWindow)
1469                                         mainWindow->HandleMouseButton(mx, my, false);
1470                                 else
1471                                         mainMenu.HandleMouseButton(mx, my, false);
1472                         }
1473
1474                         // Draw the GUI...
1475 // The way we do things here is kinda stupid (redrawing the screen every frame), but
1476 // it's simple. Perhaps there may be a reason down the road to be more selective with
1477 // our clearing, but for now, this will suffice.
1478 //                      memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
1479                         memcpy(backbuffer, background, tom_getVideoModeWidth() * 240 * 2);
1480
1481                         mainMenu.Draw();
1482                         if (mainWindow)
1483                                 mainWindow->Draw();
1484
1485                         if (showMouse)
1486                                 DrawTransparentBitmap(backbuffer, mouseX, mouseY, mousePic);
1487
1488                         RenderBackbuffer();
1489                 }
1490         }
1491
1492         return true;
1493 }
1494
1495 //
1496 // GUI "action" functions
1497 //
1498 Window * LoadROM(void)
1499 {
1500         FileList * fileList = new FileList(8, 16, 304, 216);
1501
1502         return (Window *)fileList;
1503 }
1504
1505 Window * ResetJaguar(void)
1506 {
1507         jaguar_reset();
1508         return RunEmu();
1509 }
1510
1511 bool debounceRunKey = true;
1512 Window * RunEmu(void)
1513 {
1514 //This is crappy... !!! FIX !!!
1515         extern int16 * backbuffer;
1516         extern bool finished, showGUI;
1517
1518         uint32 nFrame = 0, nFrameskip = 0;
1519         uint32 totalFrames = 0;
1520         finished = false;
1521         bool showMessage = true;
1522         uint32 showMsgFrames = 120;
1523         uint8 transparency = 0;
1524         // Pass a message to the "joystick" code to debounce the ESC key...
1525         debounceRunKey = true;
1526
1527         uint32 cartType = 2;
1528         if (jaguarRomSize == 0x200000)
1529                 cartType = 0;
1530         else if (jaguarRomSize == 0x400000)
1531                 cartType = 1;
1532
1533         char * cartTypeName[3] = { "2M Cartridge", "4M Cartridge", "Homebrew" };
1534
1535         while (!finished)
1536         {
1537                 // Set up new backbuffer with new pixels and data
1538                 JaguarExecute(backbuffer, true);
1539                 totalFrames++;
1540 //WriteLog("Frame #%u...\n", totalFrames);
1541 //extern bool doDSPDis;
1542 //if (totalFrames == 373)
1543 //      doDSPDis = true;
1544
1545 //This sucks... !!! FIX !!!
1546                 joystick_exec();
1547                 if (finished)
1548                         break;
1549
1550                 // Some QnD GUI stuff here...
1551                 if (showGUI)
1552                 {
1553                         extern uint32 gpu_pc, dsp_pc;
1554                         DrawString(backbuffer, 8, 8, false, "GPU PC: %08X", gpu_pc);
1555                         DrawString(backbuffer, 8, 16, false, "DSP PC: %08X", dsp_pc);
1556                 }
1557
1558                 if (showMessage)
1559                 {
1560                         DrawStringTrans(backbuffer, 8, 24*8, 0xFF0F, transparency, "Running...");
1561                         DrawStringTrans(backbuffer, 8, 26*8, 0x3FE3, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
1562                         DrawStringTrans(backbuffer, 8, 27*8, 0x3FE3, transparency, "CRC: %08X", jaguar_mainRom_crc32);
1563
1564                         if (showMsgFrames == 0)
1565                         {                       
1566                                 transparency++;
1567
1568                                 if (transparency == 33)
1569 {
1570                                         showMessage = false;
1571 /*extern bool doGPUDis;
1572 doGPUDis = true;//*/
1573 }
1574
1575                         }
1576                         else
1577                                 showMsgFrames--;
1578                 }
1579
1580                 // Simple frameskip
1581                 if (nFrame == nFrameskip)
1582                 {
1583                         RenderBackbuffer();
1584                         nFrame = 0;
1585                 }
1586                 else
1587                         nFrame++;
1588         }
1589
1590         // Reset the pitch, since it may have been changed in-game...
1591         Element::SetScreenAndPitch(backbuffer, GetSDLScreenPitch() / 2);
1592
1593         // Save the background for the GUI...
1594 //      memcpy(background, backbuffer, tom_getVideoModeWidth() * 240 * 2);
1595         // In this case, we squash the color to monochrome, then force it to blue + green...
1596         for(uint32 i=0; i<tom_getVideoModeWidth() * 240; i++)
1597         {
1598                 uint16 word = backbuffer[i];
1599                 uint8 r = (word >> 10) & 0x1F, g = (word >> 5) & 0x1F, b = word & 0x1F;
1600                 word = ((r + g + b) / 3) & 0x001F;
1601                 word = (word << 5) | word;
1602                 background[i] = word;
1603         }
1604
1605         return NULL;
1606 }
1607
1608 Window * Quit(void)
1609 {
1610 //This is crap. We need some REAL exit code, instead of this psuedo crap... !!! FIX !!!
1611         WriteLog("GUI: Quitting due to user request.\n");
1612
1613 //      log_done();
1614         jaguar_done();
1615         version_done();
1616         memory_done();
1617         VideoDone();                                                                    // Free SDL components last...!
1618         log_done();     
1619
1620         exit(0);
1621
1622         return NULL;                                                                    // We never get here...
1623 }
1624
1625 Window * About(void)
1626 {
1627         Window * window = new Window(8, 16, 304, 160);
1628         window->AddElement(new Text(8, 8, "Virtual Jaguar 1.0.7"));
1629         window->AddElement(new Text(8, 24, "Coders:"));
1630         window->AddElement(new Text(16, 32, "Niels Wagenaar (nwagenaar)"));
1631         window->AddElement(new Text(16, 40, "Carwin Jones (Caz)"));
1632         window->AddElement(new Text(16, 48, "James L. Hammons (shamus)"));
1633         window->AddElement(new Text(16, 56, "Adam Green"));
1634
1635         return window;
1636 }
1637
1638 Window * MiscOptions(void)
1639 {
1640         Window * window = new Window(8, 16, 304, 160);
1641         window->AddElement(new PushButton(8, 8, &vjs.useJaguarBIOS, "BIOS"));
1642         window->AddElement(new SlideSwitch(8, 20, &vjs.hardwareTypeNTSC, "PAL", "NTSC"));
1643         window->AddElement(new PushButton(8, 40, &vjs.DSPEnabled, "DSP"));
1644         window->AddElement(new SlideSwitch(16, 52, &vjs.usePipelinedDSP, "Original", "Pipelined"));
1645         window->AddElement(new SlideSwitch(8, 72, (bool *)&vjs.glFilter, "Sharp", "Blurry"));
1646
1647 // Missing:
1648 // * BIOS path
1649 // * ROM path
1650 // * EEPROM path
1651 // * joystick
1652 // * joystick port
1653 // * OpenGL?
1654 // * GL Filter type
1655 // * Window/fullscreen
1656
1657         return window;
1658 }
1659
1660
1661 //
1662 // Draw "picture"
1663 // Uses zero as transparent color
1664 // Can also use an optional alpha channel
1665 //
1666 void DrawTransparentBitmap(int16 * screen, uint32 x, uint32 y, uint16 * bitmap, uint8 * alpha/*=NULL*/)
1667 {
1668         uint16 width = bitmap[0], height = bitmap[1];
1669         bitmap += 2;
1670
1671         uint32 pitch = GetSDLScreenPitch() / 2;                 // Returns pitch in bytes but we need words...
1672         uint32 address = x + (y * pitch);
1673
1674         for(int yy=0; yy<height; yy++)
1675         {
1676                 for(int xx=0; xx<width; xx++)
1677                 {
1678                         if (alpha == NULL)
1679                         {
1680                                 if (*bitmap && x + xx < pitch)                  // NOTE: Still doesn't clip the Y val...
1681                                         *(screen + address + xx + (yy * pitch)) = *bitmap;
1682                         }
1683                         else
1684                         {
1685                                 uint8 trans = *alpha;
1686                                 uint16 color = *bitmap;
1687                                 uint16 existingColor = *(screen + address + xx + (yy * pitch));
1688
1689                                 uint8 eRed = (existingColor >> 10) & 0x1F,
1690                                         eGreen = (existingColor >> 5) & 0x1F,
1691                                         eBlue = existingColor & 0x1F,
1692
1693                                         nRed = (color >> 10) & 0x1F,
1694                                         nGreen = (color >> 5) & 0x1F,
1695                                         nBlue = color & 0x1F;
1696
1697                                 uint8 invTrans = 255 - trans;
1698                                 uint16 bRed = (eRed * trans + nRed * invTrans) / 255;
1699                                 uint16 bGreen = (eGreen * trans + nGreen * invTrans) / 255;
1700                                 uint16 bBlue = (eBlue * trans + nBlue * invTrans) / 255;
1701
1702                                 uint16 blendedColor = (bRed << 10) | (bGreen << 5) | bBlue;
1703
1704                                 *(screen + address + xx + (yy * pitch)) = blendedColor;
1705
1706                                 alpha++;
1707                         }
1708
1709                         bitmap++;
1710                 }
1711         }
1712 }
1713
1714 //
1715 // Very very crude GUI file selector
1716 //
1717 /*bool UserSelectFile(char * path, char * filename)
1718 {
1719 //Testing...
1720 GUIMain();
1721         
1722         extern int16 * backbuffer;
1723         vector<string> fileList;
1724
1725         // Read in the candidate files from the directory pointed to by "path"
1726
1727         DIR * dp = opendir(path);
1728         dirent * de;
1729
1730         while ((de = readdir(dp)) != NULL)
1731         {
1732                 char * ext = strrchr(de->d_name, '.');
1733
1734                 if (ext != NULL)
1735                         if (stricmp(ext, ".zip") == 0 || stricmp(ext, ".jag") == 0)
1736                                 fileList.push_back(string(de->d_name));
1737         }
1738
1739         closedir(dp);
1740
1741         if (fileList.size() == 0)                                               // Any files found?
1742                 return false;                                                           // Nope. Bail!
1743
1744         // Main GUI selection loop
1745
1746         uint32 cursor = 0, startFile = 0;
1747
1748         if (fileList.size() > 1)        // Only go GUI if more than one possibility!
1749         {
1750                 sort(fileList.begin(), fileList.end());
1751
1752                 bool done = false;
1753                 uint32 limit = (fileList.size() > 30 ? 30 : fileList.size());
1754                 SDL_Event event;
1755
1756                 // Ensure that the GUI is drawn before any user input...
1757                 event.type = SDL_USEREVENT;
1758                 SDL_PushEvent(&event);
1759
1760                 while (!done)
1761                 {
1762                         while (SDL_PollEvent(&event))
1763                         {
1764                                 if (event.type == SDL_KEYDOWN)
1765                                 {
1766                                         SDLKey key = event.key.keysym.sym;
1767
1768                                         if (key == SDLK_DOWN)
1769                                         {
1770                                                 if (cursor != limit - 1)        // Cursor is within its window
1771                                                         cursor++;
1772                                                 else                                            // Otherwise, scroll the window...
1773                                                 {
1774                                                         if (cursor + startFile != fileList.size() - 1)
1775                                                                 startFile++;
1776                                                 }
1777                                         }
1778                                         if (key == SDLK_UP)
1779                                         {
1780                                                 if (cursor != 0)
1781                                                         cursor--;
1782                                                 else
1783                                                 {
1784                                                         if (startFile != 0)
1785                                                                 startFile--;
1786                                                 }
1787                                         }
1788                                         if (key == SDLK_PAGEDOWN)
1789                                         {
1790                                                 if (cursor != limit - 1)
1791                                                         cursor = limit - 1;
1792                                                 else
1793                                                 {
1794                                                         startFile += limit;
1795                                                         if (startFile > fileList.size() - limit)
1796                                                                 startFile = fileList.size() - limit;
1797                                                 }
1798                                         }
1799                                         if (key == SDLK_PAGEUP)
1800                                         {
1801                                                 if (cursor != 0)
1802                                                         cursor = 0;
1803                                                 else
1804                                                 {
1805                                                         if (startFile < limit)
1806                                                                 startFile = 0;
1807                                                         else
1808                                                                 startFile -= limit;
1809                                                 }
1810                                         }
1811                                         if (key == SDLK_RETURN)
1812                                                 done = true;
1813                                         if (key == SDLK_ESCAPE)
1814                                         {
1815                                                 WriteLog("GUI: Aborting VJ by user request.\n");
1816                                                 return false;                                           // Bail out!
1817                                         }
1818                                         if (key >= SDLK_a && key <= SDLK_z)
1819                                         {
1820                                                 // Advance cursor to filename with first letter pressed...
1821                                                 uint8 which = (key - SDLK_a) + 65;      // Convert key to A-Z char
1822
1823                                                 for(uint32 i=0; i<fileList.size(); i++)
1824                                                 {
1825                                                         if ((fileList[i][0] & 0xDF) == which)
1826                                                         {
1827                                                                 cursor = i - startFile;
1828                                                                 if (i > startFile + limit - 1)
1829                                                                         startFile = i - limit + 1,
1830                                                                         cursor = limit - 1;
1831                                                                 if (i < startFile)
1832                                                                         startFile = i,
1833                                                                         cursor = 0;
1834                                                                 break;
1835                                                         }
1836                                                 }
1837                                         }
1838                                 }
1839                                 else if (event.type == SDL_MOUSEMOTION)
1840                                 {
1841                                         mouseX = event.motion.x, mouseY = event.motion.y;
1842                                         if (vjs.useOpenGL)
1843                                                 mouseX /= 2, mouseY /= 2;
1844                                 }
1845                                 else if (event.type == SDL_MOUSEBUTTONDOWN)
1846                                 {
1847                                         uint32 mx = event.button.x, my = event.button.y;
1848                                         if (vjs.useOpenGL)
1849                                                 mx /= 2, my /= 2;
1850                                         cursor = my / 8;
1851                                 }
1852
1853                                 // Draw the GUI...
1854 //                              memset(backbuffer, 0x11, tom_getVideoModeWidth() * tom_getVideoModeHeight() * 2);
1855                                 memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
1856
1857                                 for(uint32 i=0; i<limit; i++)
1858                                 {
1859                                         // Clip our strings to guarantee that they fit on the screen...
1860                                         // (and strip off the extension too)
1861                                         string s(fileList[startFile + i], 0, fileList[startFile + i].length() - 4);
1862                                         if (s.length() > 38)
1863                                                 s[38] = 0;
1864                                         DrawString(backbuffer, 0, i*8, (cursor == i ? true : false), " %s ", s.c_str());
1865                                 }
1866
1867                                 DrawTransparentBitmap(backbuffer, mouseX, mouseY, mousePic);
1868
1869                                 RenderBackbuffer();
1870                         }
1871                 }
1872         }
1873
1874         strcpy(filename, path);
1875
1876         if (strlen(path) > 0)
1877                 if (path[strlen(path) - 1] != '/')
1878                         strcat(filename, "/");
1879
1880         strcat(filename, fileList[startFile + cursor].c_str());
1881
1882         return true;
1883 }*/
1884
1885 //
1886 // Generic ROM loading
1887 //
1888 uint32 JaguarLoadROM(uint8 * rom, char * path)
1889 {
1890         uint32 romSize = 0;
1891
1892         char * ext = strrchr(path, '.');
1893         if (ext != NULL)
1894         {
1895                 WriteLog("VJ: Loading \"%s\"...", path);
1896
1897                 if (stricmp(ext, ".zip") == 0)
1898                 {
1899                         // Handle ZIP file loading here...
1900                         WriteLog("(ZIPped)...");
1901
1902                         if (load_zipped_file(0, 0, path, NULL, &rom, &romSize) == -1)
1903                         {
1904                                 WriteLog("Failed!\n");
1905                                 return 0;
1906                         }
1907                 }
1908                 else
1909                 {
1910 /*                      FILE * fp = fopen(path, "rb");
1911
1912                         if (fp == NULL)
1913                         {
1914                                 WriteLog("Failed!\n");
1915                                 return 0;
1916                         }
1917
1918                         fseek(fp, 0, SEEK_END);
1919                         romSize = ftell(fp);
1920                         fseek(fp, 0, SEEK_SET);
1921                         fread(rom, 1, romSize, fp);
1922                         fclose(fp);*/
1923
1924                         gzFile fp = gzopen(path, "rb");
1925
1926                         if (fp == NULL)
1927                         {
1928                                 WriteLog("Failed!\n");
1929                                 return 0;
1930                         }
1931
1932                         romSize = gzfilelength(fp);
1933                         gzseek(fp, 0, SEEK_SET);
1934                         gzread(fp, rom, romSize);
1935                         gzclose(fp);
1936                 }
1937
1938                 WriteLog("OK (%i bytes)\n", romSize);
1939         }
1940
1941         return romSize;
1942 }
1943
1944 //
1945 // Jaguar cartridge ROM loading
1946 //
1947 void JaguarLoadCart(uint8 * mem, char * path)
1948 {
1949         jaguarRomSize = JaguarLoadROM(mem, path);
1950
1951 //      if (romSize == 0)
1952 //      {
1953 /*              char newPath[2048];
1954                 WriteLog("VJ: Trying GUI...\n");
1955
1956 //This is not *nix friendly for some reason...
1957 //              if (!UserSelectFile(path, newPath))
1958                 if (!UserSelectFile((strlen(path) == 0 ? (char *)"." : path), newPath))
1959                 {
1960                         WriteLog("VJ: Could not find valid ROM in directory \"%s\"...\nAborting!\n", path);
1961                         log_done();
1962                         exit(0);
1963                 }
1964
1965                 romSize = JaguarLoadROM(mem, newPath);
1966 */
1967                 if (jaguarRomSize == 0)
1968                 {
1969 //                      WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
1970                         WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", path);
1971                         log_done();
1972                         exit(0);
1973                 }
1974 //      }
1975
1976         jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, jaguarRomSize);
1977         WriteLog("CRC: %08X\n", (unsigned int)jaguar_mainRom_crc32);
1978         eeprom_init();
1979
1980         jaguarRunAddress = 0x802000;
1981 //NOTE: The bytes 'JAGR' should also be at position $1C...
1982 //      Also, there's *always* a $601A header at position $00...
1983         if (jaguar_mainRom[0] == 0x60 && jaguar_mainRom[1] == 0x1A)
1984         {
1985                 uint32 loadAddress = GET32(jaguar_mainRom, 0x22), runAddress = GET32(jaguar_mainRom, 0x2A);
1986 //This is not always right! Especially when converted via bin2jag1!!!
1987 //We should have access to the length of the furshlumiger file that was loaded anyway!
1988 //Now, we do! ;-)
1989 //                      uint32 progLength = GET32(jaguar_mainRom, 0x02);
1990 //jaguarRomSize
1991 //jaguarRunAddress
1992 //                      WriteLog("Jaguar: Setting up PD ROM... Run address: %08X, length: %08X\n", runAddress, progLength);
1993 //                      memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, progLength);
1994                 WriteLog("Jaguar: Setting up homebrew... Run address: %08X, length: %08X\n", runAddress, jaguarRomSize - 0x2E);
1995                 memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, jaguarRomSize - 0x2E);
1996 //              SET32(jaguar_mainRam, 4, runAddress);
1997                 jaguarRunAddress = runAddress;
1998         }
1999 }
2000
2001 /*
2002 ABS Format sleuthing (LBUGDEMO.ABS):
2003
2004 000000  60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
2005 000010  12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
2006 000020  00 00 40 00
2007
2008 DRI-format file detected...
2009 Text segment size = 0x0000050c bytes
2010 Data segment size = 0x000462c0 bytes
2011 BSS Segment size = 0x00000428 bytes
2012 Symbol Table size = 0x000012a6 bytes
2013 Absolute Address for text segment = 0x00802000
2014 Absolute Address for data segment = 0x0080250c
2015 Absolute Address for BSS segment = 0x00004000
2016
2017 (CRZDEMO.ABS):
2018 000000  01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
2019 000010  00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
2020 000020  00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
2021
2022 000030  2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
2023 000040  00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
2024 000050  00 00 00 00 00 00 00 20
2025 000058  2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
2026 000068  00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
2027 000078  00 00 00 00 00 00 00 40
2028 000080  2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
2029 000090  00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
2030 0000a0  00 00 00 00 00 00 00 80
2031
2032 Header size is $A8 bytes...
2033
2034 BSD/COFF format file detected...
2035 3 sections specified
2036 Symbol Table offset = 230160                            ($00038310)
2037 Symbol Table contains 1339 symbol entries       ($0000053B)
2038 The additional header size is 28 bytes          ($001C)
2039 Magic Number for RUN_HDR = 0x00000107
2040 Text Segment Size = 7632                                        ($00001DD0)
2041 Data Segment Size = 222360                                      ($00036498)
2042 BSS Segment Size = 428928                                       ($00068B80)
2043 Starting Address for executable = 0x00802000
2044 Start of Text Segment = 0x00802000
2045 Start of Data Segment = 0x00803dd0
2046 */
2047
2048
2049 //
2050 // Get the length of a (possibly) gzipped file
2051 //
2052 int gzfilelength(gzFile gd)
2053 {
2054    int size = 0, length = 0;
2055    unsigned char buffer[0x10000];
2056
2057    gzrewind(gd);
2058
2059    do
2060    {
2061       // Read in chunks until EOF
2062       size = gzread(gd, buffer, 0x10000);
2063
2064       if (size <= 0)
2065         break;
2066
2067       length += size;
2068    }
2069    while (!gzeof(gd));
2070
2071    gzrewind(gd);
2072    return length;
2073 }