4 // Graphical User Interface support
11 #include <sys/types.h> // For MacOS <dirent.h> dependency
17 #include <ctype.h> // For toupper()
23 #include "font14pt.h" // Also 15, 16, 17, 18
24 #include "guielements.h"
28 #include "sdlemu_opengl.h"
33 using namespace std; // For STL stuff
35 // Private function prototypes
37 class Window; // Forward declaration...
39 //void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap, uint8 * alpha = NULL);
40 void DrawTransparentBitmapDeprecated(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap);
41 void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap);
42 void DrawBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap);
43 //Should call this FillScreenRectangle with a number representing the RGBA value to fill. !!! FIX !!!
44 //void ClearScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h);
45 void FillScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h, uint32 color);
46 void DrawStringTrans(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 opacity, const char * text, ...);
47 void DrawStringOpaque(uint32 * screen, uint32 x, uint32 y, uint32 color1, uint32 color2, const char * text, ...);
48 void DrawString(uint32 * screen, uint32 x, uint32 y, bool invert, const char * text, ...);
49 void DrawString2(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 transparency, const char * text, ...);
50 Window * LoadROM(void);
51 Window * ResetJaguar(void);
52 Window * ResetJaguarCD(void);
53 Window * RunEmu(void);
56 Window * MiscOptions(void);
58 int gzfilelength(gzFile gd);
62 extern uint8 * jaguar_mainRam;
63 extern uint8 * jaguar_mainRom;
64 extern uint8 * jaguar_bootRom;
65 extern uint8 * jaguar_CDBootROM;
66 extern bool BIOSLoaded;
67 extern bool CDBIOSLoaded;
69 // Local global variables
71 bool exitGUI = false; // GUI (emulator) done variable
72 int mouseX = 0, mouseY = 0;
73 uint32 background[1280 * 256]; // GUI background buffer
75 const char separator[] = "--------------------------------------------------------";
78 // Case insensitive string compare function
79 // Taken straight out of Thinking In C++ by Bruce Eckel. Thanks Bruce!
82 int stringCmpi(const string &s1, const string &s2)
84 // Select the first element of each string:
85 string::const_iterator p1 = s1.begin(), p2 = s2.begin();
87 while (p1 != s1.end() && p2 != s2.end()) // Don�t run past the end
89 if (toupper(*p1) != toupper(*p2)) // Compare upper-cased chars
90 return (toupper(*p1) < toupper(*p2) ? -1 : 1);// Report which was lexically greater
96 // If they match up to the detected eos, say which was longer. Return 0 if the same.
97 return s2.size() - s1.size();
104 enum { WINDOW_CLOSE, MENU_ITEM_CHOSEN };
109 Element(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0)
110 { extents.x = x, extents.y = y, extents.w = w, extents.h = h; }
111 virtual void HandleKey(SDLKey key) = 0; // These are "pure" virtual functions...
112 virtual void HandleMouseMove(uint32 x, uint32 y) = 0;
113 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) = 0;
114 virtual void Draw(uint32, uint32) = 0;
115 virtual void Notify(Element *) = 0;
116 //Needed? virtual ~Element() = 0;
117 //We're not allocating anything in the base class, so the answer would be NO.
118 bool Inside(uint32 x, uint32 y);
120 // static void SetScreenAndPitch(int16 * s, uint32 p) { screenBuffer = s, pitch = p; }
121 static void SetScreenAndPitch(uint32 * s, uint32 p) { screenBuffer = s, pitch = p; }
126 // Class variables...
127 // static int16 * screenBuffer;
128 static uint32 * screenBuffer;
132 // Initialize class variables (Element)
133 //int16 * Element::screenBuffer = NULL;
134 uint32 * Element::screenBuffer = NULL;
135 uint32 Element::pitch = 0;
137 bool Element::Inside(uint32 x, uint32 y)
139 return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
140 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false);
148 class Button: public Element
151 Button(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
152 activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
153 bgColor(0xFF00FF00), pic(NULL), elementToTell(NULL) {}
154 Button(uint32 x, uint32 y, uint32 w, uint32 h, uint32 * p): Element(x, y, w, h),
155 activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
156 bgColor(0xFF00FF00), pic(p), elementToTell(NULL) {}
157 // Button(uint32 x, uint32 y, uint32 * p): Element(x, y, 0, 0),
158 Button(uint32 x, uint32 y, uint32 * p, uint32 * pH = NULL, uint32 * pD = NULL): Element(x, y, 0, 0),
159 activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
160 bgColor(0xFF00FF00), pic(p), picHover(pH), picDown(pD), elementToTell(NULL)
161 { if (pic) extents.w = pic[0], extents.h = pic[1]; }
162 Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
163 activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
164 bgColor(0xFF00FF00), pic(NULL), text(s), elementToTell(NULL) {}
165 Button(uint32 x, uint32 y, string s): Element(x, y, 0, FONT_HEIGHT),
166 activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
167 bgColor(0xFF00FF00), pic(NULL), text(s), elementToTell(NULL)
168 { extents.w = s.length() * FONT_WIDTH; }
169 virtual void HandleKey(SDLKey key) {}
170 virtual void HandleMouseMove(uint32 x, uint32 y);
171 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
172 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
173 virtual void Notify(Element *) {}
174 bool ButtonClicked(void) { return activated; }
175 void SetNotificationElement(Element * e) { elementToTell = e; }
178 bool activated, clicked, inside;
179 uint32 fgColor, bgColor;
180 uint32 * pic, * picHover, * picDown;
182 Element * elementToTell;
185 void Button::HandleMouseMove(uint32 x, uint32 y)
187 inside = Inside(x, y);
190 void Button::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
197 if (clicked && !mouseDown)
199 clicked = false, activated = true;
201 // Send a message that we're activated (if there's someone to tell, that is)
203 elementToTell->Notify(this);
207 clicked = activated = false;
210 void Button::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
212 uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
214 if (text.length() > 0) // Simple text button
217 for(uint32 y=0; y<extents.h; y++)
219 for(uint32 x=0; x<extents.w; x++)
221 // Doesn't clip in y axis! !!! FIX !!!
222 if (extents.x + x < pitch)
223 screenBuffer[addr + x + (y * pitch)]
224 // = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
225 //43F0 -> 010000 11111 10000 -> 0100 0001 1111 1111 1000 0100 -> 41 FF 84
226 = (clicked && inside ? fgColor : (inside ? 0xFF84FF41 : bgColor));
230 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
232 else // Graphical button
234 uint32 * picToShow = pic;
236 if (picHover != NULL && inside && !clicked)
237 picToShow = picHover;
239 if (picDown != NULL && inside && clicked)
242 DrawTransparentBitmapDeprecated(screenBuffer, extents.x + offsetX, extents.y + offsetY, picToShow);
251 class PushButton: public Element
254 // Save state externally?
255 //We pass in a state variable if we want to track it externally, otherwise we use our own
256 //internal state var. Still need to do some kind of callback for pushbuttons that do things
257 //like change from fullscreen to windowed... !!! FIX !!!
260 // PushButton(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
261 // activated(false), clicked(false), inside(false), fgColor(0xFFFF),
262 // bgColor(0x03E0), pic(NULL), elementToTell(NULL) {}
263 // PushButton(uint32 x, uint32 y, bool * st, string s): Element(x, y, 8, 8), state(st),
264 // inside(false), text(s) { if (st == NULL) state = &internalState; }
265 PushButton(uint32 x, uint32 y, bool * st, string s): Element(x, y, 16, 16), state(st),
266 inside(false), text(s) { if (st == NULL) state = &internalState; }
267 /* Button(uint32 x, uint32 y, uint32 w, uint32 h, uint32 * p): Element(x, y, w, h),
268 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
269 bgColor(0x03E0), pic(p), elementToTell(NULL) {}
270 Button(uint32 x, uint32 y, uint32 * p): Element(x, y, 0, 0),
271 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
272 bgColor(0x03E0), pic(p), elementToTell(NULL)
273 { if (pic) extents.w = pic[0], extents.h = pic[1]; }
274 Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
275 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
276 bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL) {}
277 PushButton(uint32 x, uint32 y, string s): Element(x, y, 0, 8),
278 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
279 bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL)
280 { extents.w = s.length() * 8; }*/
281 virtual void HandleKey(SDLKey key) {}
282 virtual void HandleMouseMove(uint32 x, uint32 y);
283 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
284 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
285 virtual void Notify(Element *) {}
286 // bool ButtonClicked(void) { return activated; }
287 // void SetNotificationElement(Element * e) { elementToTell = e; }
292 // bool activated, clicked, inside;
293 // uint16 fgColor, bgColor;
296 // Element * elementToTell;
300 void PushButton::HandleMouseMove(uint32 x, uint32 y)
302 inside = Inside(x, y);
305 void PushButton::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
307 if (inside && mouseDown)
312 if (clicked && !mouseDown)
314 clicked = false, activated = true;
316 // Send a message that we're activated (if there's someone to tell, that is)
318 elementToTell->Notify(this);
323 // clicked = activated = false;
326 void PushButton::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
328 /* uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
330 for(uint32 y=0; y<extents.h; y++)
332 for(uint32 x=0; x<extents.w; x++)
334 // Doesn't clip in y axis! !!! FIX !!!
335 if (extents.x + x < pitch)
336 screenBuffer[addr + x + (y * pitch)]
337 = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
342 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, &pbDown);
344 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, &pbUp);
346 if (text.length() > 0)
347 DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY, false, "%s", text.c_str());
355 class SlideSwitch: public Element
358 // Save state externally?
359 //Seems to be handled the same as PushButton, but without sanity checks. !!! FIX !!!
362 SlideSwitch(uint32 x, uint32 y, bool * st, string s1, string s2): Element(x, y, 16, 32), state(st),
363 inside(false), text1(s1), text2(s2) {}
364 virtual void HandleKey(SDLKey key) {}
365 virtual void HandleMouseMove(uint32 x, uint32 y);
366 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
367 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
368 virtual void Notify(Element *) {}
369 // bool ButtonClicked(void) { return activated; }
370 // void SetNotificationElement(Element * e) { elementToTell = e; }
375 // bool activated, clicked, inside;
376 // uint16 fgColor, bgColor;
379 // Element * elementToTell;
382 void SlideSwitch::HandleMouseMove(uint32 x, uint32 y)
384 inside = Inside(x, y);
387 void SlideSwitch::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
389 if (inside && mouseDown)
394 if (clicked && !mouseDown)
396 clicked = false, activated = true;
398 // Send a message that we're activated (if there's someone to tell, that is)
400 elementToTell->Notify(this);
405 // clicked = activated = false;
408 void SlideSwitch::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
410 DrawTransparentBitmapDeprecated(screenBuffer, extents.x + offsetX, extents.y + offsetY, (*state ? slideSwitchDown : slideSwitchUp));
412 if (text1.length() > 0)
413 DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY, false, "%s", text1.c_str());
415 if (text2.length() > 0)
416 DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY + 16, false, "%s", text2.c_str());
424 class Window: public Element
427 /* Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
428 fgColor(0x4FF0), bgColor(0xFE10)
429 { close = new Button(w - 8, 1, closeBox); list.push_back(close); }*/
430 Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0,
431 void (* f)(Element *) = NULL): Element(x, y, w, h),
432 // /*clicked(false), inside(false),*/ fgColor(0x4FF0), bgColor(0x1E10),
433 //4FF0 -> 010011 11111 10000 -> 0100 1101 1111 1111 1000 0100 -> 4D FF 84
434 //1E10 -> 000111 10000 10000 -> 0001 1111 1000 0100 1000 0100 -> 1F 84 84
435 /*clicked(false), inside(false),*/ fgColor(0xFF84FF4D), bgColor(0xFF84841F),
437 { close = new Button(w - (CLOSEBOX_WIDTH + 1), 1, closeBox, closeBoxHover, closeBoxDown);
438 list.push_back(close);
439 close->SetNotificationElement(this); }
441 virtual void HandleKey(SDLKey key);
442 virtual void HandleMouseMove(uint32 x, uint32 y);
443 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
444 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
445 virtual void Notify(Element * e);
446 void AddElement(Element * e);
447 // bool WindowActive(void) { return true; }//return !close->ButtonClicked(); }
450 // bool clicked, inside;
451 uint32 fgColor, bgColor;
452 void (* handler)(Element *);
454 //We have to use a list of Element *pointers* because we can't make a list that will hold
455 //all the different object types in the same list...
456 vector<Element *> list;
461 for(uint32 i=0; i<list.size(); i++)
466 void Window::HandleKey(SDLKey key)
468 if (key == SDLK_ESCAPE)
471 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
472 SDL_PushEvent(&event);
475 // Handle the items this window contains...
476 for(uint32 i=0; i<list.size(); i++)
477 // Make coords relative to upper right corner of this window...
478 list[i]->HandleKey(key);
481 void Window::HandleMouseMove(uint32 x, uint32 y)
483 // Handle the items this window contains...
484 for(uint32 i=0; i<list.size(); i++)
485 // Make coords relative to upper right corner of this window...
486 list[i]->HandleMouseMove(x - extents.x, y - extents.y);
489 void Window::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
491 // Handle the items this window contains...
492 for(uint32 i=0; i<list.size(); i++)
493 // Make coords relative to upper right corner of this window...
494 list[i]->HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
497 void Window::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
499 uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
501 for(uint32 y=0; y<extents.h; y++)
503 for(uint32 x=0; x<extents.w; x++)
505 // Doesn't clip in y axis! !!! FIX !!!
506 if (extents.x + x < pitch)
507 screenBuffer[addr + x + (y * pitch)] = bgColor;
511 // Handle the items this window contains...
512 for(uint32 i=0; i<list.size(); i++)
513 list[i]->Draw(extents.x, extents.y);
516 void Window::AddElement(Element * e)
521 void Window::Notify(Element * e)
526 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
527 SDL_PushEvent(&event);
536 class Text: public Element
539 // Text(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
540 // fgColor(0x4FF0), bgColor(0xFE10) {}
541 // Text(uint32 x, uint32 y, string s, uint16 fg = 0x4FF0, uint16 bg = 0xFE10): Element(x, y, 0, 0),
542 // fgColor(fg), bgColor(bg), text(s) {}
543 //4FF0 -> 010011 11111 10000 -> 0100 1101 1111 1111 1000 0100 -> 4D FF 84
544 //FE10 -> 111111 10000 10000 -> 1111 1111 1000 0100 1000 0100 -> FF 84 84
545 Text(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
546 fgColor(0xFF8484FF), bgColor(0xFF84FF4D) {}
547 Text(uint32 x, uint32 y, string s, uint32 fg = 0xFF8484FF, uint32 bg = 0xFF84FF4D):
548 Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s) {}
549 virtual void HandleKey(SDLKey key) {}
550 virtual void HandleMouseMove(uint32 x, uint32 y) {}
551 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
552 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
553 virtual void Notify(Element *) {}
556 uint32 fgColor, bgColor;
560 void Text::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
562 if (text.length() > 0)
563 // DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
564 DrawStringOpaque(screenBuffer, extents.x + offsetX, extents.y + offsetY, fgColor, bgColor, "%s", text.c_str());
569 // Static image class
572 class Image: public Element
575 Image(uint32 x, uint32 y, const void * img): Element(x, y, 0, 0), image(img) {}
576 virtual void HandleKey(SDLKey key) {}
577 virtual void HandleMouseMove(uint32 x, uint32 y) {}
578 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
579 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
580 virtual void Notify(Element *) {}
583 uint32 fgColor, bgColor;
587 void Image::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
590 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, image);
598 class TextEdit: public Element
601 TextEdit(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
602 fgColor(0xFF8484FF), bgColor(0xFF84FF4D), text(""), caretPos(0),
604 TextEdit(uint32 x, uint32 y, string s, uint32 mss = 10, uint32 fg = 0xFF8484FF,
605 uint32 bg = 0xFF84FF4D): Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s),
606 caretPos(0), maxScreenSize(mss) {}
607 virtual void HandleKey(SDLKey key);
608 virtual void HandleMouseMove(uint32 x, uint32 y) {}
609 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
610 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
611 virtual void Notify(Element *) {}
614 uint32 fgColor, bgColor;
617 uint32 maxScreenSize;
620 //Set different filters depending on type passed in on construction, e.g., filename, amount, etc...?
621 void TextEdit::HandleKey(SDLKey key)
623 if ((key >= SDLK_a && key <= SDLK_z) || (key >= SDLK_0 && key <= SDLK_9) || key == SDLK_PERIOD
624 || key == SDLK_SLASH)
626 //Need to handle shift key as well...
627 text[caretPos++] = key;
630 else if (key == SDLK_BACKSPACE)
634 else if (key == SDLK_DELETE)
640 void TextEdit::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
642 if (text.length() > 0)
644 FillScreenRectangle(screenBuffer, extents.x + offsetX, extents.y + offsetY, FONT_WIDTH * maxScreenSize, FONT_HEIGHT, bgColor);
645 // DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
646 DrawStringOpaque(screenBuffer, extents.x + offsetX, extents.y + offsetY, fgColor, bgColor, "%s", text.c_str());
649 // Draw the caret (underscore? or vertical line?)
657 class ListBox: public Element
658 //class ListBox: public Window
661 // ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
662 ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);//: Window(x, y, w, h),
663 // windowPtr(0), cursor(0), limit(0), charWidth((w / 8) - 1), charHeight(h / 8),
664 // elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
665 // downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox) {}
666 virtual void HandleKey(SDLKey key);
667 virtual void HandleMouseMove(uint32 x, uint32 y);
668 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
669 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
670 virtual void Notify(Element * e);
671 void SetNotificationElement(Element * e) { elementToTell = e; }
672 void AddItem(string s);
673 string GetSelectedItem(void);
677 uint32 windowPtr, cursor, limit;
678 uint32 charWidth, charHeight; // Box width/height in characters
679 Element * elementToTell;
680 Button upArrow, downArrow, upArrow2;
684 uint32 yRelativePoint;
687 ListBox::ListBox(uint32 x, uint32 y, uint32 w, uint32 h): Element(x, y, w, h),
688 thumbClicked(false), windowPtr(0), cursor(0), limit(0), charWidth((w / FONT_WIDTH) - 1),
689 charHeight(h / FONT_HEIGHT), elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
690 downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox)
692 upArrow.SetNotificationElement(this);
693 downArrow.SetNotificationElement(this);
694 upArrow2.SetNotificationElement(this);
695 extents.w -= 8; // Make room for scrollbar...
698 void ListBox::HandleKey(SDLKey key)
700 if (key == SDLK_DOWN)
702 if (cursor != limit - 1) // Cursor is within its window
704 else // Otherwise, scroll the window...
706 if (cursor + windowPtr != item.size() - 1)
710 else if (key == SDLK_UP)
720 else if (key == SDLK_PAGEDOWN)
722 if (cursor != limit - 1)
727 if (windowPtr > item.size() - limit)
728 windowPtr = item.size() - limit;
731 else if (key == SDLK_PAGEUP)
737 if (windowPtr < limit)
743 else if (key >= SDLK_a && key <= SDLK_z)
745 // Advance cursor to filename with first letter pressed...
746 uint8 which = (key - SDLK_a) + 65; // Convert key to A-Z char
748 for(uint32 i=0; i<item.size(); i++)
750 if ((item[i][0] & 0xDF) == which)
752 cursor = i - windowPtr;
753 if (i > windowPtr + limit - 1)
754 windowPtr = i - limit + 1, cursor = limit - 1;
756 windowPtr = i, cursor = 0;
763 void ListBox::HandleMouseMove(uint32 x, uint32 y)
765 upArrow.HandleMouseMove(x - extents.x, y - extents.y);
766 downArrow.HandleMouseMove(x - extents.x, y - extents.y);
767 upArrow2.HandleMouseMove(x - extents.x, y - extents.y);
771 uint32 sbHeight = extents.h - 24,
772 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight);
774 //yRelativePoint is the spot on the thumb where we clicked...
775 int32 newThumbStart = y - yRelativePoint;
777 if (newThumbStart < 0)
780 if ((uint32)newThumbStart > sbHeight - thumb)
781 newThumbStart = sbHeight - thumb;
783 windowPtr = (uint32)(((float)newThumbStart / (float)sbHeight) * (float)item.size());
784 //Check for cursor bounds as well... Or do we need to???
785 //Actually, we don't...!
789 void ListBox::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
791 if (Inside(x, y) && mouseDown)
793 // Why do we have to do this??? (- extents.y?)
794 // I guess it's because only the Window class has offsetting implemented... !!! FIX !!!
795 // cursor = (y - extents.y) / 8;
796 cursor = (y - extents.y) / FONT_HEIGHT;
799 // Check for a hit on the scrollbar...
800 if (x > (uint32)(extents.x + extents.w) && x <= (uint32)(extents.x + extents.w + 8)
801 && y > (uint32)(extents.y + 8) && y <= (uint32)(extents.y + extents.h - 16))
805 // This shiaut should be calculated in AddItem(), not here... (or in Draw() for that matter)
806 uint32 sbHeight = extents.h - 24,
807 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
808 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
810 // Did we hit the thumb?
811 if (y >= (extents.y + 8 + thumbStart) && y < (extents.y + 8 + thumbStart + thumb))
812 thumbClicked = true, yRelativePoint = y - thumbStart;
814 //Seems that this is useless--never reached except in rare cases and that the code outside is
817 // thumbClicked = false;
821 thumbClicked = false;
823 upArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
824 downArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
825 upArrow2.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
828 void ListBox::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
830 for(uint32 i=0; i<limit; i++)
832 // Strip off the extension
833 // (extension stripping should be an option, not default!)
834 string s(item[windowPtr + i], 0, item[windowPtr + i].length() - 4);
835 // DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*8,
836 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*FONT_HEIGHT,
837 (cursor == i ? true : false), "%-*.*s", charWidth, charWidth, s.c_str());
840 upArrow.Draw(extents.x + offsetX, extents.y + offsetY);
841 downArrow.Draw(extents.x + offsetX, extents.y + offsetY);
842 upArrow2.Draw(extents.x + offsetX, extents.y + offsetY);
844 uint32 sbHeight = extents.h - 24,
845 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
846 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
848 for(uint32 y=extents.y+offsetY+8; y<extents.y+offsetY+extents.h-16; y++)
850 // for(uint32 x=extents.x+offsetX+extents.w-8; x<extents.x+offsetX+extents.w; x++)
851 for(uint32 x=extents.x+offsetX+extents.w; x<extents.x+offsetX+extents.w+8; x++)
853 if (y >= thumbStart + (extents.y+offsetY+8) && y < thumbStart + thumb + (extents.y+offsetY+8))
854 // screenBuffer[x + (y * pitch)] = (thumbClicked ? 0x458E : 0xFFFF);
855 //458E -> 01 0001 0 1100 0 1110 -> 0100 0101 0110 0011 0111 0011 -> 45 63 73
856 screenBuffer[x + (y * pitch)] = (thumbClicked ? 0xFF736345 : 0xFFFFFFFF);
858 // screenBuffer[x + (y * pitch)] = 0x0200;
859 //0200 -> 000000 10000 00000 -> 00 1000 0100 00
860 screenBuffer[x + (y * pitch)] = 0xFF008400;
865 void ListBox::Notify(Element * e)
867 if (e == &upArrow || e == &upArrow2)
873 if (cursor < limit - 1)
877 else if (e == &downArrow)
879 if (windowPtr < item.size() - limit)
889 void ListBox::AddItem(string s)
891 // Do a simple insertion sort
892 bool inserted = false;
894 for(vector<string>::iterator i=item.begin(); i<item.end(); i++)
896 if (stringCmpi(s, *i) == -1)
907 limit = (item.size() > charHeight ? charHeight : item.size());
910 string ListBox::GetSelectedItem(void)
912 return item[windowPtr + cursor];
920 class FileList: public Window
923 FileList(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);
924 virtual ~FileList() {}
925 virtual void HandleKey(SDLKey key);
926 virtual void HandleMouseMove(uint32 x, uint32 y) { Window::HandleMouseMove(x, y); }
927 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) { Window::HandleMouseButton(x, y, mouseDown); }
928 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) { Window::Draw(offsetX, offsetY); }
929 virtual void Notify(Element * e);
936 //Need 4 buttons, one scrollbar...
937 FileList::FileList(uint32 x, uint32 y, uint32 w, uint32 h): Window(x, y, w, h)
939 files = new ListBox(8, 8, w - 16, h - 32);
941 load = new Button(8, h - 16, " Load ");
943 load->SetNotificationElement(this);
945 //!!! FIX !!! Directory might not exist--this shouldn't cause VJ to crash!
946 DIR * dp = opendir(vjs.ROMPath);
951 while ((de = readdir(dp)) != NULL)
953 char * ext = strrchr(de->d_name, '.');
956 if (strcasecmp(ext, ".zip") == 0 || strcasecmp(ext, ".j64") == 0
957 || strcasecmp(ext, ".abs") == 0 || strcasecmp(ext, ".jag") == 0
958 || strcasecmp(ext, ".rom") == 0)
959 files->AddItem(string(de->d_name));
966 //Give a diagnostic message here so that the (l)user can figure out what went wrong. !!! FIX !!!
970 void FileList::HandleKey(SDLKey key)
972 if (key == SDLK_RETURN)
975 Window::HandleKey(key);
978 void FileList::Notify(Element * e)
982 char filename[MAX_PATH];
983 strcpy(filename, vjs.ROMPath);
985 if (strlen(filename) > 0)
986 if (filename[strlen(filename) - 1] != '/')
987 strcat(filename, "/");
989 strcat(filename, files->GetSelectedItem().c_str());
991 // uint32 romSize = JaguarLoadROM(jaguar_mainRom, filename);
992 // JaguarLoadCart(jaguar_mainRom, filename);
993 if (JaguarLoadFile(filename))
996 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
997 SDL_PushEvent(&event);
999 event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
1000 event.user.data1 = (void *)ResetJaguar;
1001 SDL_PushEvent(&event);
1006 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
1007 SDL_PushEvent(&event);
1009 // Handle the error, but don't run...
1010 // Tell the user that we couldn't run their file for some reason... !!! FIX !!!
1011 //how to kludge: Make a function like ResetJaguar which creates the dialog window
1020 // Menu class & supporting structs/classes
1026 Window * (* action)(void);
1029 NameAction(string n, Window * (* a)(void) = NULL, SDLKey k = SDLK_UNKNOWN): name(n),
1030 action(a), hotKey(k) {}
1036 MenuItems(): charLength(0) {}
1037 bool Inside(uint32 x, uint32 y)
1038 { return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
1039 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false); }
1042 vector<NameAction> item;
1047 class Menu: public Element
1050 // 1CFF -> 0 001 11 00 111 1 1111
1051 // 421F -> 0 100 00 10 000 1 1111
1052 Menu(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = FONT_HEIGHT,
1053 /* uint16 fgc = 0x1CFF, uint16 bgc = 0x000F, uint16 fgch = 0x421F,
1054 uint16 bgch = 0x1CFF): Element(x, y, w, h), activated(false), clicked(false),*/
1055 /* uint32 fgc = 0xFF3F3F00, uint32 bgc = 0x7F000000, uint32 fgch = 0xFF878700,
1056 uint32 bgch = 0xFF3F3F00): Element(x, y, w, h), activated(false), clicked(false),*/
1057 /* uint32 fgc = 0xFFFF3F3F, uint32 bgc = 0xFF7F0000, uint32 fgch = 0xFFFF8787,
1058 uint32 bgch = 0xFFFF3F3F): Element(x, y, w, h), activated(false), clicked(false),*/
1059 uint32 fgc = 0xFF7F0000, uint32 bgc = 0xFFFF3F3F, uint32 fgch = 0xFFFF3F3F,
1060 uint32 bgch = 0xFFFF8787): Element(x, y, w, h), activated(false), clicked(false),
1061 inside(0), insidePopup(0), fgColor(fgc), bgColor(bgc), fgColorHL(fgch),
1062 bgColorHL(bgch), menuChosen(-1), menuItemChosen(-1) {}
1063 virtual void HandleKey(SDLKey key);
1064 virtual void HandleMouseMove(uint32 x, uint32 y);
1065 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
1066 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
1067 virtual void Notify(Element *) {}
1068 void Add(MenuItems mi);
1071 bool activated, clicked;
1072 uint32 inside, insidePopup;
1073 // uint16 fgColor, bgColor, fgColorHL, bgColorHL;
1074 uint32 fgColor, bgColor, fgColorHL, bgColorHL;
1075 int menuChosen, menuItemChosen;
1078 vector<MenuItems> itemList;
1081 void Menu::HandleKey(SDLKey key)
1083 for(uint32 i=0; i<itemList.size(); i++)
1085 for(uint32 j=0; j<itemList[i].item.size(); j++)
1087 if (itemList[i].item[j].hotKey == key)
1090 event.type = SDL_USEREVENT;
1091 event.user.code = MENU_ITEM_CHOSEN;
1092 event.user.data1 = (void *)itemList[i].item[j].action;
1093 SDL_PushEvent(&event);
1095 clicked = false, menuChosen = menuItemChosen = -1;
1102 void Menu::HandleMouseMove(uint32 x, uint32 y)
1104 inside = insidePopup = 0;
1108 // Find out *where* we are inside the menu bar
1109 uint32 xpos = extents.x;
1111 for(uint32 i=0; i<itemList.size(); i++)
1113 uint32 width = (itemList[i].title.length() + 2) * FONT_WIDTH;
1115 if (x >= xpos && x < xpos + width)
1126 if (!Inside(x, y) && !clicked)
1131 if (itemList[menuChosen].Inside(x, y) && clicked)
1133 insidePopup = ((y - itemList[menuChosen].extents.y) / FONT_HEIGHT) + 1;
1134 menuItemChosen = insidePopup - 1;
1138 void Menu::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
1147 menuChosen = -1; // clicked is already false...!
1150 else // clicked == true
1152 if (insidePopup && !mouseDown) // I.e., mouse-button-up
1155 if (itemList[menuChosen].item[menuItemChosen].action != NULL)
1157 // itemList[menuChosen].item[menuItemChosen].action();
1159 event.type = SDL_USEREVENT;
1160 event.user.code = MENU_ITEM_CHOSEN;
1161 event.user.data1 = (void *)itemList[menuChosen].item[menuItemChosen].action;
1162 SDL_PushEvent(&event);
1164 clicked = false, menuChosen = menuItemChosen = -1;
1167 while (SDL_PollEvent(&event)); // Flush the event queue...
1168 event.type = SDL_MOUSEMOTION;
1170 SDL_GetMouseState(&mx, &my);
1171 event.motion.x = mx, event.motion.y = my;
1172 SDL_PushEvent(&event); // & update mouse position...!
1176 if (!inside && !insidePopup && mouseDown)
1177 clicked = false, menuChosen = menuItemChosen = -1;
1181 void Menu::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
1183 uint32 xpos = extents.x + offsetX;
1185 for(uint32 i=0; i<itemList.size(); i++)
1187 // uint16 color1 = fgColor, color2 = bgColor;
1188 uint32 color1 = fgColor, color2 = bgColor;
1189 if (inside == (i + 1) || (menuChosen != -1 && (uint32)menuChosen == i))
1190 color1 = fgColorHL, color2 = bgColorHL;
1192 DrawStringOpaque(screenBuffer, xpos, extents.y + offsetY, color1, color2,
1193 " %s ", itemList[i].title.c_str());
1194 xpos += (itemList[i].title.length() + 2) * FONT_WIDTH;
1197 // Draw sub menu (but only if active)
1200 uint32 ypos = extents.y + FONT_HEIGHT + 1;
1202 for(uint32 i=0; i<itemList[menuChosen].item.size(); i++)
1204 // uint16 color1 = fgColor, color2 = bgColor;
1205 uint32 color1 = fgColor, color2 = bgColor;
1207 if (insidePopup == i + 1)
1208 color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
1210 if (itemList[menuChosen].item[i].name.length() > 0)
1211 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
1212 color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
1213 itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
1215 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
1216 fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
1218 ypos += FONT_HEIGHT;
1223 void Menu::Add(MenuItems mi)
1225 for(uint32 i=0; i<mi.item.size(); i++)
1226 if (mi.item[i].name.length() > mi.charLength)
1227 mi.charLength = mi.item[i].name.length();
1229 // Set extents here as well...
1230 mi.extents.x = extents.x + extents.w, mi.extents.y = extents.y + FONT_HEIGHT + 1;
1231 mi.extents.w = (mi.charLength + 2) * FONT_WIDTH, mi.extents.h = mi.item.size() * FONT_HEIGHT;
1233 itemList.push_back(mi);
1234 extents.w += (mi.title.length() + 2) * FONT_WIDTH;
1238 //Do we even *need* this?
1239 //Doesn't seem like it...
1240 /*class RootWindow: public Window
1243 RootWindow(Menu * m, Window * w = NULL): menu(m), window(w) {}
1244 //Do we even need to care about this crap?
1245 // { extents.x = extents.y = 0, extents.w = 320, extents.h = 240; }
1246 virtual void HandleKey(SDLKey key) {}
1247 virtual void HandleMouseMove(uint32 x, uint32 y) {}
1248 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
1249 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) {}
1250 virtual void Notify(Element *) {}
1255 int16 * rootImage[1280 * 240 * 2];
1260 // Draw text at the given x/y coordinates. Can invert text as well.
1262 void DrawString(uint32 * screen, uint32 x, uint32 y, bool invert, const char * text, ...)
1267 va_start(arg, text);
1268 vsprintf(string, text, arg);
1271 uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1272 uint32 length = strlen(string), address = x + (y * pitch);
1274 uint32 color1 = 0x0080FF;
1275 uint8 nBlue = (color1 >> 16) & 0xFF, nGreen = (color1 >> 8) & 0xFF, nRed = color1 & 0xFF;
1276 uint8 xorMask = (invert ? 0xFF : 0x00);
1278 for(uint32 i=0; i<length; i++)
1280 uint8 c = string[i];
1281 uint32 fontAddr = (uint32)(c < 32 ? 0 : c - 32) * FONT_WIDTH * FONT_HEIGHT;
1283 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
1285 for(uint32 xx=0; xx<FONT_WIDTH; xx++)
1287 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1289 uint8 eBlue = (existingColor >> 16) & 0xFF,
1290 eGreen = (existingColor >> 8) & 0xFF,
1291 eRed = existingColor & 0xFF;
1293 uint8 trans = font2[fontAddr] ^ xorMask;
1294 uint8 invTrans = trans ^ 0xFF;
1296 uint32 bRed = (eRed * invTrans + nRed * trans) / 255,
1297 bGreen = (eGreen * invTrans + nGreen * trans) / 255,
1298 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1300 *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
1305 address += FONT_WIDTH;
1310 // Draw text at the given x/y coordinates, using FG/BG colors.
1312 void DrawStringOpaque(uint32 * screen, uint32 x, uint32 y, uint32 color1, uint32 color2, const char * text, ...)
1317 va_start(arg, text);
1318 vsprintf(string, text, arg);
1321 uint32 pitch = sdlemuGetOverlayWidthInPixels();
1322 uint32 length = strlen(string), address = x + (y * pitch);
1324 uint8 eBlue = (color2 >> 16) & 0xFF, eGreen = (color2 >> 8) & 0xFF, eRed = color2 & 0xFF,
1325 nBlue = (color1 >> 16) & 0xFF, nGreen = (color1 >> 8) & 0xFF, nRed = color1 & 0xFF;
1327 for(uint32 i=0; i<length; i++)
1329 uint8 c = string[i];
1330 c = (c < 32 ? 0 : c - 32);
1331 uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;
1333 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
1335 for(uint32 xx=0; xx<FONT_WIDTH; xx++)
1337 uint8 trans = font2[fontAddr++];
1338 uint8 invTrans = trans ^ 0xFF;
1340 uint32 bRed = (eRed * invTrans + nRed * trans) / 255;
1341 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
1342 uint32 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1344 *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
1348 address += FONT_WIDTH;
1353 // Draw text at the given x/y coordinates with transparency (0 is fully opaque, 32 is fully transparent).
1355 void DrawStringTrans(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 trans, const char * text, ...)
1360 va_start(arg, text);
1361 vsprintf(string, text, arg);
1364 uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1365 uint32 length = strlen(string), address = x + (y * pitch);
1367 for(uint32 i=0; i<length; i++)
1369 uint32 fontAddr = (uint32)string[i] * 64;
1371 for(uint32 yy=0; yy<8; yy++)
1373 for(uint32 xx=0; xx<8; xx++)
1375 if (font1[fontAddr])
1377 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1379 uint8 eBlue = (existingColor >> 16) & 0xFF,
1380 eGreen = (existingColor >> 8) & 0xFF,
1381 eRed = existingColor & 0xFF,
1382 //This could be done ahead of time, instead of on each pixel...
1383 nBlue = (color >> 16) & 0xFF,
1384 nGreen = (color >> 8) & 0xFF,
1385 nRed = color & 0xFF;
1387 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
1388 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
1389 //because dividing by 32 is faster than dividing by 31...!
1390 uint8 invTrans = 32 - trans;
1392 uint32 bRed = (eRed * trans + nRed * invTrans) / 32;
1393 uint32 bGreen = (eGreen * trans + nGreen * invTrans) / 32;
1394 uint32 bBlue = (eBlue * trans + nBlue * invTrans) / 32;
1396 *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
1408 // Draw text at the given x/y coordinates, using FG color and overlay alpha blending.
1410 void DrawString2(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 transparency, const char * text, ...)
1415 va_start(arg, text);
1416 vsprintf(string, text, arg);
1419 uint32 pitch = sdlemuGetOverlayWidthInPixels();
1420 uint32 length = strlen(string), address = x + (y * pitch);
1422 color &= 0x00FFFFFF; // Just in case alpha was passed in...
1424 for(uint32 i=0; i<length; i++)
1426 uint8 c = string[i];
1427 c = (c < 32 ? 0 : c - 32);
1428 uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;
1430 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
1432 for(uint32 xx=0; xx<FONT_WIDTH; xx++)
1434 uint8 fontTrans = font2[fontAddr++];
1435 uint32 newTrans = (fontTrans * transparency / 255) << 24;
1436 uint32 pixel = newTrans | color;
1438 *(screen + address + xx + (yy * pitch)) = pixel;
1442 address += FONT_WIDTH;
1448 // Uses zero as transparent color
1449 // Can also use an optional alpha channel
1450 // Alpha channel is now mandatory! ;-)
1452 //void DrawTransparentBitmap(int16 * screen, uint32 x, uint32 y, uint16 * bitmap, uint8 * alpha/*=NULL*/)
1453 /*void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap, uint8 * alpha)
1455 uint32 width = bitmap[0], height = bitmap[1];
1458 // uint32 pitch = GetSDLScreenPitch() / 2; // Returns pitch in bytes but we need words...
1459 uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1460 uint32 address = x + (y * pitch);
1462 for(uint32 yy=0; yy<height; yy++)
1464 for(uint32 xx=0; xx<width; xx++)
1468 if (*bitmap && x + xx < pitch) // NOTE: Still doesn't clip the Y val...
1469 *(screen + address + xx + (yy * pitch)) = *bitmap;
1473 uint8 trans = *alpha;
1474 uint32 color = *bitmap;
1475 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1477 uint8 eRed = existingColor & 0xFF,
1478 eGreen = (existingColor >> 8) & 0xFF,
1479 eBlue = (existingColor >> 16) & 0xFF,
1481 nRed = color & 0xFF,
1482 nGreen = (color >> 8) & 0xFF,
1483 nBlue = (color >> 16) & 0xFF;
1485 uint8 invTrans = 255 - trans;
1486 uint32 bRed = (eRed * trans + nRed * invTrans) / 255;
1487 uint32 bGreen = (eGreen * trans + nGreen * invTrans) / 255;
1488 uint32 bBlue = (eBlue * trans + nBlue * invTrans) / 255;
1490 uint32 blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
1492 *(screen + address + xx + (yy * pitch)) = blendedColor;
1501 void DrawTransparentBitmapDeprecated(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap)
1503 uint32 width = bitmap[0], height = bitmap[1];
1506 uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1507 uint32 address = x + (y * pitch);
1509 for(uint32 yy=0; yy<height; yy++)
1511 for(uint32 xx=0; xx<width; xx++)
1513 uint32 color = *bitmap;
1514 uint32 blendedColor = color;
1515 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1517 if (existingColor >> 24 != 0x00) // Pixel needs blending
1519 uint8 trans = color >> 24;
1520 uint8 invTrans = trans ^ 0xFF;//255 - trans;
1522 uint8 eRed = existingColor & 0xFF,
1523 eGreen = (existingColor >> 8) & 0xFF,
1524 eBlue = (existingColor >> 16) & 0xFF,
1526 nRed = color & 0xFF,
1527 nGreen = (color >> 8) & 0xFF,
1528 nBlue = (color >> 16) & 0xFF;
1530 uint32 bRed = (eRed * invTrans + nRed * trans) / 255;
1531 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
1532 uint32 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1534 blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
1537 *(screen + address + xx + (yy * pitch)) = blendedColor;
1543 void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap)
1545 uint32 pitch = sdlemuGetOverlayWidthInPixels();
1546 uint32 address = x + (y * pitch);
1549 for(uint32 yy=0; yy<((Bitmap *)bitmap)->height; yy++)
1551 for(uint32 xx=0; xx<((Bitmap *)bitmap)->width; xx++)
1553 uint32 color = ((uint32 *)((Bitmap *)bitmap)->pixelData)[count];
1554 uint32 blendedColor = color;
1555 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1557 if (existingColor >> 24 != 0x00) // Pixel needs blending
1559 uint8 trans = color >> 24;
1560 uint8 invTrans = trans ^ 0xFF;
1562 uint8 eRed = existingColor & 0xFF,
1563 eGreen = (existingColor >> 8) & 0xFF,
1564 eBlue = (existingColor >> 16) & 0xFF,
1566 nRed = color & 0xFF,
1567 nGreen = (color >> 8) & 0xFF,
1568 nBlue = (color >> 16) & 0xFF;
1570 uint32 bRed = (eRed * invTrans + nRed * trans) / 255;
1571 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
1572 uint32 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1574 // Instead of $FF, should use the alpha from the destination pixel as the final alpha value...
1575 blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
1578 *(screen + address + xx + (yy * pitch)) = blendedColor;
1585 // Draw a bitmap without using blending
1587 void DrawBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap)
1589 uint32 pitch = sdlemuGetOverlayWidthInPixels();
1590 uint32 address = x + (y * pitch);
1593 for(uint32 yy=0; yy<((Bitmap *)bitmap)->height; yy++)
1595 for(uint32 xx=0; xx<((Bitmap *)bitmap)->width; xx++)
1597 *(screen + address + xx + (yy * pitch)) = ((uint32 *)((Bitmap *)bitmap)->pixelData)[count];
1604 // Fill a portion of the screen with the passed in color
1606 void FillScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h, uint32 color)
1607 //void ClearScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h)
1609 uint32 pitch = sdlemuGetOverlayWidthInPixels();
1610 uint32 address = x + (y * pitch);
1612 for(uint32 yy=0; yy<h; yy++)
1613 for(uint32 xx=0; xx<w; xx++)
1614 *(screen + address + xx + (yy * pitch)) = color;
1619 // GUI stuff--it's not crunchy, it's GUI! ;-)
1624 SDL_ShowCursor(SDL_DISABLE);
1625 SDL_GetMouseState(&mouseX, &mouseY);
1635 //bool GUIMain(void)
1636 bool GUIMain(char * filename)
1638 WriteLog("GUI: Inside GUIMain...\n");
1640 uint32 pointerBGSave[6 * 8 + 2];
1641 pointerBGSave[0] = 6;
1642 pointerBGSave[1] = 8;
1644 // Need to set things up so that it loads and runs a file if given on the command line. !!! FIX !!! [DONE]
1645 extern uint32 * backbuffer;
1646 // bool done = false;
1648 Window * mainWindow = NULL;
1650 // Set up the GUI classes...
1651 // Element::SetScreenAndPitch(backbuffer, GetSDLScreenWidthInPixels());
1652 Element::SetScreenAndPitch((uint32 *)sdlemuGetOverlayPixels(), sdlemuGetOverlayWidthInPixels());
1653 sdlemuEnableOverlay();
1657 mi.title = "Jaguar";
1658 mi.item.push_back(NameAction("Load...", LoadROM, SDLK_l));
1659 mi.item.push_back(NameAction("Reset", ResetJaguar, SDLK_r));
1661 mi.item.push_back(NameAction("Reset CD", ResetJaguarCD, SDLK_c));
1662 mi.item.push_back(NameAction("Run", RunEmu, SDLK_ESCAPE));
1663 mi.item.push_back(NameAction(""));
1664 mi.item.push_back(NameAction("Quit", Quit, SDLK_q));
1666 mi.title = "Settings";
1668 mi.item.push_back(NameAction("Video..."));
1669 mi.item.push_back(NameAction("Audio..."));
1670 mi.item.push_back(NameAction("Misc...", MiscOptions, SDLK_m));
1674 mi.item.push_back(NameAction("About...", About));
1677 bool showMouse = true;
1679 // Grab the BG where the mouse will be painted (prime the backstore)
1683 Bitmap ptr = { 6, 8, 4,
1684 ""//"000011112222333344445555"
1685 //"000011112222333344445555"
1686 //"000011112222333344445555"
1687 //"000011112222333344445555"
1688 //"000011112222333344445555"
1689 //"000011112222333344445555"
1690 //"000011112222333344445555"
1691 //"000011112222333344445555"
1693 uint32 * overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
1696 for(uint32 y=0; y<pointerBGSave[1]; y++)
1697 for(uint32 x=0; x<pointerBGSave[0]; x++)
1698 pointerBGSave[count++] = overlayPixels[((mouseY + y) * sdlemuGetOverlayWidthInPixels()) + (mouseX + x)];
1700 uint32 oldMouseX = mouseX, oldMouseY = mouseY;
1702 //This is crappy!!! !!! FIX !!!
1703 //Is this even needed any more? Hmm. Maybe. Dunno.
1704 WriteLog("GUI: Resetting Jaguar...\n");
1707 WriteLog("GUI: Clearing BG save...\n");
1708 // Set up our background save...
1709 // memset(background, 0x11, tom_getVideoModeWidth() * 240 * 2);
1710 //1111 -> 000100 01000 10001 -> 0001 0000 0100 0010 1000 1100 -> 10 42 8C
1711 for(uint32 i=0; i<tom_getVideoModeWidth()*240; i++)
1712 // background[i] = 0xFF8C4210;
1713 backbuffer[i] = 0xFF8C4210;
1715 /* uint32 * overlayPix = (uint32 *)sdlemuGetOverlayPixels();
1716 for(uint32 i=0; i<sdlemuGetOverlayWidthInPixels()*480; i++)
1717 overlayPix[i] = 0x00000000;*/
1719 // Handle loading file passed in on the command line...! [DONE]
1723 if (JaguarLoadFile(filename))
1725 // event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
1726 // event.user.data1 = (void *)ResetJaguar;
1727 // SDL_PushEvent(&event);
1728 // Make it so that if passed in on the command line, we quit right
1729 // away when pressing ESC
1730 WriteLog("GUI: Bypassing GUI since ROM passed in on command line...\n");
1736 // Create error dialog...
1738 sprintf(errText, "The file %40s could not be loaded.", filename);
1740 mainWindow = new Window(8, 16, 304, 160);
1741 mainWindow->AddElement(new Text(8, 8, "Error!"));
1742 mainWindow->AddElement(new Text(8, 24, errText));
1746 WriteLog("GUI: Entering main loop...\n");
1749 if (SDL_PollEvent(&event))
1751 if (event.type == SDL_USEREVENT)
1753 if (event.user.code == WINDOW_CLOSE)
1758 else if (event.user.code == MENU_ITEM_CHOSEN)
1760 // Confused? Let me enlighten... What we're doing here is casting
1761 // data1 as a pointer to a function which returns a Window pointer and
1762 // which takes no parameters (the "(Window *(*)(void))" part), then
1763 // derefencing it (the "*" in front of that) in order to call the
1764 // function that it points to. Clear as mud? Yeah, I hate function
1765 // pointers too, but what else are you gonna do?
1766 mainWindow = (*(Window *(*)(void))event.user.data1)();
1768 while (SDL_PollEvent(&event)); // Flush the event queue...
1769 event.type = SDL_MOUSEMOTION;
1771 SDL_GetMouseState(&mx, &my);
1772 event.motion.x = mx, event.motion.y = my;
1773 SDL_PushEvent(&event); // & update mouse position...!
1775 oldMouseX = mouseX, oldMouseY = mouseY;
1776 mouseX = mx, mouseY = my; // This prevents "mouse flash"...
1779 else if (event.type == SDL_ACTIVEEVENT)
1781 if (event.active.state == SDL_APPMOUSEFOCUS)
1782 showMouse = (event.active.gain ? true : false);
1784 else if (event.type == SDL_KEYDOWN)
1787 mainWindow->HandleKey(event.key.keysym.sym);
1789 mainMenu.HandleKey(event.key.keysym.sym);
1791 else if (event.type == SDL_MOUSEMOTION)
1793 oldMouseX = mouseX, oldMouseY = mouseY;
1794 mouseX = event.motion.x, mouseY = event.motion.y;
1797 mainWindow->HandleMouseMove(mouseX, mouseY);
1799 mainMenu.HandleMouseMove(mouseX, mouseY);
1801 else if (event.type == SDL_MOUSEBUTTONDOWN)
1803 uint32 mx = event.button.x, my = event.button.y;
1806 mainWindow->HandleMouseButton(mx, my, true);
1808 mainMenu.HandleMouseButton(mx, my, true);
1810 else if (event.type == SDL_MOUSEBUTTONUP)
1812 uint32 mx = event.button.x, my = event.button.y;
1815 mainWindow->HandleMouseButton(mx, my, false);
1817 mainMenu.HandleMouseButton(mx, my, false);
1820 //PROBLEM: In order to use the dirty rectangle approach here, we need some way of
1821 // handling it in mainMenu.Draw() and mainWindow->Draw(). !!! FIX !!!
1822 //POSSIBLE SOLUTION:
1823 // When mouse is moving and not on menu or window, can do straight dirty rect.
1824 // When mouse is on menu, need to update screen. Same for buttons on windows...
1825 // What the menu & windows should do is only redraw on a state change. IOW, they
1826 // should call their own/child window's Draw() function instead of doing it top
1828 //#define NEW_BACKSTORE_METHOD
1831 // The way we do things here is kinda stupid (redrawing the screen every frame), but
1832 // it's simple. Perhaps there may be a reason down the road to be more selective with
1833 // our clearing, but for now, this will suffice.
1834 // memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
1835 // memcpy(backbuffer, background, tom_getVideoModeWidth() * 256 * 2);
1836 // memcpy(backbuffer, background, tom_getVideoModeWidth() * 256 * 4);
1837 #ifndef NEW_BACKSTORE_METHOD
1838 memset(sdlemuGetOverlayPixels(), 0, sdlemuGetOverlayWidthInPixels() * 480 * 4);
1841 //Could do multiple windows here by using a vector + priority info...
1842 //Though the way ZSNES does it seems to be by a bool (i.e., they're always active, just not shown)
1847 /*uint32 pBGS[6 * 8 + 3] = { 6, 8, 4,
1857 //This isn't working... Why????
1858 //It's because DrawTransparentBitmap does alpha blending if it detects zero in the alpha channel.
1859 //So why do it that way? Hm.
1860 overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
1862 #ifdef NEW_BACKSTORE_METHOD
1863 // DrawTransparentBitmapDeprecated(overlayPixels, oldMouseX, oldMouseY, pointerBGSave);
1864 // DrawTransparentBitmap(overlayPixels, oldMouseX, oldMouseY, pBGS);
1865 for(uint32 y=0; y<pointerBGSave[1]; y++)
1866 for(uint32 x=0; x<pointerBGSave[0]; x++)
1867 overlayPixels[((oldMouseY + y) * sdlemuGetOverlayWidthInPixels()) + (oldMouseX + x)] = 0x00000000;
1871 for(uint32 y=0; y<pointerBGSave[1]; y++)
1872 for(uint32 x=0; x<pointerBGSave[0]; x++)
1873 pointerBGSave[count++] = overlayPixels[((mouseY + y) * sdlemuGetOverlayWidthInPixels()) + (mouseX + x)];
1877 // DrawTransparentBitmapDeprecated(backbuffer, mouseX, mouseY, mousePic);
1878 DrawTransparentBitmapDeprecated(overlayPixels, mouseX, mouseY, mousePic);
1888 // GUI "action" functions
1891 Window * LoadROM(void)
1893 FileList * fileList = new FileList(20, 20, 600, 440);
1895 return (Window *)fileList;
1898 Window * ResetJaguar(void)
1905 Window * ResetJaguarCD(void)
1907 memcpy(jaguar_mainRom, jaguar_CDBootROM, 0x40000);
1908 jaguarRunAddress = 0x802000;
1909 jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, 0x40000);
1911 //This is a quick kludge to get the CDBIOS to boot properly...
1912 //Wild speculation: It could be that this memory location is wired into the CD unit
1913 //somehow, which lets it know whether or not a cart is present in the unit...
1914 jaguar_mainRom[0x0040B] = 0x03;
1922 bool debounceRunKey = true;
1923 Window * RunEmu(void)
1925 extern uint32 * backbuffer;
1926 //Temporary, to test the new timer based code...
1927 sdlemuDisableOverlay();
1929 sdlemuEnableOverlay();
1930 // Save the background for the GUI...
1931 // In this case, we squash the color to monochrome, then force it to blue + green...
1932 for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
1934 uint32 pixel = backbuffer[i];
1935 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
1936 pixel = ((r + g + b) / 3) & 0x00FF;
1937 backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
1941 //This is crappy... !!! FIX !!!
1942 extern bool finished, showGUI;
1944 // uint32 nFrame = 0, nFrameskip = 0;
1945 uint32 totalFrames = 0;
1947 bool showMessage = true;
1948 uint32 showMsgFrames = 120;
1949 uint8 transparency = 0;
1950 // Pass a message to the "joystick" code to debounce the ESC key...
1951 debounceRunKey = true;
1953 uint32 cartType = 4;
1954 if (jaguarRomSize == 0x200000)
1956 else if (jaguarRomSize == 0x400000)
1958 else if (jaguar_mainRom_crc32 == 0x687068D5)
1960 else if (jaguar_mainRom_crc32 == 0x55A0669C)
1963 char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
1964 uint32 elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
1968 // Set up new backbuffer with new pixels and data
1969 JaguarExecute(backbuffer, true);
1970 // JaguarExecuteNew();
1972 //WriteLog("Frame #%u...\n", totalFrames);
1973 //extern bool doDSPDis;
1974 //if (totalFrames == 373)
1977 //This sucks... !!! FIX !!!
1979 //This is done here so that the crud below doesn't get on our GUI background...
1983 // Some QnD GUI stuff here...
1986 extern uint32 gpu_pc, dsp_pc;
1987 DrawString(backbuffer, 8, 8, false, "GPU PC: %08X", gpu_pc);
1988 DrawString(backbuffer, 8, 16, false, "DSP PC: %08X", dsp_pc);
1989 DrawString(backbuffer, 8, 32, false, "%u FPS", framesPerSecond);
1994 // FF0F -> 1111 11 11 000 0 1111 -> 3F 18 0F
1995 // 3FE3 -> 0011 11 11 111 0 0011 -> 0F 3F 03
1996 /* DrawStringTrans((uint32 *)backbuffer, 8, 24*8, 0xFF0F, transparency, "Running...");
1997 DrawStringTrans((uint32 *)backbuffer, 8, 26*8, 0x3FE3, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
1998 DrawStringTrans((uint32 *)backbuffer, 8, 27*8, 0x3FE3, transparency, "CRC: %08X", jaguar_mainRom_crc32);//*/
1999 //first has wrong color. !!! FIX !!!
2000 DrawStringTrans(backbuffer, 8, 24*8, 0xFF7F63FF, transparency, "Running...");
2001 DrawStringTrans(backbuffer, 8, 26*8, 0xFF1FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
2002 DrawStringTrans(backbuffer, 8, 27*8, 0xFF1FFF3F, transparency, "CRC: %08X", jaguar_mainRom_crc32);
2004 if (showMsgFrames == 0)
2008 if (transparency == 33)
2010 showMessage = false;
2011 /*extern bool doGPUDis;
2012 doGPUDis = true;//*/
2023 if (SDL_GetTicks() - elapsedTicks > 250)
2024 elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
2027 // Reset the pitch, since it may have been changed in-game...
2028 Element::SetScreenAndPitch((uint32 *)backbuffer, GetSDLScreenWidthInPixels());
2030 // Save the background for the GUI...
2031 // memcpy(background, backbuffer, tom_getVideoModeWidth() * 240 * 2);
2032 // In this case, we squash the color to monochrome, then force it to blue + green...
2033 for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
2035 uint32 pixel = backbuffer[i];
2036 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
2037 pixel = ((r + g + b) / 3) & 0x00FF;
2038 background[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
2046 bool debounceRunKey = true;
2047 Window * RunEmu(void)
2049 extern uint32 * backbuffer;
2050 uint32 * overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
2051 memset(overlayPixels, 0x00, 640 * 480 * 4); // Clear out overlay...
2053 //This is crappy... !!! FIX !!!
2054 extern bool finished, showGUI;
2056 sdlemuDisableOverlay();
2058 // uint32 nFrame = 0, nFrameskip = 0;
2059 uint32 totalFrames = 0;
2061 bool showMessage = true;
2062 uint32 showMsgFrames = 120;
2063 uint8 transparency = 0xFF;
2064 // Pass a message to the "joystick" code to debounce the ESC key...
2065 debounceRunKey = true;
2067 uint32 cartType = 4;
2068 if (jaguarRomSize == 0x200000)
2070 else if (jaguarRomSize == 0x400000)
2072 else if (jaguar_mainRom_crc32 == 0x687068D5)
2074 else if (jaguar_mainRom_crc32 == 0x55A0669C)
2077 const char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
2078 uint32 elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
2082 // Set up new backbuffer with new pixels and data
2085 //WriteLog("Frame #%u...\n", totalFrames);
2086 //extern bool doDSPDis;
2087 //if (totalFrames == 373)
2090 //Problem: Need to do this *only* when the state changes from visible to not...
2091 //Also, need to clear out the GUI when not on (when showMessage is active...)
2092 if (showGUI || showMessage)
2093 sdlemuEnableOverlay();
2095 sdlemuDisableOverlay();
2097 //Add in a new function for clearing patches of screen (ClearOverlayRect)
2099 // Some QnD GUI stuff here...
2102 FillScreenRectangle(overlayPixels, 8, 1*FONT_HEIGHT, 128, 4*FONT_HEIGHT, 0x00000000);
2103 extern uint32 gpu_pc, dsp_pc;
2104 DrawString(overlayPixels, 8, 1*FONT_HEIGHT, false, "GPU PC: %08X", gpu_pc);
2105 DrawString(overlayPixels, 8, 2*FONT_HEIGHT, false, "DSP PC: %08X", dsp_pc);
2106 DrawString(overlayPixels, 8, 4*FONT_HEIGHT, false, "%u FPS", framesPerSecond);
2111 DrawString2(overlayPixels, 8, 24*FONT_HEIGHT, 0x007F63FF, transparency, "Running...");
2112 DrawString2(overlayPixels, 8, 26*FONT_HEIGHT, 0x001FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
2113 DrawString2(overlayPixels, 8, 27*FONT_HEIGHT, 0x001FFF3F, transparency, "CRC: %08X", jaguar_mainRom_crc32);
2115 if (showMsgFrames == 0)
2119 if (transparency == 0)
2121 showMessage = false;
2122 /*extern bool doGPUDis;
2123 doGPUDis = true;//*/
2133 if (SDL_GetTicks() - elapsedTicks > 250)
2134 elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
2137 // Save the background for the GUI...
2138 // In this case, we squash the color to monochrome, then force it to blue + green...
2139 for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
2141 uint32 pixel = backbuffer[i];
2142 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
2143 pixel = ((r + g + b) / 3) & 0x00FF;
2144 backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
2147 sdlemuEnableOverlay();
2157 WriteLog("GUI: Quitting due to user request.\n");
2163 Window * About(void)
2166 // sprintf(buf, "Virtual Jaguar CVS %s", __DATE__);
2167 sprintf(buf, "SVN %s", __DATE__);
2168 //fprintf(fp, "VirtualJaguar v1.0.8 (Last full build was on %s %s)\n", __DATE__, __TIME__);
2169 //VirtualJaguar v1.0.8 (Last full build was on Dec 30 2004 20:01:31)
2170 //Hardwired, bleh... !!! FIX !!!
2171 uint32 width = 55 * FONT_WIDTH, height = 18 * FONT_HEIGHT;
2172 uint32 xpos = (640 - width) / 2, ypos = (480 - height) / 2;
2173 // Window * window = new Window(8, 16, 50 * FONT_WIDTH, 21 * FONT_HEIGHT);
2174 Window * window = new Window(xpos, ypos, width, height);
2175 // window->AddElement(new Text(8, 8, "Virtual Jaguar 1.0.8"));
2176 // window->AddElement(new Text(8, 8, "Virtual Jaguar CVS 20050110", 0xFF3030FF, 0xFF000000));
2177 // window->AddElement(new Text(208, 8+0*FONT_HEIGHT, buf, 0xFF3030FF, 0xFF000000));
2178 window->AddElement(new Text(248, 8+4*FONT_HEIGHT+5, buf, 0xFF3030FF, 0xFF000000));
2179 window->AddElement(new Text(8, 8+0*FONT_HEIGHT, "Coders:"));
2180 window->AddElement(new Text(16, 8+1*FONT_HEIGHT, "James L. Hammons (shamus)"));
2181 window->AddElement(new Text(16, 8+2*FONT_HEIGHT, "Niels Wagenaar (nwagenaar)"));
2182 window->AddElement(new Text(16, 8+3*FONT_HEIGHT, "Carwin Jones (Caz)"));
2183 window->AddElement(new Text(16, 8+4*FONT_HEIGHT, "Adam Green"));
2184 window->AddElement(new Text(8, 8+6*FONT_HEIGHT, "Testers:"));
2185 window->AddElement(new Text(16, 8+7*FONT_HEIGHT, "Guruma"));
2186 window->AddElement(new Text(8, 8+9*FONT_HEIGHT, "Thanks go out to:"));
2187 window->AddElement(new Text(16, 8+10*FONT_HEIGHT, "Aaron Giles for the original CoJag"));
2188 window->AddElement(new Text(16, 8+11*FONT_HEIGHT, "David Raingeard for the original VJ"));
2189 window->AddElement(new Text(16, 8+12*FONT_HEIGHT, "Karl Stenerud for his Musashi 68K emu"));
2190 window->AddElement(new Text(16, 8+13*FONT_HEIGHT, "Sam Lantinga for his amazing SDL libs"));
2191 window->AddElement(new Text(16, 8+14*FONT_HEIGHT, "Ryan C. Gordon for VJ's web presence"));
2192 window->AddElement(new Text(16, 8+15*FONT_HEIGHT, "Curt Vendel for various Jaguar goodies"));
2193 window->AddElement(new Text(16, 8+16*FONT_HEIGHT, "The guys over at Atari Age ;-)"));
2194 // window->AddElement(new Image(8, 8, &vj_title_small));
2195 window->AddElement(new Image(width - (vj_title_small.width + 8), 8, &vj_title_small));
2200 Window * MiscOptions(void)
2202 Window * window = new Window(8, 16, 304, 192);
2203 window->AddElement(new PushButton(8, 8, &vjs.useJaguarBIOS, "BIOS"));
2204 window->AddElement(new SlideSwitch(8, 32, &vjs.hardwareTypeNTSC, "PAL", "NTSC"));
2205 window->AddElement(new PushButton(8, 64, &vjs.DSPEnabled, "DSP"));
2206 window->AddElement(new SlideSwitch(24, 88, &vjs.usePipelinedDSP, "Original", "Pipelined"));
2207 window->AddElement(new SlideSwitch(8, 120, (bool *)&vjs.glFilter, "Sharp", "Blurry"));
2208 window->AddElement(new SlideSwitch(8, 152, (bool *)&vjs.renderType, "Normal render", "TV style"));
2210 window->AddElement(new TextEdit(88, 8, vjs.ROMPath, 20, 0xFF8484FF, 0xFF000000));
2212 /*TextEdit(uint32 x, uint32 y, string s, uint32 mss = 10, uint32 fg = 0xFF8484FF,
2213 uint32 bg = 0xFF84FF4D): Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s),
2214 caretPos(0), maxScreenSize(mss) {}*/
2223 // * Window/fullscreen
2224 // * Key definitions
2231 // Generic ROM loading
2233 uint32 JaguarLoadROM(uint8 * rom, char * path)
2235 // We really should have some kind of sanity checking for the ROM size here to prevent
2236 // a buffer overflow... !!! FIX !!!
2239 WriteLog("JaguarLoadROM: Attempting to load file '%s'...", path);
2240 char * ext = strrchr(path, '.');
2242 WriteLog("FAILED!\n");
2244 WriteLog("Succeeded in finding extension (%s)!\n", ext);
2248 WriteLog("VJ: Loading \"%s\"...", path);
2250 if (strcasecmp(ext, ".zip") == 0)
2252 // Handle ZIP file loading here...
2253 WriteLog("(ZIPped)...");
2255 if (load_zipped_file(0, 0, path, NULL, &rom, &romSize) == -1)
2257 WriteLog("Failed!\n");
2263 /* FILE * fp = fopen(path, "rb");
2267 WriteLog("Failed!\n");
2271 fseek(fp, 0, SEEK_END);
2272 romSize = ftell(fp);
2273 fseek(fp, 0, SEEK_SET);
2274 fread(rom, 1, romSize, fp);
2277 // Handle gzipped files transparently [Adam Green]...
2279 gzFile fp = gzopen(path, "rb");
2283 WriteLog("Failed!\n");
2287 romSize = gzfilelength(fp);
2288 gzseek(fp, 0, SEEK_SET);
2289 gzread(fp, rom, romSize);
2293 WriteLog("OK (%i bytes)\n", romSize);
2300 // Jaguar file loading
2302 bool JaguarLoadFile(char * path)
2304 // jaguarRomSize = JaguarLoadROM(mem, path);
2305 jaguarRomSize = JaguarLoadROM(jaguar_mainRom, path);
2307 /*//This is not *nix friendly for some reason...
2308 // if (!UserSelectFile(path, newPath))
2309 if (!UserSelectFile((strlen(path) == 0 ? (char *)"." : path), newPath))
2311 WriteLog("VJ: Could not find valid ROM in directory \"%s\"...\nAborting!\n", path);
2316 if (jaguarRomSize == 0)
2318 // WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
2319 WriteLog("GUI: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
2320 // Need to do something else here, like throw up an error dialog instead of aborting. !!! FIX !!!
2323 return false; // This is a start...
2326 jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, jaguarRomSize);
2327 WriteLog("CRC: %08X\n", (unsigned int)jaguar_mainRom_crc32);
2330 jaguarRunAddress = 0x802000;
2332 char * ext = strrchr(path, '.'); // Get the file's extension for non-cartridge checking
2334 //NOTE: Should fix JaguarLoadROM() to replace .zip with what's *in* the zip (.abs, .j64, etc.)
2335 if (strcasecmp(ext, ".rom") == 0)
2337 // File extension ".ROM": Alpine image that loads/runs at $802000
2338 WriteLog("GUI: Setting up homebrew (ROM)... Run address: 00802000, length: %08X\n", jaguarRomSize);
2340 for(int i=jaguarRomSize-1; i>=0; i--)
2341 jaguar_mainRom[0x2000 + i] = jaguar_mainRom[i];
2343 memset(jaguar_mainRom, 0xFF, 0x2000);
2344 /* memcpy(jaguar_mainRam, jaguar_mainRom, jaguarRomSize);
2345 memset(jaguar_mainRom, 0xFF, 0x600000);
2346 memcpy(jaguar_mainRom + 0x2000, jaguar_mainRam, jaguarRomSize);
2347 memset(jaguar_mainRam, 0x00, 0x400000);*/
2350 Stubulator ROM vectors...
2351 handler 001 at $00E00008
2352 handler 002 at $00E008DE
2353 handler 003 at $00E008E2
2354 handler 004 at $00E008E6
2355 handler 005 at $00E008EA
2356 handler 006 at $00E008EE
2357 handler 007 at $00E008F2
2358 handler 008 at $00E0054A
2359 handler 009 at $00E008FA
2360 handler 010 at $00000000
2361 handler 011 at $00000000
2362 handler 012 at $00E008FE
2363 handler 013 at $00E00902
2364 handler 014 at $00E00906
2365 handler 015 at $00E0090A
2366 handler 016 at $00E0090E
2367 handler 017 at $00E00912
2368 handler 018 at $00E00916
2369 handler 019 at $00E0091A
2370 handler 020 at $00E0091E
2371 handler 021 at $00E00922
2372 handler 022 at $00E00926
2373 handler 023 at $00E0092A
2374 handler 024 at $00E0092E
2375 handler 025 at $00E0107A
2376 handler 026 at $00E0107A
2377 handler 027 at $00E0107A
2378 handler 028 at $00E008DA
2379 handler 029 at $00E0107A
2380 handler 030 at $00E0107A
2381 handler 031 at $00E0107A
2382 handler 032 at $00000000
2384 Let's try setting up the illegal instruction vector for a stubulated jaguar...
2386 /* SET32(jaguar_mainRam, 0x08, 0x00E008DE);
2387 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
2388 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
2389 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
2391 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
2392 // This kludge works! Yeah!
2393 SET32(jaguar_mainRam, 0x10, 0x00001000);
2394 SET16(jaguar_mainRam, 0x1000, 0x60FE); // Here: bra Here
2396 else if (strcasecmp(ext, ".abs") == 0)
2398 // File extension ".ABS": Atari linker output file with header (w/o is useless to us here)
2401 ABS Format sleuthing (LBUGDEMO.ABS):
2403 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
2404 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
2407 DRI-format file detected...
2408 Text segment size = 0x0000050c bytes
2409 Data segment size = 0x000462c0 bytes
2410 BSS Segment size = 0x00000428 bytes
2411 Symbol Table size = 0x000012a6 bytes
2412 Absolute Address for text segment = 0x00802000
2413 Absolute Address for data segment = 0x0080250c
2414 Absolute Address for BSS segment = 0x00004000
2417 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
2418 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
2419 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
2421 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
2422 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
2423 000050 00 00 00 00 00 00 00 20
2424 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
2425 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
2426 000078 00 00 00 00 00 00 00 40
2427 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
2428 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
2429 0000a0 00 00 00 00 00 00 00 80
2431 Header size is $A8 bytes...
2433 BSD/COFF format file detected...
2434 3 sections specified
2435 Symbol Table offset = 230160 ($00038310)
2436 Symbol Table contains 1339 symbol entries ($0000053B)
2437 The additional header size is 28 bytes ($001C)
2438 Magic Number for RUN_HDR = 0x00000107
2439 Text Segment Size = 7632 ($00001DD0)
2440 Data Segment Size = 222360 ($00036498)
2441 BSS Segment Size = 428928 ($00068B80)
2442 Starting Address for executable = 0x00802000
2443 Start of Text Segment = 0x00802000
2444 Start of Data Segment = 0x00803dd0
2446 if (jaguar_mainRom[0] == 0x60 && jaguar_mainRom[1] == 0x1B)
2448 uint32 loadAddress = GET32(jaguar_mainRom, 0x16), //runAddress = GET32(jaguar_mainRom, 0x2A),
2449 codeSize = GET32(jaguar_mainRom, 0x02) + GET32(jaguar_mainRom, 0x06);
2450 WriteLog("GUI: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
2452 if (loadAddress < 0x800000)
2453 memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x24, codeSize);
2456 for(int i=codeSize-1; i>=0; i--)
2457 jaguar_mainRom[(loadAddress - 0x800000) + i] = jaguar_mainRom[i + 0x24];
2458 /* memcpy(jaguar_mainRam, jaguar_mainRom + 0x24, codeSize);
2459 memset(jaguar_mainRom, 0xFF, 0x600000);
2460 memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
2461 memset(jaguar_mainRam, 0x00, 0x400000);*/
2464 jaguarRunAddress = loadAddress;
2466 else if (jaguar_mainRom[0] == 0x01 && jaguar_mainRom[1] == 0x50)
2468 uint32 loadAddress = GET32(jaguar_mainRom, 0x28), runAddress = GET32(jaguar_mainRom, 0x24),
2469 codeSize = GET32(jaguar_mainRom, 0x18) + GET32(jaguar_mainRom, 0x1C);
2470 WriteLog("GUI: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
2472 if (loadAddress < 0x800000)
2473 memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0xA8, codeSize);
2476 for(int i=codeSize-1; i>=0; i--)
2477 jaguar_mainRom[(loadAddress - 0x800000) + i] = jaguar_mainRom[i + 0xA8];
2478 /* memcpy(jaguar_mainRam, jaguar_mainRom + 0xA8, codeSize);
2479 memset(jaguar_mainRom, 0xFF, 0x600000);
2480 memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
2481 memset(jaguar_mainRam, 0x00, 0x400000);*/
2484 jaguarRunAddress = runAddress;
2488 WriteLog("GUI: Couldn't find correct ABS format: %02X %02X\n", jaguar_mainRom[0], jaguar_mainRom[1]);
2492 else if (strcasecmp(ext, ".jag") == 0)
2494 // File extension ".JAG": Atari server file with header
2495 //NOTE: The bytes 'JAGR' should also be at position $1C...
2496 // Also, there's *always* a $601A header at position $00...
2497 if (jaguar_mainRom[0] == 0x60 && jaguar_mainRom[1] == 0x1A)
2499 uint32 loadAddress = GET32(jaguar_mainRom, 0x22), runAddress = GET32(jaguar_mainRom, 0x2A);
2500 //This is not always right! Especially when converted via bin2jag1!!!
2501 //We should have access to the length of the furshlumiger file that was loaded anyway!
2503 // uint32 progLength = GET32(jaguar_mainRom, 0x02);
2506 // WriteLog("Jaguar: Setting up PD ROM... Run address: %08X, length: %08X\n", runAddress, progLength);
2507 // memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, progLength);
2508 WriteLog("GUI: Setting up homebrew (JAG)... Run address: %08X, length: %08X\n", runAddress, jaguarRomSize - 0x2E);
2509 memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, jaguarRomSize - 0x2E);
2510 // SET32(jaguar_mainRam, 4, runAddress);
2511 jaguarRunAddress = runAddress;
2516 // .J64 (Jaguar cartridge ROM image) is implied by the FileList object...
2522 // Get the length of a (possibly) gzipped file
2524 int gzfilelength(gzFile gd)
2526 int size = 0, length = 0;
2527 unsigned char buffer[0x10000];
2533 // Read in chunks until EOF
2534 size = gzread(gd, buffer, 0x10000);