4 // Graphical User Interface support
13 #include <ctype.h> // For toupper()
24 using namespace std; // For STL stuff
26 // Private function prototypes
28 class Window; // Forward declaration...
30 void DrawTransparentBitmap(int16 * screen, uint32 x, uint32 y, uint16 * bitmap);
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);
38 Window * MiscOptions(void);
40 int gzfilelength(gzFile gd);
44 extern uint8 * jaguar_mainRam;
45 extern uint8 * jaguar_bootRom;
46 extern uint8 * jaguar_mainRom;
48 // Local global variables
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 // @
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 // @
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
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 // .....
92 uint16 upArrowBox[] = {
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 // ........
105 uint16 downArrowBox[] = {
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 // ........
118 uint16 pushButtonUp[] = {
121 0x0000,0x0000,0x0000,0x000A,0x000A,0x0000,0x0000,0x0000, // ...##...
122 0x0000,0x000A,0x000A,0x000A,0x000A,0x000A,0x000A,0x0000, // .######.
123 0x0000,0x000A,0x000A,0x000A,0x000A,0x000A,0x000A,0x0000, // .######.
124 0x000A,0x000A,0x000A,0x000A,0x000A,0x000A,0x000A,0x000A, // ########
125 0x000A,0x000A,0x000A,0x000A,0x000A,0x000A,0x000A,0x000A, // ########
126 0x0000,0x000A,0x000A,0x000A,0x000A,0x000A,0x000A,0x0000, // .######.
127 0x0000,0x000A,0x000A,0x000A,0x000A,0x000A,0x000A,0x0000, // .######.
128 0x0000,0x0000,0x0000,0x000A,0x000A,0x0000,0x0000,0x0000 // ...##...
131 uint16 pushButtonDown[] = {
134 0x0000,0x0000,0x0000,0x000A,0x000A,0x0000,0x0000,0x0000, // ...@@...
135 0x0000,0x000A,0x000A,0x0C7F,0x0C7F,0x000A,0x000A,0x0000, // .@@##@@.
136 0x0000,0x000A,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x000A,0x0000, // .@####@.
137 0x000A,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x000A, // @######@
138 0x000A,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x000A, // @######@
139 0x0000,0x000A,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x000A,0x0000, // .@####@.
140 0x0000,0x000A,0x000A,0x0C7F,0x0C7F,0x000A,0x000A,0x0000, // .@@##@@.
141 0x0000,0x0000,0x0000,0x000A,0x000A,0x0000,0x0000,0x0000 // ...@@...
144 uint16 slideSwitchUp[] = {
147 0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E, // ++++++++
148 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
149 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
150 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
151 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
152 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
153 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
154 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
155 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
156 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
157 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
158 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
159 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
160 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
161 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
162 0x0217,0x0217,0x0217,0x0217,0x0217,0x0217,0x0217,0x0217 // ........
165 uint16 slideSwitchDown[] = {
168 0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E,0x4B5E, // ++++++++
169 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
170 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
171 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
172 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
173 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
174 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
175 0x4B5E,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0217, // +.......
176 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
177 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
178 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
179 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
180 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
181 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
182 0x4B5E,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0C7F,0x0217, // + .
183 0x0217,0x0217,0x0217,0x0217,0x0217,0x0217,0x0217,0x0217 // ........
189 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ........
190 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ........
191 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ........
192 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ........
193 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ........
194 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ........
195 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, // ........
196 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 // ........
199 char separator[] = "--------------------------------------------------------";
201 uint16 background[1280 * 240];
204 // Case insensitive string compare function
205 // Taken straight out of Thinking In C++ by Bruce Eckel. Thanks Bruce!
208 int stringCmpi(const string &s1, const string &s2)
210 // Select the first element of each string:
211 string::const_iterator p1 = s1.begin(), p2 = s2.begin();
213 while (p1 != s1.end() && p2 != s2.end()) // Don
\92t run past the end
215 if (toupper(*p1) != toupper(*p2)) // Compare upper-cased chars
216 return (toupper(*p1) < toupper(*p2) ? -1 : 1);// Report which was lexically greater
222 // If they match up to the detected eos, say which was longer. Return 0 if the same.
223 return s2.size() - s1.size();
230 enum { WINDOW_CLOSE, MENU_ITEM_CHOSEN };
235 Element(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0)
236 { extents.x = x, extents.y = y, extents.w = w, extents.h = h; }
237 virtual void HandleKey(SDLKey key) = 0;
238 virtual void HandleMouseMove(uint32 x, uint32 y) = 0;
239 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) = 0;
240 virtual void Draw(uint32, uint32) = 0;
241 virtual void Notify(Element *) = 0;
242 //Needed? virtual ~Element() = 0;
243 //We're not allocating anything in the base class, so the answer would be NO.
244 bool Inside(uint32 x, uint32 y);
246 static void SetScreenAndPitch(int16 * s, uint32 p) { screenBuffer = s, pitch = p; }
251 // Class variables...
252 static int16 * screenBuffer;
256 int16 * Element::screenBuffer = NULL;
257 uint32 Element::pitch = 0;
259 bool Element::Inside(uint32 x, uint32 y)
261 return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
262 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false);
266 class Button: public Element
269 Button(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
270 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
271 bgColor(0x03E0), pic(NULL), elementToTell(NULL) {}
272 Button(uint32 x, uint32 y, uint32 w, uint32 h, uint16 * p): Element(x, y, w, h),
273 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
274 bgColor(0x03E0), pic(p), elementToTell(NULL) {}
275 Button(uint32 x, uint32 y, uint16 * p): Element(x, y, 0, 0),
276 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
277 bgColor(0x03E0), pic(p), elementToTell(NULL)
278 { if (pic) extents.w = pic[0], extents.h = pic[1]; }
279 Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
280 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
281 bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL) {}
282 Button(uint32 x, uint32 y, string s): Element(x, y, 0, 8),
283 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
284 bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL)
285 { extents.w = s.length() * 8; }
286 virtual void HandleKey(SDLKey key) {}
287 virtual void HandleMouseMove(uint32 x, uint32 y);
288 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
289 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
290 virtual void Notify(Element *) {}
291 bool ButtonClicked(void) { return activated; }
292 void SetNotificationElement(Element * e) { elementToTell = e; }
295 bool activated, clicked, inside;
296 uint16 fgColor, bgColor;
299 Element * elementToTell;
302 void Button::HandleMouseMove(uint32 x, uint32 y)
304 inside = Inside(x, y);
307 void Button::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
314 if (clicked && !mouseDown)
316 clicked = false, activated = true;
318 // Send a message that we're activated (if there's someone to tell, that is)
320 elementToTell->Notify(this);
324 clicked = activated = false;
327 void Button::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
329 uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
331 for(uint32 y=0; y<extents.h; y++)
333 for(uint32 x=0; x<extents.w; x++)
335 // Doesn't clip in y axis! !!! FIX !!!
336 if (extents.x + x < pitch)
337 screenBuffer[addr + x + (y * pitch)]
338 = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
343 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, pic);
345 if (text.length() > 0)
346 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
350 class PushButton: public Element
353 // Save state externally?
356 // PushButton(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
357 // activated(false), clicked(false), inside(false), fgColor(0xFFFF),
358 // bgColor(0x03E0), pic(NULL), elementToTell(NULL) {}
359 PushButton(uint32 x, uint32 y, bool * st, string s): Element(x, y, 8, 8), state(st),
360 inside(false), text(s) { if (st == NULL) state = &internalState; }
361 /* Button(uint32 x, uint32 y, uint32 w, uint32 h, uint16 * p): Element(x, y, w, h),
362 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
363 bgColor(0x03E0), pic(p), elementToTell(NULL) {}
364 Button(uint32 x, uint32 y, uint16 * p): Element(x, y, 0, 0),
365 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
366 bgColor(0x03E0), pic(p), elementToTell(NULL)
367 { if (pic) extents.w = pic[0], extents.h = pic[1]; }
368 Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
369 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
370 bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL) {}
371 PushButton(uint32 x, uint32 y, string s): Element(x, y, 0, 8),
372 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
373 bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL)
374 { extents.w = s.length() * 8; }*/
375 virtual void HandleKey(SDLKey key) {}
376 virtual void HandleMouseMove(uint32 x, uint32 y);
377 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
378 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
379 virtual void Notify(Element *) {}
380 // bool ButtonClicked(void) { return activated; }
381 // void SetNotificationElement(Element * e) { elementToTell = e; }
386 // bool activated, clicked, inside;
387 // uint16 fgColor, bgColor;
390 // Element * elementToTell;
394 void PushButton::HandleMouseMove(uint32 x, uint32 y)
396 inside = Inside(x, y);
399 void PushButton::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
401 if (inside && mouseDown)
406 if (clicked && !mouseDown)
408 clicked = false, activated = true;
410 // Send a message that we're activated (if there's someone to tell, that is)
412 elementToTell->Notify(this);
417 // clicked = activated = false;
420 void PushButton::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
422 /* uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
424 for(uint32 y=0; y<extents.h; y++)
426 for(uint32 x=0; x<extents.w; x++)
428 // Doesn't clip in y axis! !!! FIX !!!
429 if (extents.x + x < pitch)
430 screenBuffer[addr + x + (y * pitch)]
431 = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
435 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, (*state ? pushButtonDown : pushButtonUp));
436 if (text.length() > 0)
437 DrawString(screenBuffer, extents.x + offsetX + 12, extents.y + offsetY, false, "%s", text.c_str());
441 class SlideSwitch: public Element
444 // Save state externally?
447 SlideSwitch(uint32 x, uint32 y, bool * st, string s1, string s2): Element(x, y, 8, 16), state(st),
448 inside(false), text1(s1), text2(s2) {}
449 virtual void HandleKey(SDLKey key) {}
450 virtual void HandleMouseMove(uint32 x, uint32 y);
451 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
452 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
453 virtual void Notify(Element *) {}
454 // bool ButtonClicked(void) { return activated; }
455 // void SetNotificationElement(Element * e) { elementToTell = e; }
460 // bool activated, clicked, inside;
461 // uint16 fgColor, bgColor;
464 // Element * elementToTell;
467 void SlideSwitch::HandleMouseMove(uint32 x, uint32 y)
469 inside = Inside(x, y);
472 void SlideSwitch::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
474 if (inside && mouseDown)
479 if (clicked && !mouseDown)
481 clicked = false, activated = true;
483 // Send a message that we're activated (if there's someone to tell, that is)
485 elementToTell->Notify(this);
490 // clicked = activated = false;
493 void SlideSwitch::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
495 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, (*state ? slideSwitchDown : slideSwitchUp));
496 if (text1.length() > 0)
497 DrawString(screenBuffer, extents.x + offsetX + 12, extents.y + offsetY, false, "%s", text1.c_str());
498 if (text2.length() > 0)
499 DrawString(screenBuffer, extents.x + offsetX + 12, extents.y + offsetY + 8, false, "%s", text2.c_str());
503 class Window: public Element
506 /* Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
507 fgColor(0x4FF0), bgColor(0xFE10)
508 { close = new Button(w - 8, 1, closeBox); list.push_back(close); }*/
509 Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0,
510 void (* f)(Element *) = NULL): Element(x, y, w, h),
511 /*clicked(false), inside(false),*/ fgColor(0x4FF0), bgColor(0x1E10),
513 { close = new Button(w - 8, 1, closeBox); list.push_back(close);
514 close->SetNotificationElement(this); }
516 virtual void HandleKey(SDLKey key);
517 virtual void HandleMouseMove(uint32 x, uint32 y);
518 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
519 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
520 virtual void Notify(Element * e);
521 void AddElement(Element * e);
522 // bool WindowActive(void) { return true; }//return !close->ButtonClicked(); }
525 // bool clicked, inside;
526 uint16 fgColor, bgColor;
527 void (* handler)(Element *);
529 //We have to use a list of Element *pointers* because we can't make a list that will hold
530 //all the different object types in the same list...
531 vector<Element *> list;
536 for(uint32 i=0; i<list.size(); i++)
541 void Window::HandleKey(SDLKey key)
543 if (key == SDLK_ESCAPE)
546 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
547 SDL_PushEvent(&event);
550 // Handle the items this window contains...
551 for(uint32 i=0; i<list.size(); i++)
552 // Make coords relative to upper right corner of this window...
553 list[i]->HandleKey(key);
556 void Window::HandleMouseMove(uint32 x, uint32 y)
558 // Handle the items this window contains...
559 for(uint32 i=0; i<list.size(); i++)
560 // Make coords relative to upper right corner of this window...
561 list[i]->HandleMouseMove(x - extents.x, y - extents.y);
564 void Window::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
566 // Handle the items this window contains...
567 for(uint32 i=0; i<list.size(); i++)
568 // Make coords relative to upper right corner of this window...
569 list[i]->HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
572 void Window::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
574 uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
576 for(uint32 y=0; y<extents.h; y++)
578 for(uint32 x=0; x<extents.w; x++)
580 // Doesn't clip in y axis! !!! FIX !!!
581 if (extents.x + x < pitch)
582 screenBuffer[addr + x + (y * pitch)] = bgColor;
586 // Handle the items this window contains...
587 for(uint32 i=0; i<list.size(); i++)
588 list[i]->Draw(extents.x, extents.y);
591 void Window::AddElement(Element * e)
596 void Window::Notify(Element * e)
601 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
602 SDL_PushEvent(&event);
607 class Text: public Element
610 Text(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
611 fgColor(0x4FF0), bgColor(0xFE10) {}
612 Text(uint32 x, uint32 y, string s): Element(x, y, 0, 0),
613 fgColor(0x4FF0), bgColor(0xFE10), text(s) {}
614 virtual void HandleKey(SDLKey key) {}
615 virtual void HandleMouseMove(uint32 x, uint32 y) {}
616 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
617 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
618 virtual void Notify(Element *) {}
621 uint16 fgColor, bgColor;
625 void Text::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
627 if (text.length() > 0)
628 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
632 class ListBox: public Element
633 //class ListBox: public Window
636 // ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
637 ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);//: Window(x, y, w, h),
638 // windowPtr(0), cursor(0), limit(0), charWidth((w / 8) - 1), charHeight(h / 8),
639 // elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
640 // downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox) {}
641 virtual void HandleKey(SDLKey key);
642 virtual void HandleMouseMove(uint32 x, uint32 y);
643 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
644 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
645 virtual void Notify(Element * e);
646 void SetNotificationElement(Element * e) { elementToTell = e; }
647 void AddItem(string s);
648 string GetSelectedItem(void);
652 uint32 windowPtr, cursor, limit;
653 uint32 charWidth, charHeight; // Box width/height in characters
654 Element * elementToTell;
655 Button upArrow, downArrow, upArrow2;
659 uint32 yRelativePoint;
662 ListBox::ListBox(uint32 x, uint32 y, uint32 w, uint32 h): Element(x, y, w, h),
663 thumbClicked(false), windowPtr(0), cursor(0), limit(0), charWidth((w / 8) - 1),
664 charHeight(h / 8), elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
665 downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox)
667 upArrow.SetNotificationElement(this);
668 downArrow.SetNotificationElement(this);
669 upArrow2.SetNotificationElement(this);
670 extents.w -= 8; // Make room for scrollbar...
673 void ListBox::HandleKey(SDLKey key)
675 if (key == SDLK_DOWN)
677 if (cursor != limit - 1) // Cursor is within its window
679 else // Otherwise, scroll the window...
681 if (cursor + windowPtr != item.size() - 1)
685 else if (key == SDLK_UP)
695 else if (key == SDLK_PAGEDOWN)
697 if (cursor != limit - 1)
702 if (windowPtr > item.size() - limit)
703 windowPtr = item.size() - limit;
706 else if (key == SDLK_PAGEUP)
712 if (windowPtr < limit)
718 else if (key >= SDLK_a && key <= SDLK_z)
720 // Advance cursor to filename with first letter pressed...
721 uint8 which = (key - SDLK_a) + 65; // Convert key to A-Z char
723 for(uint32 i=0; i<item.size(); i++)
725 if ((item[i][0] & 0xDF) == which)
727 cursor = i - windowPtr;
728 if (i > windowPtr + limit - 1)
729 windowPtr = i - limit + 1, cursor = limit - 1;
731 windowPtr = i, cursor = 0;
738 void ListBox::HandleMouseMove(uint32 x, uint32 y)
740 upArrow.HandleMouseMove(x - extents.x, y - extents.y);
741 downArrow.HandleMouseMove(x - extents.x, y - extents.y);
742 upArrow2.HandleMouseMove(x - extents.x, y - extents.y);
746 uint32 sbHeight = extents.h - 24,
747 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight);
749 //yRelativePoint is the spot on the thumb where we clicked...
750 int32 newThumbStart = y - yRelativePoint;
752 if (newThumbStart < 0)
755 if (newThumbStart > sbHeight - thumb)
756 newThumbStart = sbHeight - thumb;
758 windowPtr = (uint32)(((float)newThumbStart / (float)sbHeight) * (float)item.size());
759 //Check for cursor bounds as well... Or do we need to???
760 //Actually, we don't...!
764 void ListBox::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
766 if (Inside(x, y) && mouseDown)
768 // Why do we have to do this??? (- extents.y?)
769 // I guess it's because only the Window class has offsetting implemented...
770 cursor = (y - extents.y) / 8;
773 // Check for a hit on the scrollbar...
774 if (x > (extents.x + extents.w) && x <= (extents.x + extents.w + 8)
775 && y > (extents.y + 8) && y <= (extents.y + extents.h - 16))
779 // This shiaut should be calculated in AddItem(), not here... (or in Draw() for that matter)
780 uint32 sbHeight = extents.h - 24,
781 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
782 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
784 // Did we hit the thumb?
785 if (y >= (extents.y + 8 + thumbStart) && y < (extents.y + 8 + thumbStart + thumb))
786 thumbClicked = true, yRelativePoint = y - thumbStart;
789 thumbClicked = false;
792 upArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
793 downArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
794 upArrow2.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
797 void ListBox::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
799 for(uint32 i=0; i<limit; i++)
801 // Strip off the extension
802 // (extension stripping should be an option, not default!)
803 string s(item[windowPtr + i], 0, item[windowPtr + i].length() - 4);
804 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*8,
805 (cursor == i ? true : false), "%-*.*s", charWidth, charWidth, s.c_str());
808 upArrow.Draw(extents.x + offsetX, extents.y + offsetY);
809 downArrow.Draw(extents.x + offsetX, extents.y + offsetY);
810 upArrow2.Draw(extents.x + offsetX, extents.y + offsetY);
812 uint32 sbHeight = extents.h - 24,
813 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
814 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
816 for(uint32 y=extents.y+offsetY+8; y<extents.y+offsetY+extents.h-16; y++)
818 // for(uint32 x=extents.x+offsetX+extents.w-8; x<extents.x+offsetX+extents.w; x++)
819 for(uint32 x=extents.x+offsetX+extents.w; x<extents.x+offsetX+extents.w+8; x++)
821 if (y >= thumbStart + (extents.y+offsetY+8) && y < thumbStart + thumb + (extents.y+offsetY+8))
822 screenBuffer[x + (y * pitch)] = (thumbClicked ? 0x458E : 0xFFFF);
824 screenBuffer[x + (y * pitch)] = 0x0200;
829 void ListBox::Notify(Element * e)
831 if (e == &upArrow || e == &upArrow2)
837 if (cursor < limit - 1)
841 else if (e == &downArrow)
843 if (windowPtr < item.size() - limit)
853 void ListBox::AddItem(string s)
855 // Do a simple insertion sort
856 bool inserted = false;
858 for(vector<string>::iterator i=item.begin(); i<item.end(); i++)
860 if (stringCmpi(s, *i) == -1)
871 limit = (item.size() > charHeight ? charHeight : item.size());
874 string ListBox::GetSelectedItem(void)
876 return item[windowPtr + cursor];
880 class FileList: public Window
883 FileList(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);
884 virtual ~FileList() {}
885 virtual void HandleKey(SDLKey key);
886 virtual void HandleMouseMove(uint32 x, uint32 y) { Window::HandleMouseMove(x, y); }
887 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) { Window::HandleMouseButton(x, y, mouseDown); }
888 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) { Window::Draw(offsetX, offsetY); }
889 virtual void Notify(Element * e);
896 //Need 4 buttons, one scrollbar...
897 FileList::FileList(uint32 x, uint32 y, uint32 w, uint32 h): Window(x, y, w, h)
899 files = new ListBox(8, 8, w - 16, h - 32);
901 load = new Button(8, h - 16, " Load ");
903 load->SetNotificationElement(this);
905 DIR * dp = opendir(vjs.ROMPath);
908 while ((de = readdir(dp)) != NULL)
910 char * ext = strrchr(de->d_name, '.');
913 if (stricmp(ext, ".zip") == 0 || stricmp(ext, ".jag") == 0)
914 files->AddItem(string(de->d_name));
920 void FileList::HandleKey(SDLKey key)
922 if (key == SDLK_RETURN)
925 Window::HandleKey(key);
928 void FileList::Notify(Element * e)
932 char filename[MAX_PATH];
933 strcpy(filename, vjs.ROMPath);
935 if (strlen(filename) > 0)
936 if (filename[strlen(filename) - 1] != '/')
937 strcat(filename, "/");
939 strcat(filename, files->GetSelectedItem().c_str());
941 // uint32 romSize = JaguarLoadROM(jaguar_mainRom, filename);
942 JaguarLoadCart(jaguar_mainRom, filename);
945 //We need better error checking here... !!! FIX !!!
946 // WriteLog("VJ: Could not load ROM from file \"%s\"...", files->GetSelectedItem().c_str());
949 // jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, romSize);
950 // WriteLog("CRC: %08X\n", (unsigned int)jaguar_mainRom_crc32);
954 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
955 SDL_PushEvent(&event);
957 event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
958 event.user.data1 = (void *)ResetJaguar;
959 SDL_PushEvent(&event);
970 Window * (* action)(void);
973 NameAction(string n, Window * (* a)(void) = NULL, SDLKey k = SDLK_UNKNOWN): name(n),
974 action(a), hotKey(k) {}
981 MenuItems(): charLength(0) {}
982 bool Inside(uint32 x, uint32 y)
983 { return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
984 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false); }
987 vector<NameAction> item;
992 class Menu: public Element
995 Menu(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 8,
996 uint16 fgc = 0x1CFF, uint16 bgc = 0x000F, uint16 fgch = 0x421F,
997 uint16 bgch = 0x1CFF): Element(x, y, w, h), activated(false), clicked(false),
998 inside(0), insidePopup(0), fgColor(fgc), bgColor(bgc), fgColorHL(fgch),
999 bgColorHL(bgch), menuChosen(-1), menuItemChosen(-1) {}
1000 virtual void HandleKey(SDLKey key);
1001 virtual void HandleMouseMove(uint32 x, uint32 y);
1002 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
1003 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
1004 virtual void Notify(Element *) {}
1005 void Add(MenuItems mi);
1008 bool activated, clicked;
1009 uint32 inside, insidePopup;
1010 uint16 fgColor, bgColor, fgColorHL, bgColorHL;
1011 int menuChosen, menuItemChosen;
1014 vector<MenuItems> itemList;
1017 void Menu::HandleKey(SDLKey key)
1019 for(uint32 i=0; i<itemList.size(); i++)
1021 for(uint32 j=0; j<itemList[i].item.size(); j++)
1023 if (itemList[i].item[j].hotKey == key)
1026 event.type = SDL_USEREVENT;
1027 event.user.code = MENU_ITEM_CHOSEN;
1028 event.user.data1 = (void *)itemList[i].item[j].action;
1029 SDL_PushEvent(&event);
1031 clicked = false, menuChosen = menuItemChosen = -1;
1038 void Menu::HandleMouseMove(uint32 x, uint32 y)
1040 inside = insidePopup = 0;
1044 // Find out *where* we are inside the menu bar
1045 uint32 xpos = extents.x;
1047 for(uint32 i=0; i<itemList.size(); i++)
1049 uint32 width = (itemList[i].title.length() + 2) * 8;
1051 if (x >= xpos && x < xpos + width)
1062 if (!Inside(x, y) && !clicked)
1067 if (itemList[menuChosen].Inside(x, y) && clicked)
1069 insidePopup = ((y - itemList[menuChosen].extents.y) / 8) + 1;
1070 menuItemChosen = insidePopup - 1;
1074 void Menu::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
1083 menuChosen = -1; // clicked is already false...!
1086 else // clicked == true
1088 if (insidePopup && !mouseDown) // I.e., mouse-button-up
1091 if (itemList[menuChosen].item[menuItemChosen].action != NULL)
1093 // itemList[menuChosen].item[menuItemChosen].action();
1095 event.type = SDL_USEREVENT;
1096 event.user.code = MENU_ITEM_CHOSEN;
1097 event.user.data1 = (void *)itemList[menuChosen].item[menuItemChosen].action;
1098 SDL_PushEvent(&event);
1100 clicked = false, menuChosen = menuItemChosen = -1;
1103 while (SDL_PollEvent(&event)); // Flush the event queue...
1104 event.type = SDL_MOUSEMOTION;
1106 SDL_GetMouseState(&mx, &my);
1107 event.motion.x = mx, event.motion.y = my;
1108 SDL_PushEvent(&event); // & update mouse position...!
1112 if (!inside && !insidePopup && mouseDown)
1113 clicked = false, menuChosen = menuItemChosen = -1;
1117 void Menu::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
1119 uint32 xpos = extents.x + offsetX;
1121 for(uint32 i=0; i<itemList.size(); i++)
1123 uint16 color1 = fgColor, color2 = bgColor;
1124 if (inside == (i + 1) || (menuChosen != -1 && (uint32)menuChosen == i))
1125 color1 = fgColorHL, color2 = bgColorHL;
1127 DrawStringOpaque(screenBuffer, xpos, extents.y + offsetY, color1, color2,
1128 " %s ", itemList[i].title.c_str());
1129 xpos += (itemList[i].title.length() + 2) * 8;
1132 // Draw sub menu (but only if active)
1135 uint32 ypos = extents.y + 9;
1137 for(uint32 i=0; i<itemList[menuChosen].item.size(); i++)
1139 uint16 color1 = fgColor, color2 = bgColor;
1141 if (insidePopup == i + 1)
1142 color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
1144 if (itemList[menuChosen].item[i].name.length() > 0)
1145 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
1146 color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
1147 itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
1149 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
1150 fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
1157 void Menu::Add(MenuItems mi)
1159 for(uint32 i=0; i<mi.item.size(); i++)
1160 if (mi.item[i].name.length() > mi.charLength)
1161 mi.charLength = mi.item[i].name.length();
1163 // Set extents here as well...
1164 mi.extents.x = extents.x + extents.w, mi.extents.y = extents.y + 9;
1165 mi.extents.w = (mi.charLength + 2) * 8, mi.extents.h = mi.item.size() * 8;
1167 itemList.push_back(mi);
1168 extents.w += (mi.title.length() + 2) * 8;
1172 //Do we even *need* this?
1173 class RootWindow: public Window
1176 RootWindow(Menu * m, Window * w = NULL): menu(m), window(w) {}
1177 //Do we even need to care about this crap?
1178 // { extents.x = extents.y = 0, extents.w = 320, extents.h = 240; }
1179 virtual void HandleKey(SDLKey key) {}
1180 virtual void HandleMouseMove(uint32 x, uint32 y) {}
1181 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
1182 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) {}
1183 virtual void Notify(Element *) {}
1188 int16 * rootImage[1280 * 240 * 2];
1194 // GUI stuff--it's not crunchy, it's GUI! ;-)
1199 SDL_ShowCursor(SDL_DISABLE);
1200 SDL_GetMouseState(&mouseX, &mouseY);
1208 // Draw text at the given x/y coordinates. Can invert text as well.
1210 void DrawString(int16 * screen, uint32 x, uint32 y, bool invert, const char * text, ...)
1215 va_start(arg, text);
1216 vsprintf(string, text, arg);
1219 uint32 pitch = GetSDLScreenPitch() / 2; // Returns pitch in bytes but we need words...
1220 uint32 length = strlen(string), address = x + (y * pitch);
1222 for(uint32 i=0; i<length; i++)
1224 uint32 fontAddr = (uint32)string[i] * 64;
1226 for(uint32 yy=0; yy<8; yy++)
1228 for(uint32 xx=0; xx<8; xx++)
1230 if ((font1[fontAddr] && !invert) || (!font1[fontAddr] && invert))
1231 *(screen + address + xx + (yy * pitch)) = 0xFE00;
1241 // Draw text at the given x/y coordinates, using FG/BG colors.
1243 void DrawStringOpaque(int16 * screen, uint32 x, uint32 y, uint16 color1, uint16 color2, const char * text, ...)
1248 va_start(arg, text);
1249 vsprintf(string, text, arg);
1252 uint32 pitch = GetSDLScreenPitch() / 2; // Returns pitch in bytes but we need words...
1253 uint32 length = strlen(string), address = x + (y * pitch);
1255 for(uint32 i=0; i<length; i++)
1257 uint32 fontAddr = (uint32)string[i] * 64;
1259 for(uint32 yy=0; yy<8; yy++)
1261 for(uint32 xx=0; xx<8; xx++)
1263 *(screen + address + xx + (yy * pitch)) = (font1[fontAddr] ? color1 : color2);
1273 // Draw text at the given x/y coordinates with transparency (0 is fully opaque, 32 is fully transparent).
1275 void DrawStringTrans(int16 * screen, uint32 x, uint32 y, uint16 color, uint8 trans, const char * text, ...)
1280 va_start(arg, text);
1281 vsprintf(string, text, arg);
1284 uint32 pitch = GetSDLScreenPitch() / 2; // Returns pitch in bytes but we need words...
1285 uint32 length = strlen(string), address = x + (y * pitch);
1287 for(uint32 i=0; i<length; i++)
1289 uint32 fontAddr = (uint32)string[i] * 64;
1291 for(uint32 yy=0; yy<8; yy++)
1293 for(uint32 xx=0; xx<8; xx++)
1295 if (font1[fontAddr])
1297 uint16 existingColor = *(screen + address + xx + (yy * pitch));
1299 uint8 eRed = (existingColor >> 10) & 0x1F,
1300 eGreen = (existingColor >> 5) & 0x1F,
1301 eBlue = existingColor & 0x1F,
1302 //This could be done ahead of time, instead of on each pixel...
1303 nRed = (color >> 10) & 0x1F,
1304 nGreen = (color >> 5) & 0x1F,
1305 nBlue = color & 0x1F;
1307 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
1308 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
1309 //because dividing by 32 is faster than dividing by 31...!
1310 uint8 invTrans = 32 - trans;
1311 uint16 bRed = (eRed * trans + nRed * invTrans) / 32;
1312 uint16 bGreen = (eGreen * trans + nGreen * invTrans) / 32;
1313 uint16 bBlue = (eBlue * trans + nBlue * invTrans) / 32;
1315 uint16 blendedColor = (bRed << 10) | (bGreen << 5) | bBlue;
1317 *(screen + address + xx + (yy * pitch)) = blendedColor;
1333 extern int16 * backbuffer;
1336 Window * mainWindow = NULL;
1338 // Set up the GUI classes...
1339 Element::SetScreenAndPitch(backbuffer, GetSDLScreenPitch() / 2);
1344 mi.item.push_back(NameAction("Load...", LoadROM, SDLK_l));
1345 mi.item.push_back(NameAction("Reset", ResetJaguar));
1346 mi.item.push_back(NameAction("Run", RunEmu, SDLK_ESCAPE));
1347 mi.item.push_back(NameAction(""));
1348 mi.item.push_back(NameAction("Quit", Quit, SDLK_q));
1350 mi.title = "Settings";
1352 mi.item.push_back(NameAction("Video..."));
1353 mi.item.push_back(NameAction("Audio..."));
1354 mi.item.push_back(NameAction("Misc...", MiscOptions, SDLK_m));
1358 mi.item.push_back(NameAction("About...", About));
1361 bool showMouse = true;
1363 //This is crappy!!! !!! FIX !!!
1366 // Set up our background save...
1367 memset(background, 0x11, tom_getVideoModeWidth() * 240 * 2);
1371 while (SDL_PollEvent(&event))
1373 if (event.type == SDL_USEREVENT)
1375 if (event.user.code == WINDOW_CLOSE)
1380 else if (event.user.code == MENU_ITEM_CHOSEN)
1382 // Confused? Let me enlighten... What we're doing here is casting
1383 // data1 as a pointer to a function which returns a Window pointer and
1384 // which takes no parameters (the "(Window *(*)(void))" part), then
1385 // derefencing it (the "*" in front of that) in order to call the
1386 // function that it points to. Clear as mud? Yeah, I hate function
1387 // pointers too, but what else are you gonna do?
1388 mainWindow = (*(Window *(*)(void))event.user.data1)();
1390 while (SDL_PollEvent(&event)); // Flush the event queue...
1391 event.type = SDL_MOUSEMOTION;
1393 SDL_GetMouseState(&mx, &my);
1394 event.motion.x = mx, event.motion.y = my;
1395 SDL_PushEvent(&event); // & update mouse position...!
1397 mouseX = mx, mouseY = my; // This prevents "mouse flash"...
1399 mouseX /= 2, mouseY /= 2;
1402 else if (event.type == SDL_ACTIVEEVENT)
1404 if (event.active.state == SDL_APPMOUSEFOCUS)
1405 showMouse = (event.active.gain ? true : false);
1407 else if (event.type == SDL_KEYDOWN)
1410 mainWindow->HandleKey(event.key.keysym.sym);
1412 mainMenu.HandleKey(event.key.keysym.sym);
1414 else if (event.type == SDL_MOUSEMOTION)
1416 mouseX = event.motion.x, mouseY = event.motion.y;
1419 mouseX /= 2, mouseY /= 2;
1422 mainWindow->HandleMouseMove(mouseX, mouseY);
1424 mainMenu.HandleMouseMove(mouseX, mouseY);
1426 else if (event.type == SDL_MOUSEBUTTONDOWN)
1428 uint32 mx = event.button.x, my = event.button.y;
1434 mainWindow->HandleMouseButton(mx, my, true);
1436 mainMenu.HandleMouseButton(mx, my, true);
1438 else if (event.type == SDL_MOUSEBUTTONUP)
1440 uint32 mx = event.button.x, my = event.button.y;
1446 mainWindow->HandleMouseButton(mx, my, false);
1448 mainMenu.HandleMouseButton(mx, my, false);
1452 // The way we do things here is kinda stupid (redrawing the screen every frame), but
1453 // it's simple. Perhaps there may be a reason down the road to be more selective with
1454 // our clearing, but for now, this will suffice.
1455 // memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
1456 memcpy(backbuffer, background, tom_getVideoModeWidth() * 240 * 2);
1463 DrawTransparentBitmap(backbuffer, mouseX, mouseY, mousePic);
1473 // GUI "action" functions
1475 Window * LoadROM(void)
1477 FileList * fileList = new FileList(8, 16, 304, 216);
1479 return (Window *)fileList;
1482 Window * ResetJaguar(void)
1488 bool debounceRunKey = true;
1489 Window * RunEmu(void)
1491 //This is crappy... !!! FIX !!!
1492 extern int16 * backbuffer;
1493 extern bool finished, showGUI;
1495 uint32 nFrame = 0, nFrameskip = 0;
1496 uint32 totalFrames = 0;
1498 bool showMessage = true;
1499 uint32 showMsgFrames = 120;
1500 uint8 transparency = 0;
1501 // Pass a message to the "joystick" code to debounce the ESC key...
1502 debounceRunKey = true;
1504 uint32 cartType = 2;
1505 if (jaguarRomSize == 0x200000)
1507 else if (jaguarRomSize == 0x400000)
1510 char * cartTypeName[3] = { "2M Cartridge", "4M Cartridge", "Homebrew" };
1514 // Set up new backbuffer with new pixels and data
1515 JaguarExecute(backbuffer, true);
1517 //WriteLog("Frame #%u...\n", totalFrames);
1518 //extern bool doDSPDis;
1519 //if (totalFrames == 373)
1522 //This sucks... !!! FIX !!!
1527 // Some QnD GUI stuff here...
1530 extern uint32 gpu_pc, dsp_pc;
1531 DrawString(backbuffer, 8, 8, false, "GPU PC: %08X", gpu_pc);
1532 DrawString(backbuffer, 8, 16, false, "DSP PC: %08X", dsp_pc);
1537 DrawStringTrans(backbuffer, 8, 24*8, 0xFF0F, transparency, "Running...");
1538 DrawStringTrans(backbuffer, 8, 26*8, 0x3FE3, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
1539 DrawStringTrans(backbuffer, 8, 27*8, 0x3FE3, transparency, "CRC: %08X", jaguar_mainRom_crc32);
1541 if (showMsgFrames == 0)
1545 if (transparency == 33)
1547 showMessage = false;
1548 /*extern bool doGPUDis;
1549 doGPUDis = true;//*/
1558 if (nFrame == nFrameskip)
1567 // Reset the pitch, since it may have been changed in-game...
1568 Element::SetScreenAndPitch(backbuffer, GetSDLScreenPitch() / 2);
1570 // Save the background for the GUI...
1571 // memcpy(background, backbuffer, tom_getVideoModeWidth() * 240 * 2);
1572 // In this case, we squash the color to monochrome, then force it to blue + green...
1573 for(uint32 i=0; i<tom_getVideoModeWidth() * 240; i++)
1575 uint16 word = backbuffer[i];
1576 uint8 r = (word >> 10) & 0x1F, g = (word >> 5) & 0x1F, b = word & 0x1F;
1577 word = ((r + g + b) / 3) & 0x001F;
1578 word = (word << 5) | word;
1579 background[i] = word;
1587 //This is crap. We need some REAL exit code, instead of this psuedo crap... !!! FIX !!!
1588 WriteLog("GUI: Quitting due to user request.\n");
1594 VideoDone(); // Free SDL components last...!
1599 return NULL; // We never get here...
1602 Window * About(void)
1604 Window * window = new Window(8, 16, 304, 160);
1605 window->AddElement(new Text(8, 8, "Virtual Jaguar 1.0.7"));
1606 window->AddElement(new Text(8, 24, "Coders:"));
1607 window->AddElement(new Text(16, 32, "Niels Wagenaar (nwagenaar)"));
1608 window->AddElement(new Text(16, 40, "Carwin Jones (Caz)"));
1609 window->AddElement(new Text(16, 48, "James L. Hammons (shamus)"));
1610 window->AddElement(new Text(16, 56, "Adam Green"));
1615 Window * MiscOptions(void)
1617 Window * window = new Window(8, 16, 304, 160);
1618 window->AddElement(new PushButton(8, 8, &vjs.useJaguarBIOS, "BIOS"));
1619 window->AddElement(new SlideSwitch(8, 20, &vjs.hardwareTypeNTSC, "PAL", "NTSC"));
1620 window->AddElement(new PushButton(8, 40, &vjs.DSPEnabled, "DSP"));
1621 window->AddElement(new SlideSwitch(16, 52, &vjs.usePipelinedDSP, "Original", "Pipelined"));
1622 window->AddElement(new SlideSwitch(8, 72, (bool *)&vjs.glFilter, "Sharp", "Blurry"));
1632 // * Window/fullscreen
1640 // Uses zero as transparent color
1642 void DrawTransparentBitmap(int16 * screen, uint32 x, uint32 y, uint16 * bitmap)
1644 uint16 width = bitmap[0], height = bitmap[1];
1647 uint32 pitch = GetSDLScreenPitch() / 2; // Returns pitch in bytes but we need words...
1648 uint32 address = x + (y * pitch);
1650 for(int yy=0; yy<height; yy++)
1652 for(int xx=0; xx<width; xx++)
1654 if (*bitmap && x + xx < pitch) // NOTE: Still doesn't clip the Y val...
1655 *(screen + address + xx + (yy * pitch)) = *bitmap;
1662 // Very very crude GUI file selector
1664 /*bool UserSelectFile(char * path, char * filename)
1669 extern int16 * backbuffer;
1670 vector<string> fileList;
1672 // Read in the candidate files from the directory pointed to by "path"
1674 DIR * dp = opendir(path);
1677 while ((de = readdir(dp)) != NULL)
1679 char * ext = strrchr(de->d_name, '.');
1682 if (stricmp(ext, ".zip") == 0 || stricmp(ext, ".jag") == 0)
1683 fileList.push_back(string(de->d_name));
1688 if (fileList.size() == 0) // Any files found?
1689 return false; // Nope. Bail!
1691 // Main GUI selection loop
1693 uint32 cursor = 0, startFile = 0;
1695 if (fileList.size() > 1) // Only go GUI if more than one possibility!
1697 sort(fileList.begin(), fileList.end());
1700 uint32 limit = (fileList.size() > 30 ? 30 : fileList.size());
1703 // Ensure that the GUI is drawn before any user input...
1704 event.type = SDL_USEREVENT;
1705 SDL_PushEvent(&event);
1709 while (SDL_PollEvent(&event))
1711 if (event.type == SDL_KEYDOWN)
1713 SDLKey key = event.key.keysym.sym;
1715 if (key == SDLK_DOWN)
1717 if (cursor != limit - 1) // Cursor is within its window
1719 else // Otherwise, scroll the window...
1721 if (cursor + startFile != fileList.size() - 1)
1735 if (key == SDLK_PAGEDOWN)
1737 if (cursor != limit - 1)
1742 if (startFile > fileList.size() - limit)
1743 startFile = fileList.size() - limit;
1746 if (key == SDLK_PAGEUP)
1752 if (startFile < limit)
1758 if (key == SDLK_RETURN)
1760 if (key == SDLK_ESCAPE)
1762 WriteLog("GUI: Aborting VJ by user request.\n");
1763 return false; // Bail out!
1765 if (key >= SDLK_a && key <= SDLK_z)
1767 // Advance cursor to filename with first letter pressed...
1768 uint8 which = (key - SDLK_a) + 65; // Convert key to A-Z char
1770 for(uint32 i=0; i<fileList.size(); i++)
1772 if ((fileList[i][0] & 0xDF) == which)
1774 cursor = i - startFile;
1775 if (i > startFile + limit - 1)
1776 startFile = i - limit + 1,
1786 else if (event.type == SDL_MOUSEMOTION)
1788 mouseX = event.motion.x, mouseY = event.motion.y;
1790 mouseX /= 2, mouseY /= 2;
1792 else if (event.type == SDL_MOUSEBUTTONDOWN)
1794 uint32 mx = event.button.x, my = event.button.y;
1801 // memset(backbuffer, 0x11, tom_getVideoModeWidth() * tom_getVideoModeHeight() * 2);
1802 memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
1804 for(uint32 i=0; i<limit; i++)
1806 // Clip our strings to guarantee that they fit on the screen...
1807 // (and strip off the extension too)
1808 string s(fileList[startFile + i], 0, fileList[startFile + i].length() - 4);
1809 if (s.length() > 38)
1811 DrawString(backbuffer, 0, i*8, (cursor == i ? true : false), " %s ", s.c_str());
1814 DrawTransparentBitmap(backbuffer, mouseX, mouseY, mousePic);
1821 strcpy(filename, path);
1823 if (strlen(path) > 0)
1824 if (path[strlen(path) - 1] != '/')
1825 strcat(filename, "/");
1827 strcat(filename, fileList[startFile + cursor].c_str());
1833 // Generic ROM loading
1835 uint32 JaguarLoadROM(uint8 * rom, char * path)
1839 char * ext = strrchr(path, '.');
1842 WriteLog("VJ: Loading \"%s\"...", path);
1844 if (stricmp(ext, ".zip") == 0)
1846 // Handle ZIP file loading here...
1847 WriteLog("(ZIPped)...");
1849 if (load_zipped_file(0, 0, path, NULL, &rom, &romSize) == -1)
1851 WriteLog("Failed!\n");
1857 /* FILE * fp = fopen(path, "rb");
1861 WriteLog("Failed!\n");
1865 fseek(fp, 0, SEEK_END);
1866 romSize = ftell(fp);
1867 fseek(fp, 0, SEEK_SET);
1868 fread(rom, 1, romSize, fp);
1871 gzFile fp = gzopen(path, "rb");
1875 WriteLog("Failed!\n");
1879 romSize = gzfilelength(fp);
1880 gzseek(fp, 0, SEEK_SET);
1881 gzread(fp, rom, romSize);
1885 WriteLog("OK (%i bytes)\n", romSize);
1892 // Jaguar cartridge ROM loading
1894 void JaguarLoadCart(uint8 * mem, char * path)
1896 jaguarRomSize = JaguarLoadROM(mem, path);
1898 // if (romSize == 0)
1900 /* char newPath[2048];
1901 WriteLog("VJ: Trying GUI...\n");
1903 //This is not *nix friendly for some reason...
1904 // if (!UserSelectFile(path, newPath))
1905 if (!UserSelectFile((strlen(path) == 0 ? (char *)"." : path), newPath))
1907 WriteLog("VJ: Could not find valid ROM in directory \"%s\"...\nAborting!\n", path);
1912 romSize = JaguarLoadROM(mem, newPath);
1914 if (jaguarRomSize == 0)
1916 // WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
1917 WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", path);
1923 jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, jaguarRomSize);
1924 WriteLog("CRC: %08X\n", (unsigned int)jaguar_mainRom_crc32);
1927 jaguarRunAddress = 0x802000;
1928 //NOTE: The bytes 'JAGR' should also be at position $1C...
1929 // Also, there's *always* a $601A header at position $00...
1930 if (jaguar_mainRom[0] == 0x60 && jaguar_mainRom[1] == 0x1A)
1932 uint32 loadAddress = GET32(jaguar_mainRom, 0x22), runAddress = GET32(jaguar_mainRom, 0x2A);
1933 //This is not always right! Especially when converted via bin2jag1!!!
1934 //We should have access to the length of the furshlumiger file that was loaded anyway!
1936 // uint32 progLength = GET32(jaguar_mainRom, 0x02);
1939 // WriteLog("Jaguar: Setting up PD ROM... Run address: %08X, length: %08X\n", runAddress, progLength);
1940 // memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, progLength);
1941 WriteLog("Jaguar: Setting up homebrew... Run address: %08X, length: %08X\n", runAddress, jaguarRomSize - 0x2E);
1942 memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, jaguarRomSize - 0x2E);
1943 // SET32(jaguar_mainRam, 4, runAddress);
1944 jaguarRunAddress = runAddress;
1949 ABS Format sleuthing (LBUGDEMO.ABS):
1951 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
1952 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
1955 DRI-format file detected...
1956 Text segment size = 0x0000050c bytes
1957 Data segment size = 0x000462c0 bytes
1958 BSS Segment size = 0x00000428 bytes
1959 Symbol Table size = 0x000012a6 bytes
1960 Absolute Address for text segment = 0x00802000
1961 Absolute Address for data segment = 0x0080250c
1962 Absolute Address for BSS segment = 0x00004000
1965 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
1966 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
1967 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
1969 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
1970 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
1971 000050 00 00 00 00 00 00 00 20
1972 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
1973 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
1974 000078 00 00 00 00 00 00 00 40
1975 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
1976 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
1977 0000a0 00 00 00 00 00 00 00 80
1979 Header size is $A8 bytes...
1981 BSD/COFF format file detected...
1982 3 sections specified
1983 Symbol Table offset = 230160 ($00038310)
1984 Symbol Table contains 1339 symbol entries ($0000053B)
1985 The additional header size is 28 bytes ($001C)
1986 Magic Number for RUN_HDR = 0x00000107
1987 Text Segment Size = 7632 ($00001DD0)
1988 Data Segment Size = 222360 ($00036498)
1989 BSS Segment Size = 428928 ($00068B80)
1990 Starting Address for executable = 0x00802000
1991 Start of Text Segment = 0x00802000
1992 Start of Data Segment = 0x00803dd0
1997 // Get the length of a (possibly) gzipped file
1999 int gzfilelength(gzFile gd)
2001 int size = 0, length = 0;
2002 unsigned char buffer[0x10000];
2008 // Read in chunks until EOF
2009 size = gzread(gd, buffer, 0x10000);