4 // Graphical User Interface support
9 #include <sys/types.h> // For MacOS <dirent.h> dependency
15 #include <ctype.h> // For toupper()
21 #include "font14pt.h" // Also 15, 16, 17, 18
22 #include "guielements.h"
26 #include "sdlemu_opengl.h"
29 using namespace std; // For STL stuff
31 // Private function prototypes
33 class Window; // Forward declaration...
35 //void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap, uint8 * alpha = NULL);
36 void DrawTransparentBitmapDeprecated(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap);
37 void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap);
38 void DrawBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap);
39 //Should call this FillScreenRectangle with a number representing the RGBA value to fill. !!! FIX !!!
40 //void ClearScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h);
41 void FillScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h, uint32 color);
42 void DrawStringTrans(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 opacity, const char * text, ...);
43 void DrawStringOpaque(uint32 * screen, uint32 x, uint32 y, uint32 color1, uint32 color2, const char * text, ...);
44 void DrawString(uint32 * screen, uint32 x, uint32 y, bool invert, const char * text, ...);
45 void DrawString2(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 transparency, const char * text, ...);
46 Window * LoadROM(void);
47 Window * ResetJaguar(void);
48 Window * ResetJaguarCD(void);
49 Window * RunEmu(void);
52 Window * MiscOptions(void);
54 int gzfilelength(gzFile gd);
58 extern uint8 * jaguar_mainRam;
59 extern uint8 * jaguar_mainRom;
60 extern uint8 * jaguar_bootRom;
61 extern uint8 * jaguar_CDBootROM;
62 extern bool BIOSLoaded;
63 extern bool CDBIOSLoaded;
65 // Local global variables
67 bool exitGUI = false; // GUI (emulator) done variable
68 int mouseX = 0, mouseY = 0;
69 uint32 background[1280 * 256]; // GUI background buffer
71 char separator[] = "--------------------------------------------------------";
74 // Case insensitive string compare function
75 // Taken straight out of Thinking In C++ by Bruce Eckel. Thanks Bruce!
78 int stringCmpi(const string &s1, const string &s2)
80 // Select the first element of each string:
81 string::const_iterator p1 = s1.begin(), p2 = s2.begin();
83 while (p1 != s1.end() && p2 != s2.end()) // Don
\92t run past the end
85 if (toupper(*p1) != toupper(*p2)) // Compare upper-cased chars
86 return (toupper(*p1) < toupper(*p2) ? -1 : 1);// Report which was lexically greater
92 // If they match up to the detected eos, say which was longer. Return 0 if the same.
93 return s2.size() - s1.size();
100 enum { WINDOW_CLOSE, MENU_ITEM_CHOSEN };
105 Element(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0)
106 { extents.x = x, extents.y = y, extents.w = w, extents.h = h; }
107 virtual void HandleKey(SDLKey key) = 0; // These are "pure" virtual functions...
108 virtual void HandleMouseMove(uint32 x, uint32 y) = 0;
109 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) = 0;
110 virtual void Draw(uint32, uint32) = 0;
111 virtual void Notify(Element *) = 0;
112 //Needed? virtual ~Element() = 0;
113 //We're not allocating anything in the base class, so the answer would be NO.
114 bool Inside(uint32 x, uint32 y);
116 // static void SetScreenAndPitch(int16 * s, uint32 p) { screenBuffer = s, pitch = p; }
117 static void SetScreenAndPitch(uint32 * s, uint32 p) { screenBuffer = s, pitch = p; }
122 // Class variables...
123 // static int16 * screenBuffer;
124 static uint32 * screenBuffer;
128 // Initialize class variables (Element)
129 //int16 * Element::screenBuffer = NULL;
130 uint32 * Element::screenBuffer = NULL;
131 uint32 Element::pitch = 0;
133 bool Element::Inside(uint32 x, uint32 y)
135 return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
136 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false);
144 class Button: public Element
147 Button(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
148 activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
149 bgColor(0xFF00FF00), pic(NULL), elementToTell(NULL) {}
150 Button(uint32 x, uint32 y, uint32 w, uint32 h, uint32 * p): Element(x, y, w, h),
151 activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
152 bgColor(0xFF00FF00), pic(p), elementToTell(NULL) {}
153 // Button(uint32 x, uint32 y, uint32 * p): Element(x, y, 0, 0),
154 Button(uint32 x, uint32 y, uint32 * p, uint32 * pH = NULL, uint32 * pD = NULL): Element(x, y, 0, 0),
155 activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
156 bgColor(0xFF00FF00), pic(p), picHover(pH), picDown(pD), elementToTell(NULL)
157 { if (pic) extents.w = pic[0], extents.h = pic[1]; }
158 Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
159 activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
160 bgColor(0xFF00FF00), pic(NULL), text(s), elementToTell(NULL) {}
161 Button(uint32 x, uint32 y, string s): Element(x, y, 0, FONT_HEIGHT),
162 activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
163 bgColor(0xFF00FF00), pic(NULL), text(s), elementToTell(NULL)
164 { extents.w = s.length() * FONT_WIDTH; }
165 virtual void HandleKey(SDLKey key) {}
166 virtual void HandleMouseMove(uint32 x, uint32 y);
167 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
168 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
169 virtual void Notify(Element *) {}
170 bool ButtonClicked(void) { return activated; }
171 void SetNotificationElement(Element * e) { elementToTell = e; }
174 bool activated, clicked, inside;
175 uint32 fgColor, bgColor;
176 uint32 * pic, * picHover, * picDown;
178 Element * elementToTell;
181 void Button::HandleMouseMove(uint32 x, uint32 y)
183 inside = Inside(x, y);
186 void Button::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
193 if (clicked && !mouseDown)
195 clicked = false, activated = true;
197 // Send a message that we're activated (if there's someone to tell, that is)
199 elementToTell->Notify(this);
203 clicked = activated = false;
206 void Button::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
208 uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
210 if (text.length() > 0) // Simple text button
213 for(uint32 y=0; y<extents.h; y++)
215 for(uint32 x=0; x<extents.w; x++)
217 // Doesn't clip in y axis! !!! FIX !!!
218 if (extents.x + x < pitch)
219 screenBuffer[addr + x + (y * pitch)]
220 // = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
221 //43F0 -> 010000 11111 10000 -> 0100 0001 1111 1111 1000 0100 -> 41 FF 84
222 = (clicked && inside ? fgColor : (inside ? 0xFF84FF41 : bgColor));
226 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
228 else // Graphical button
230 uint32 * picToShow = pic;
232 if (picHover != NULL && inside && !clicked)
233 picToShow = picHover;
235 if (picDown != NULL && inside && clicked)
238 DrawTransparentBitmapDeprecated(screenBuffer, extents.x + offsetX, extents.y + offsetY, picToShow);
247 class PushButton: public Element
250 // Save state externally?
251 //We pass in a state variable if we want to track it externally, otherwise we use our own
252 //internal state var. Still need to do some kind of callback for pushbuttons that do things
253 //like change from fullscreen to windowed... !!! FIX !!!
256 // PushButton(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
257 // activated(false), clicked(false), inside(false), fgColor(0xFFFF),
258 // bgColor(0x03E0), pic(NULL), elementToTell(NULL) {}
259 // PushButton(uint32 x, uint32 y, bool * st, string s): Element(x, y, 8, 8), state(st),
260 // inside(false), text(s) { if (st == NULL) state = &internalState; }
261 PushButton(uint32 x, uint32 y, bool * st, string s): Element(x, y, 16, 16), state(st),
262 inside(false), text(s) { if (st == NULL) state = &internalState; }
263 /* Button(uint32 x, uint32 y, uint32 w, uint32 h, uint32 * p): Element(x, y, w, h),
264 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
265 bgColor(0x03E0), pic(p), elementToTell(NULL) {}
266 Button(uint32 x, uint32 y, uint32 * p): Element(x, y, 0, 0),
267 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
268 bgColor(0x03E0), pic(p), elementToTell(NULL)
269 { if (pic) extents.w = pic[0], extents.h = pic[1]; }
270 Button(uint32 x, uint32 y, uint32 w, uint32 h, string s): Element(x, y, w, h),
271 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
272 bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL) {}
273 PushButton(uint32 x, uint32 y, string s): Element(x, y, 0, 8),
274 activated(false), clicked(false), inside(false), fgColor(0xFFFF),
275 bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL)
276 { extents.w = s.length() * 8; }*/
277 virtual void HandleKey(SDLKey key) {}
278 virtual void HandleMouseMove(uint32 x, uint32 y);
279 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
280 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
281 virtual void Notify(Element *) {}
282 // bool ButtonClicked(void) { return activated; }
283 // void SetNotificationElement(Element * e) { elementToTell = e; }
288 // bool activated, clicked, inside;
289 // uint16 fgColor, bgColor;
292 // Element * elementToTell;
296 void PushButton::HandleMouseMove(uint32 x, uint32 y)
298 inside = Inside(x, y);
301 void PushButton::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
303 if (inside && mouseDown)
308 if (clicked && !mouseDown)
310 clicked = false, activated = true;
312 // Send a message that we're activated (if there's someone to tell, that is)
314 elementToTell->Notify(this);
319 // clicked = activated = false;
322 void PushButton::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
324 /* uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
326 for(uint32 y=0; y<extents.h; y++)
328 for(uint32 x=0; x<extents.w; x++)
330 // Doesn't clip in y axis! !!! FIX !!!
331 if (extents.x + x < pitch)
332 screenBuffer[addr + x + (y * pitch)]
333 = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
338 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, &pbDown);
340 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, &pbUp);
342 if (text.length() > 0)
343 DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY, false, "%s", text.c_str());
351 class SlideSwitch: public Element
354 // Save state externally?
355 //Seems to be handled the same as PushButton, but without sanity checks. !!! FIX !!!
358 SlideSwitch(uint32 x, uint32 y, bool * st, string s1, string s2): Element(x, y, 16, 32), state(st),
359 inside(false), text1(s1), text2(s2) {}
360 virtual void HandleKey(SDLKey key) {}
361 virtual void HandleMouseMove(uint32 x, uint32 y);
362 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
363 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
364 virtual void Notify(Element *) {}
365 // bool ButtonClicked(void) { return activated; }
366 // void SetNotificationElement(Element * e) { elementToTell = e; }
371 // bool activated, clicked, inside;
372 // uint16 fgColor, bgColor;
375 // Element * elementToTell;
378 void SlideSwitch::HandleMouseMove(uint32 x, uint32 y)
380 inside = Inside(x, y);
383 void SlideSwitch::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
385 if (inside && mouseDown)
390 if (clicked && !mouseDown)
392 clicked = false, activated = true;
394 // Send a message that we're activated (if there's someone to tell, that is)
396 elementToTell->Notify(this);
401 // clicked = activated = false;
404 void SlideSwitch::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
406 DrawTransparentBitmapDeprecated(screenBuffer, extents.x + offsetX, extents.y + offsetY, (*state ? slideSwitchDown : slideSwitchUp));
408 if (text1.length() > 0)
409 DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY, false, "%s", text1.c_str());
411 if (text2.length() > 0)
412 DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY + 16, false, "%s", text2.c_str());
420 class Window: public Element
423 /* Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
424 fgColor(0x4FF0), bgColor(0xFE10)
425 { close = new Button(w - 8, 1, closeBox); list.push_back(close); }*/
426 Window(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0,
427 void (* f)(Element *) = NULL): Element(x, y, w, h),
428 // /*clicked(false), inside(false),*/ fgColor(0x4FF0), bgColor(0x1E10),
429 //4FF0 -> 010011 11111 10000 -> 0100 1101 1111 1111 1000 0100 -> 4D FF 84
430 //1E10 -> 000111 10000 10000 -> 0001 1111 1000 0100 1000 0100 -> 1F 84 84
431 /*clicked(false), inside(false),*/ fgColor(0xFF84FF4D), bgColor(0xFF84841F),
433 { close = new Button(w - (CLOSEBOX_WIDTH + 1), 1, closeBox, closeBoxHover, closeBoxDown);
434 list.push_back(close);
435 close->SetNotificationElement(this); }
437 virtual void HandleKey(SDLKey key);
438 virtual void HandleMouseMove(uint32 x, uint32 y);
439 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
440 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
441 virtual void Notify(Element * e);
442 void AddElement(Element * e);
443 // bool WindowActive(void) { return true; }//return !close->ButtonClicked(); }
446 // bool clicked, inside;
447 uint32 fgColor, bgColor;
448 void (* handler)(Element *);
450 //We have to use a list of Element *pointers* because we can't make a list that will hold
451 //all the different object types in the same list...
452 vector<Element *> list;
457 for(uint32 i=0; i<list.size(); i++)
462 void Window::HandleKey(SDLKey key)
464 if (key == SDLK_ESCAPE)
467 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
468 SDL_PushEvent(&event);
471 // Handle the items this window contains...
472 for(uint32 i=0; i<list.size(); i++)
473 // Make coords relative to upper right corner of this window...
474 list[i]->HandleKey(key);
477 void Window::HandleMouseMove(uint32 x, uint32 y)
479 // Handle the items this window contains...
480 for(uint32 i=0; i<list.size(); i++)
481 // Make coords relative to upper right corner of this window...
482 list[i]->HandleMouseMove(x - extents.x, y - extents.y);
485 void Window::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
487 // Handle the items this window contains...
488 for(uint32 i=0; i<list.size(); i++)
489 // Make coords relative to upper right corner of this window...
490 list[i]->HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
493 void Window::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
495 uint32 addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
497 for(uint32 y=0; y<extents.h; y++)
499 for(uint32 x=0; x<extents.w; x++)
501 // Doesn't clip in y axis! !!! FIX !!!
502 if (extents.x + x < pitch)
503 screenBuffer[addr + x + (y * pitch)] = bgColor;
507 // Handle the items this window contains...
508 for(uint32 i=0; i<list.size(); i++)
509 list[i]->Draw(extents.x, extents.y);
512 void Window::AddElement(Element * e)
517 void Window::Notify(Element * e)
522 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
523 SDL_PushEvent(&event);
532 class Text: public Element
535 // Text(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
536 // fgColor(0x4FF0), bgColor(0xFE10) {}
537 // Text(uint32 x, uint32 y, string s, uint16 fg = 0x4FF0, uint16 bg = 0xFE10): Element(x, y, 0, 0),
538 // fgColor(fg), bgColor(bg), text(s) {}
539 //4FF0 -> 010011 11111 10000 -> 0100 1101 1111 1111 1000 0100 -> 4D FF 84
540 //FE10 -> 111111 10000 10000 -> 1111 1111 1000 0100 1000 0100 -> FF 84 84
541 Text(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
542 fgColor(0xFF8484FF), bgColor(0xFF84FF4D) {}
543 Text(uint32 x, uint32 y, string s, uint32 fg = 0xFF8484FF, uint32 bg = 0xFF84FF4D):
544 Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s) {}
545 virtual void HandleKey(SDLKey key) {}
546 virtual void HandleMouseMove(uint32 x, uint32 y) {}
547 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
548 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
549 virtual void Notify(Element *) {}
552 uint32 fgColor, bgColor;
556 void Text::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
558 if (text.length() > 0)
559 // DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
560 DrawStringOpaque(screenBuffer, extents.x + offsetX, extents.y + offsetY, fgColor, bgColor, "%s", text.c_str());
565 // Static image class
568 class Image: public Element
571 Image(uint32 x, uint32 y, const void * img): Element(x, y, 0, 0), image(img) {}
572 virtual void HandleKey(SDLKey key) {}
573 virtual void HandleMouseMove(uint32 x, uint32 y) {}
574 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
575 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
576 virtual void Notify(Element *) {}
579 uint32 fgColor, bgColor;
583 void Image::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
586 DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, image);
594 class TextEdit: public Element
597 TextEdit(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
598 fgColor(0xFF8484FF), bgColor(0xFF84FF4D), text(""), caretPos(0),
600 TextEdit(uint32 x, uint32 y, string s, uint32 mss = 10, uint32 fg = 0xFF8484FF,
601 uint32 bg = 0xFF84FF4D): Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s),
602 caretPos(0), maxScreenSize(mss) {}
603 virtual void HandleKey(SDLKey key);
604 virtual void HandleMouseMove(uint32 x, uint32 y) {}
605 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
606 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
607 virtual void Notify(Element *) {}
610 uint32 fgColor, bgColor;
613 uint32 maxScreenSize;
616 //Set different filters depending on type passed in on construction, e.g., filename, amount, etc...?
617 void TextEdit::HandleKey(SDLKey key)
619 if ((key >= SDLK_a && key <= SDLK_z) || (key >= SDLK_0 && key <= SDLK_9) || key == SDLK_PERIOD
620 || key == SDLK_SLASH)
622 //Need to handle shift key as well...
623 text[caretPos++] = key;
626 else if (key == SDLK_BACKSPACE)
630 else if (key == SDLK_DELETE)
636 void TextEdit::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
638 if (text.length() > 0)
640 FillScreenRectangle(screenBuffer, extents.x + offsetX, extents.y + offsetY, FONT_WIDTH * maxScreenSize, FONT_HEIGHT, bgColor);
641 // DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
642 DrawStringOpaque(screenBuffer, extents.x + offsetX, extents.y + offsetY, fgColor, bgColor, "%s", text.c_str());
645 // Draw the caret (underscore? or vertical line?)
653 class ListBox: public Element
654 //class ListBox: public Window
657 // ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0): Element(x, y, w, h),
658 ListBox(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);//: Window(x, y, w, h),
659 // windowPtr(0), cursor(0), limit(0), charWidth((w / 8) - 1), charHeight(h / 8),
660 // elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
661 // downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox) {}
662 virtual void HandleKey(SDLKey key);
663 virtual void HandleMouseMove(uint32 x, uint32 y);
664 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
665 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
666 virtual void Notify(Element * e);
667 void SetNotificationElement(Element * e) { elementToTell = e; }
668 void AddItem(string s);
669 string GetSelectedItem(void);
673 uint32 windowPtr, cursor, limit;
674 uint32 charWidth, charHeight; // Box width/height in characters
675 Element * elementToTell;
676 Button upArrow, downArrow, upArrow2;
680 uint32 yRelativePoint;
683 ListBox::ListBox(uint32 x, uint32 y, uint32 w, uint32 h): Element(x, y, w, h),
684 thumbClicked(false), windowPtr(0), cursor(0), limit(0), charWidth((w / FONT_WIDTH) - 1),
685 charHeight(h / FONT_HEIGHT), elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
686 downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox)
688 upArrow.SetNotificationElement(this);
689 downArrow.SetNotificationElement(this);
690 upArrow2.SetNotificationElement(this);
691 extents.w -= 8; // Make room for scrollbar...
694 void ListBox::HandleKey(SDLKey key)
696 if (key == SDLK_DOWN)
698 if (cursor != limit - 1) // Cursor is within its window
700 else // Otherwise, scroll the window...
702 if (cursor + windowPtr != item.size() - 1)
706 else if (key == SDLK_UP)
716 else if (key == SDLK_PAGEDOWN)
718 if (cursor != limit - 1)
723 if (windowPtr > item.size() - limit)
724 windowPtr = item.size() - limit;
727 else if (key == SDLK_PAGEUP)
733 if (windowPtr < limit)
739 else if (key >= SDLK_a && key <= SDLK_z)
741 // Advance cursor to filename with first letter pressed...
742 uint8 which = (key - SDLK_a) + 65; // Convert key to A-Z char
744 for(uint32 i=0; i<item.size(); i++)
746 if ((item[i][0] & 0xDF) == which)
748 cursor = i - windowPtr;
749 if (i > windowPtr + limit - 1)
750 windowPtr = i - limit + 1, cursor = limit - 1;
752 windowPtr = i, cursor = 0;
759 void ListBox::HandleMouseMove(uint32 x, uint32 y)
761 upArrow.HandleMouseMove(x - extents.x, y - extents.y);
762 downArrow.HandleMouseMove(x - extents.x, y - extents.y);
763 upArrow2.HandleMouseMove(x - extents.x, y - extents.y);
767 uint32 sbHeight = extents.h - 24,
768 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight);
770 //yRelativePoint is the spot on the thumb where we clicked...
771 int32 newThumbStart = y - yRelativePoint;
773 if (newThumbStart < 0)
776 if ((uint32)newThumbStart > sbHeight - thumb)
777 newThumbStart = sbHeight - thumb;
779 windowPtr = (uint32)(((float)newThumbStart / (float)sbHeight) * (float)item.size());
780 //Check for cursor bounds as well... Or do we need to???
781 //Actually, we don't...!
785 void ListBox::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
787 if (Inside(x, y) && mouseDown)
789 // Why do we have to do this??? (- extents.y?)
790 // I guess it's because only the Window class has offsetting implemented... !!! FIX !!!
791 // cursor = (y - extents.y) / 8;
792 cursor = (y - extents.y) / FONT_HEIGHT;
795 // Check for a hit on the scrollbar...
796 if (x > (uint32)(extents.x + extents.w) && x <= (uint32)(extents.x + extents.w + 8)
797 && y > (uint32)(extents.y + 8) && y <= (uint32)(extents.y + extents.h - 16))
801 // This shiaut should be calculated in AddItem(), not here... (or in Draw() for that matter)
802 uint32 sbHeight = extents.h - 24,
803 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
804 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
806 // Did we hit the thumb?
807 if (y >= (extents.y + 8 + thumbStart) && y < (extents.y + 8 + thumbStart + thumb))
808 thumbClicked = true, yRelativePoint = y - thumbStart;
810 //Seems that this is useless--never reached except in rare cases and that the code outside is
813 // thumbClicked = false;
817 thumbClicked = false;
819 upArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
820 downArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
821 upArrow2.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
824 void ListBox::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
826 for(uint32 i=0; i<limit; i++)
828 // Strip off the extension
829 // (extension stripping should be an option, not default!)
830 string s(item[windowPtr + i], 0, item[windowPtr + i].length() - 4);
831 // DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*8,
832 DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*FONT_HEIGHT,
833 (cursor == i ? true : false), "%-*.*s", charWidth, charWidth, s.c_str());
836 upArrow.Draw(extents.x + offsetX, extents.y + offsetY);
837 downArrow.Draw(extents.x + offsetX, extents.y + offsetY);
838 upArrow2.Draw(extents.x + offsetX, extents.y + offsetY);
840 uint32 sbHeight = extents.h - 24,
841 thumb = (uint32)(((float)limit / (float)item.size()) * (float)sbHeight),
842 thumbStart = (uint32)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
844 for(uint32 y=extents.y+offsetY+8; y<extents.y+offsetY+extents.h-16; y++)
846 // for(uint32 x=extents.x+offsetX+extents.w-8; x<extents.x+offsetX+extents.w; x++)
847 for(uint32 x=extents.x+offsetX+extents.w; x<extents.x+offsetX+extents.w+8; x++)
849 if (y >= thumbStart + (extents.y+offsetY+8) && y < thumbStart + thumb + (extents.y+offsetY+8))
850 // screenBuffer[x + (y * pitch)] = (thumbClicked ? 0x458E : 0xFFFF);
851 //458E -> 01 0001 0 1100 0 1110 -> 0100 0101 0110 0011 0111 0011 -> 45 63 73
852 screenBuffer[x + (y * pitch)] = (thumbClicked ? 0xFF736345 : 0xFFFFFFFF);
854 // screenBuffer[x + (y * pitch)] = 0x0200;
855 //0200 -> 000000 10000 00000 -> 00 1000 0100 00
856 screenBuffer[x + (y * pitch)] = 0xFF008400;
861 void ListBox::Notify(Element * e)
863 if (e == &upArrow || e == &upArrow2)
869 if (cursor < limit - 1)
873 else if (e == &downArrow)
875 if (windowPtr < item.size() - limit)
885 void ListBox::AddItem(string s)
887 // Do a simple insertion sort
888 bool inserted = false;
890 for(vector<string>::iterator i=item.begin(); i<item.end(); i++)
892 if (stringCmpi(s, *i) == -1)
903 limit = (item.size() > charHeight ? charHeight : item.size());
906 string ListBox::GetSelectedItem(void)
908 return item[windowPtr + cursor];
916 class FileList: public Window
919 FileList(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = 0);
920 virtual ~FileList() {}
921 virtual void HandleKey(SDLKey key);
922 virtual void HandleMouseMove(uint32 x, uint32 y) { Window::HandleMouseMove(x, y); }
923 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) { Window::HandleMouseButton(x, y, mouseDown); }
924 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) { Window::Draw(offsetX, offsetY); }
925 virtual void Notify(Element * e);
932 //Need 4 buttons, one scrollbar...
933 FileList::FileList(uint32 x, uint32 y, uint32 w, uint32 h): Window(x, y, w, h)
935 files = new ListBox(8, 8, w - 16, h - 32);
937 load = new Button(8, h - 16, " Load ");
939 load->SetNotificationElement(this);
941 //!!! FIX !!! Directory might not exist--this shouldn't cause VJ to crash!
942 DIR * dp = opendir(vjs.ROMPath);
947 while ((de = readdir(dp)) != NULL)
949 char * ext = strrchr(de->d_name, '.');
952 if (strcasecmp(ext, ".zip") == 0 || strcasecmp(ext, ".j64") == 0
953 || strcasecmp(ext, ".abs") == 0 || strcasecmp(ext, ".jag") == 0
954 || strcasecmp(ext, ".rom") == 0)
955 files->AddItem(string(de->d_name));
962 //Give a diagnostic message here so that the (l)user can figure out what went wrong. !!! FIX !!!
966 void FileList::HandleKey(SDLKey key)
968 if (key == SDLK_RETURN)
971 Window::HandleKey(key);
974 void FileList::Notify(Element * e)
978 char filename[MAX_PATH];
979 strcpy(filename, vjs.ROMPath);
981 if (strlen(filename) > 0)
982 if (filename[strlen(filename) - 1] != '/')
983 strcat(filename, "/");
985 strcat(filename, files->GetSelectedItem().c_str());
987 // uint32 romSize = JaguarLoadROM(jaguar_mainRom, filename);
988 // JaguarLoadCart(jaguar_mainRom, filename);
989 if (JaguarLoadFile(filename))
992 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
993 SDL_PushEvent(&event);
995 event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
996 event.user.data1 = (void *)ResetJaguar;
997 SDL_PushEvent(&event);
1002 event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
1003 SDL_PushEvent(&event);
1005 // Handle the error, but don't run...
1006 // Tell the user that we couldn't run their file for some reason... !!! FIX !!!
1007 //how to kludge: Make a function like ResetJaguar which creates the dialog window
1016 // Menu class & supporting structs/classes
1022 Window * (* action)(void);
1025 NameAction(string n, Window * (* a)(void) = NULL, SDLKey k = SDLK_UNKNOWN): name(n),
1026 action(a), hotKey(k) {}
1032 MenuItems(): charLength(0) {}
1033 bool Inside(uint32 x, uint32 y)
1034 { return (x >= (uint32)extents.x && x < (uint32)(extents.x + extents.w)
1035 && y >= (uint32)extents.y && y < (uint32)(extents.y + extents.h) ? true : false); }
1038 vector<NameAction> item;
1043 class Menu: public Element
1046 // 1CFF -> 0 001 11 00 111 1 1111
1047 // 421F -> 0 100 00 10 000 1 1111
1048 Menu(uint32 x = 0, uint32 y = 0, uint32 w = 0, uint32 h = FONT_HEIGHT,
1049 /* uint16 fgc = 0x1CFF, uint16 bgc = 0x000F, uint16 fgch = 0x421F,
1050 uint16 bgch = 0x1CFF): Element(x, y, w, h), activated(false), clicked(false),*/
1051 /* uint32 fgc = 0xFF3F3F00, uint32 bgc = 0x7F000000, uint32 fgch = 0xFF878700,
1052 uint32 bgch = 0xFF3F3F00): Element(x, y, w, h), activated(false), clicked(false),*/
1053 /* uint32 fgc = 0xFFFF3F3F, uint32 bgc = 0xFF7F0000, uint32 fgch = 0xFFFF8787,
1054 uint32 bgch = 0xFFFF3F3F): Element(x, y, w, h), activated(false), clicked(false),*/
1055 uint32 fgc = 0xFF7F0000, uint32 bgc = 0xFFFF3F3F, uint32 fgch = 0xFFFF3F3F,
1056 uint32 bgch = 0xFFFF8787): Element(x, y, w, h), activated(false), clicked(false),
1057 inside(0), insidePopup(0), fgColor(fgc), bgColor(bgc), fgColorHL(fgch),
1058 bgColorHL(bgch), menuChosen(-1), menuItemChosen(-1) {}
1059 virtual void HandleKey(SDLKey key);
1060 virtual void HandleMouseMove(uint32 x, uint32 y);
1061 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown);
1062 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0);
1063 virtual void Notify(Element *) {}
1064 void Add(MenuItems mi);
1067 bool activated, clicked;
1068 uint32 inside, insidePopup;
1069 // uint16 fgColor, bgColor, fgColorHL, bgColorHL;
1070 uint32 fgColor, bgColor, fgColorHL, bgColorHL;
1071 int menuChosen, menuItemChosen;
1074 vector<MenuItems> itemList;
1077 void Menu::HandleKey(SDLKey key)
1079 for(uint32 i=0; i<itemList.size(); i++)
1081 for(uint32 j=0; j<itemList[i].item.size(); j++)
1083 if (itemList[i].item[j].hotKey == key)
1086 event.type = SDL_USEREVENT;
1087 event.user.code = MENU_ITEM_CHOSEN;
1088 event.user.data1 = (void *)itemList[i].item[j].action;
1089 SDL_PushEvent(&event);
1091 clicked = false, menuChosen = menuItemChosen = -1;
1098 void Menu::HandleMouseMove(uint32 x, uint32 y)
1100 inside = insidePopup = 0;
1104 // Find out *where* we are inside the menu bar
1105 uint32 xpos = extents.x;
1107 for(uint32 i=0; i<itemList.size(); i++)
1109 uint32 width = (itemList[i].title.length() + 2) * FONT_WIDTH;
1111 if (x >= xpos && x < xpos + width)
1122 if (!Inside(x, y) && !clicked)
1127 if (itemList[menuChosen].Inside(x, y) && clicked)
1129 insidePopup = ((y - itemList[menuChosen].extents.y) / FONT_HEIGHT) + 1;
1130 menuItemChosen = insidePopup - 1;
1134 void Menu::HandleMouseButton(uint32 x, uint32 y, bool mouseDown)
1143 menuChosen = -1; // clicked is already false...!
1146 else // clicked == true
1148 if (insidePopup && !mouseDown) // I.e., mouse-button-up
1151 if (itemList[menuChosen].item[menuItemChosen].action != NULL)
1153 // itemList[menuChosen].item[menuItemChosen].action();
1155 event.type = SDL_USEREVENT;
1156 event.user.code = MENU_ITEM_CHOSEN;
1157 event.user.data1 = (void *)itemList[menuChosen].item[menuItemChosen].action;
1158 SDL_PushEvent(&event);
1160 clicked = false, menuChosen = menuItemChosen = -1;
1163 while (SDL_PollEvent(&event)); // Flush the event queue...
1164 event.type = SDL_MOUSEMOTION;
1166 SDL_GetMouseState(&mx, &my);
1167 event.motion.x = mx, event.motion.y = my;
1168 SDL_PushEvent(&event); // & update mouse position...!
1172 if (!inside && !insidePopup && mouseDown)
1173 clicked = false, menuChosen = menuItemChosen = -1;
1177 void Menu::Draw(uint32 offsetX/*= 0*/, uint32 offsetY/*= 0*/)
1179 uint32 xpos = extents.x + offsetX;
1181 for(uint32 i=0; i<itemList.size(); i++)
1183 // uint16 color1 = fgColor, color2 = bgColor;
1184 uint32 color1 = fgColor, color2 = bgColor;
1185 if (inside == (i + 1) || (menuChosen != -1 && (uint32)menuChosen == i))
1186 color1 = fgColorHL, color2 = bgColorHL;
1188 DrawStringOpaque(screenBuffer, xpos, extents.y + offsetY, color1, color2,
1189 " %s ", itemList[i].title.c_str());
1190 xpos += (itemList[i].title.length() + 2) * FONT_WIDTH;
1193 // Draw sub menu (but only if active)
1196 uint32 ypos = extents.y + FONT_HEIGHT + 1;
1198 for(uint32 i=0; i<itemList[menuChosen].item.size(); i++)
1200 // uint16 color1 = fgColor, color2 = bgColor;
1201 uint32 color1 = fgColor, color2 = bgColor;
1203 if (insidePopup == i + 1)
1204 color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
1206 if (itemList[menuChosen].item[i].name.length() > 0)
1207 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
1208 color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
1209 itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
1211 DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
1212 fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
1214 ypos += FONT_HEIGHT;
1219 void Menu::Add(MenuItems mi)
1221 for(uint32 i=0; i<mi.item.size(); i++)
1222 if (mi.item[i].name.length() > mi.charLength)
1223 mi.charLength = mi.item[i].name.length();
1225 // Set extents here as well...
1226 mi.extents.x = extents.x + extents.w, mi.extents.y = extents.y + FONT_HEIGHT + 1;
1227 mi.extents.w = (mi.charLength + 2) * FONT_WIDTH, mi.extents.h = mi.item.size() * FONT_HEIGHT;
1229 itemList.push_back(mi);
1230 extents.w += (mi.title.length() + 2) * FONT_WIDTH;
1234 //Do we even *need* this?
1235 //Doesn't seem like it...
1236 /*class RootWindow: public Window
1239 RootWindow(Menu * m, Window * w = NULL): menu(m), window(w) {}
1240 //Do we even need to care about this crap?
1241 // { extents.x = extents.y = 0, extents.w = 320, extents.h = 240; }
1242 virtual void HandleKey(SDLKey key) {}
1243 virtual void HandleMouseMove(uint32 x, uint32 y) {}
1244 virtual void HandleMouseButton(uint32 x, uint32 y, bool mouseDown) {}
1245 virtual void Draw(uint32 offsetX = 0, uint32 offsetY = 0) {}
1246 virtual void Notify(Element *) {}
1251 int16 * rootImage[1280 * 240 * 2];
1256 // Draw text at the given x/y coordinates. Can invert text as well.
1258 void DrawString(uint32 * screen, uint32 x, uint32 y, bool invert, const char * text, ...)
1263 va_start(arg, text);
1264 vsprintf(string, text, arg);
1267 uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1268 uint32 length = strlen(string), address = x + (y * pitch);
1270 uint32 color1 = 0x0080FF;
1271 uint8 nBlue = (color1 >> 16) & 0xFF, nGreen = (color1 >> 8) & 0xFF, nRed = color1 & 0xFF;
1272 uint8 xorMask = (invert ? 0xFF : 0x00);
1274 for(uint32 i=0; i<length; i++)
1276 uint8 c = string[i];
1277 uint32 fontAddr = (uint32)(c < 32 ? 0 : c - 32) * FONT_WIDTH * FONT_HEIGHT;
1279 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
1281 for(uint32 xx=0; xx<FONT_WIDTH; xx++)
1283 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1285 uint8 eBlue = (existingColor >> 16) & 0xFF,
1286 eGreen = (existingColor >> 8) & 0xFF,
1287 eRed = existingColor & 0xFF;
1289 uint8 trans = font2[fontAddr] ^ xorMask;
1290 uint8 invTrans = trans ^ 0xFF;
1292 uint32 bRed = (eRed * invTrans + nRed * trans) / 255,
1293 bGreen = (eGreen * invTrans + nGreen * trans) / 255,
1294 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1296 *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
1301 address += FONT_WIDTH;
1306 // Draw text at the given x/y coordinates, using FG/BG colors.
1308 void DrawStringOpaque(uint32 * screen, uint32 x, uint32 y, uint32 color1, uint32 color2, const char * text, ...)
1313 va_start(arg, text);
1314 vsprintf(string, text, arg);
1317 uint32 pitch = sdlemuGetOverlayWidthInPixels();
1318 uint32 length = strlen(string), address = x + (y * pitch);
1320 uint8 eBlue = (color2 >> 16) & 0xFF, eGreen = (color2 >> 8) & 0xFF, eRed = color2 & 0xFF,
1321 nBlue = (color1 >> 16) & 0xFF, nGreen = (color1 >> 8) & 0xFF, nRed = color1 & 0xFF;
1323 for(uint32 i=0; i<length; i++)
1325 uint8 c = string[i];
1326 c = (c < 32 ? 0 : c - 32);
1327 uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;
1329 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
1331 for(uint32 xx=0; xx<FONT_WIDTH; xx++)
1333 uint8 trans = font2[fontAddr++];
1334 uint8 invTrans = trans ^ 0xFF;
1336 uint32 bRed = (eRed * invTrans + nRed * trans) / 255;
1337 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
1338 uint32 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1340 *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
1344 address += FONT_WIDTH;
1349 // Draw text at the given x/y coordinates with transparency (0 is fully opaque, 32 is fully transparent).
1351 void DrawStringTrans(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 trans, const char * text, ...)
1356 va_start(arg, text);
1357 vsprintf(string, text, arg);
1360 uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1361 uint32 length = strlen(string), address = x + (y * pitch);
1363 for(uint32 i=0; i<length; i++)
1365 uint32 fontAddr = (uint32)string[i] * 64;
1367 for(uint32 yy=0; yy<8; yy++)
1369 for(uint32 xx=0; xx<8; xx++)
1371 if (font1[fontAddr])
1373 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1375 uint8 eBlue = (existingColor >> 16) & 0xFF,
1376 eGreen = (existingColor >> 8) & 0xFF,
1377 eRed = existingColor & 0xFF,
1378 //This could be done ahead of time, instead of on each pixel...
1379 nBlue = (color >> 16) & 0xFF,
1380 nGreen = (color >> 8) & 0xFF,
1381 nRed = color & 0xFF;
1383 //This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
1384 //Here we've modified it to have 33 levels of transparency (could have any # we want!)
1385 //because dividing by 32 is faster than dividing by 31...!
1386 uint8 invTrans = 32 - trans;
1388 uint32 bRed = (eRed * trans + nRed * invTrans) / 32;
1389 uint32 bGreen = (eGreen * trans + nGreen * invTrans) / 32;
1390 uint32 bBlue = (eBlue * trans + nBlue * invTrans) / 32;
1392 *(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
1404 // Draw text at the given x/y coordinates, using FG color and overlay alpha blending.
1406 void DrawString2(uint32 * screen, uint32 x, uint32 y, uint32 color, uint8 transparency, const char * text, ...)
1411 va_start(arg, text);
1412 vsprintf(string, text, arg);
1415 uint32 pitch = sdlemuGetOverlayWidthInPixels();
1416 uint32 length = strlen(string), address = x + (y * pitch);
1418 color &= 0x00FFFFFF; // Just in case alpha was passed in...
1420 for(uint32 i=0; i<length; i++)
1422 uint8 c = string[i];
1423 c = (c < 32 ? 0 : c - 32);
1424 uint32 fontAddr = (uint32)c * FONT_WIDTH * FONT_HEIGHT;
1426 for(uint32 yy=0; yy<FONT_HEIGHT; yy++)
1428 for(uint32 xx=0; xx<FONT_WIDTH; xx++)
1430 uint8 fontTrans = font2[fontAddr++];
1431 uint32 newTrans = (fontTrans * transparency / 255) << 24;
1432 uint32 pixel = newTrans | color;
1434 *(screen + address + xx + (yy * pitch)) = pixel;
1438 address += FONT_WIDTH;
1444 // Uses zero as transparent color
1445 // Can also use an optional alpha channel
1446 // Alpha channel is now mandatory! ;-)
1448 //void DrawTransparentBitmap(int16 * screen, uint32 x, uint32 y, uint16 * bitmap, uint8 * alpha/*=NULL*/)
1449 /*void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap, uint8 * alpha)
1451 uint32 width = bitmap[0], height = bitmap[1];
1454 // uint32 pitch = GetSDLScreenPitch() / 2; // Returns pitch in bytes but we need words...
1455 uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1456 uint32 address = x + (y * pitch);
1458 for(uint32 yy=0; yy<height; yy++)
1460 for(uint32 xx=0; xx<width; xx++)
1464 if (*bitmap && x + xx < pitch) // NOTE: Still doesn't clip the Y val...
1465 *(screen + address + xx + (yy * pitch)) = *bitmap;
1469 uint8 trans = *alpha;
1470 uint32 color = *bitmap;
1471 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1473 uint8 eRed = existingColor & 0xFF,
1474 eGreen = (existingColor >> 8) & 0xFF,
1475 eBlue = (existingColor >> 16) & 0xFF,
1477 nRed = color & 0xFF,
1478 nGreen = (color >> 8) & 0xFF,
1479 nBlue = (color >> 16) & 0xFF;
1481 uint8 invTrans = 255 - trans;
1482 uint32 bRed = (eRed * trans + nRed * invTrans) / 255;
1483 uint32 bGreen = (eGreen * trans + nGreen * invTrans) / 255;
1484 uint32 bBlue = (eBlue * trans + nBlue * invTrans) / 255;
1486 uint32 blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
1488 *(screen + address + xx + (yy * pitch)) = blendedColor;
1497 void DrawTransparentBitmapDeprecated(uint32 * screen, uint32 x, uint32 y, uint32 * bitmap)
1499 uint32 width = bitmap[0], height = bitmap[1];
1502 uint32 pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
1503 uint32 address = x + (y * pitch);
1505 for(uint32 yy=0; yy<height; yy++)
1507 for(uint32 xx=0; xx<width; xx++)
1509 uint32 color = *bitmap;
1510 uint32 blendedColor = color;
1511 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1513 if (existingColor >> 24 != 0x00) // Pixel needs blending
1515 uint8 trans = color >> 24;
1516 uint8 invTrans = trans ^ 0xFF;//255 - trans;
1518 uint8 eRed = existingColor & 0xFF,
1519 eGreen = (existingColor >> 8) & 0xFF,
1520 eBlue = (existingColor >> 16) & 0xFF,
1522 nRed = color & 0xFF,
1523 nGreen = (color >> 8) & 0xFF,
1524 nBlue = (color >> 16) & 0xFF;
1526 uint32 bRed = (eRed * invTrans + nRed * trans) / 255;
1527 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
1528 uint32 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1530 blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
1533 *(screen + address + xx + (yy * pitch)) = blendedColor;
1539 void DrawTransparentBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap)
1541 uint32 pitch = sdlemuGetOverlayWidthInPixels();
1542 uint32 address = x + (y * pitch);
1545 for(uint32 yy=0; yy<((Bitmap *)bitmap)->height; yy++)
1547 for(uint32 xx=0; xx<((Bitmap *)bitmap)->width; xx++)
1549 uint32 color = ((uint32 *)((Bitmap *)bitmap)->pixelData)[count];
1550 uint32 blendedColor = color;
1551 uint32 existingColor = *(screen + address + xx + (yy * pitch));
1553 if (existingColor >> 24 != 0x00) // Pixel needs blending
1555 uint8 trans = color >> 24;
1556 uint8 invTrans = trans ^ 0xFF;
1558 uint8 eRed = existingColor & 0xFF,
1559 eGreen = (existingColor >> 8) & 0xFF,
1560 eBlue = (existingColor >> 16) & 0xFF,
1562 nRed = color & 0xFF,
1563 nGreen = (color >> 8) & 0xFF,
1564 nBlue = (color >> 16) & 0xFF;
1566 uint32 bRed = (eRed * invTrans + nRed * trans) / 255;
1567 uint32 bGreen = (eGreen * invTrans + nGreen * trans) / 255;
1568 uint32 bBlue = (eBlue * invTrans + nBlue * trans) / 255;
1570 // Instead of $FF, should use the alpha from the destination pixel as the final alpha value...
1571 blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
1574 *(screen + address + xx + (yy * pitch)) = blendedColor;
1581 // Draw a bitmap without using blending
1583 void DrawBitmap(uint32 * screen, uint32 x, uint32 y, const void * bitmap)
1585 uint32 pitch = sdlemuGetOverlayWidthInPixels();
1586 uint32 address = x + (y * pitch);
1589 for(uint32 yy=0; yy<((Bitmap *)bitmap)->height; yy++)
1591 for(uint32 xx=0; xx<((Bitmap *)bitmap)->width; xx++)
1593 *(screen + address + xx + (yy * pitch)) = ((uint32 *)((Bitmap *)bitmap)->pixelData)[count];
1600 // Fill a portion of the screen with the passed in color
1602 void FillScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h, uint32 color)
1603 //void ClearScreenRectangle(uint32 * screen, uint32 x, uint32 y, uint32 w, uint32 h)
1605 uint32 pitch = sdlemuGetOverlayWidthInPixels();
1606 uint32 address = x + (y * pitch);
1608 for(uint32 yy=0; yy<h; yy++)
1609 for(uint32 xx=0; xx<w; xx++)
1610 *(screen + address + xx + (yy * pitch)) = color;
1615 // GUI stuff--it's not crunchy, it's GUI! ;-)
1620 SDL_ShowCursor(SDL_DISABLE);
1621 SDL_GetMouseState(&mouseX, &mouseY);
1631 //bool GUIMain(void)
1632 bool GUIMain(char * filename)
1634 WriteLog("GUI: Inside GUIMain...\n");
1636 uint32 pointerBGSave[6 * 8 + 2];
1637 pointerBGSave[0] = 6;
1638 pointerBGSave[1] = 8;
1640 // Need to set things up so that it loads and runs a file if given on the command line. !!! FIX !!! [DONE]
1641 extern uint32 * backbuffer;
1642 // bool done = false;
1644 Window * mainWindow = NULL;
1646 // Set up the GUI classes...
1647 // Element::SetScreenAndPitch(backbuffer, GetSDLScreenWidthInPixels());
1648 Element::SetScreenAndPitch((uint32 *)sdlemuGetOverlayPixels(), sdlemuGetOverlayWidthInPixels());
1649 sdlemuEnableOverlay();
1653 mi.title = "Jaguar";
1654 mi.item.push_back(NameAction("Load...", LoadROM, SDLK_l));
1655 mi.item.push_back(NameAction("Reset", ResetJaguar, SDLK_r));
1657 mi.item.push_back(NameAction("Reset CD", ResetJaguarCD, SDLK_c));
1658 mi.item.push_back(NameAction("Run", RunEmu, SDLK_ESCAPE));
1659 mi.item.push_back(NameAction(""));
1660 mi.item.push_back(NameAction("Quit", Quit, SDLK_q));
1662 mi.title = "Settings";
1664 mi.item.push_back(NameAction("Video..."));
1665 mi.item.push_back(NameAction("Audio..."));
1666 mi.item.push_back(NameAction("Misc...", MiscOptions, SDLK_m));
1670 mi.item.push_back(NameAction("About...", About));
1673 bool showMouse = true;
1675 // Grab the BG where the mouse will be painted (prime the backstore)
1679 Bitmap ptr = { 6, 8, 4,
1680 ""//"000011112222333344445555"
1681 //"000011112222333344445555"
1682 //"000011112222333344445555"
1683 //"000011112222333344445555"
1684 //"000011112222333344445555"
1685 //"000011112222333344445555"
1686 //"000011112222333344445555"
1687 //"000011112222333344445555"
1689 uint32 * overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
1692 for(uint32 y=0; y<pointerBGSave[1]; y++)
1693 for(uint32 x=0; x<pointerBGSave[0]; x++)
1694 pointerBGSave[count++] = overlayPixels[((mouseY + y) * sdlemuGetOverlayWidthInPixels()) + (mouseX + x)];
1696 uint32 oldMouseX = mouseX, oldMouseY = mouseY;
1698 //This is crappy!!! !!! FIX !!!
1699 //Is this even needed any more? Hmm. Maybe. Dunno.
1700 WriteLog("GUI: Resetting Jaguar...\n");
1703 WriteLog("GUI: Clearing BG save...\n");
1704 // Set up our background save...
1705 // memset(background, 0x11, tom_getVideoModeWidth() * 240 * 2);
1706 //1111 -> 000100 01000 10001 -> 0001 0000 0100 0010 1000 1100 -> 10 42 8C
1707 for(uint32 i=0; i<tom_getVideoModeWidth()*240; i++)
1708 // background[i] = 0xFF8C4210;
1709 backbuffer[i] = 0xFF8C4210;
1711 /* uint32 * overlayPix = (uint32 *)sdlemuGetOverlayPixels();
1712 for(uint32 i=0; i<sdlemuGetOverlayWidthInPixels()*480; i++)
1713 overlayPix[i] = 0x00000000;*/
1715 // Handle loading file passed in on the command line...! [DONE]
1719 if (JaguarLoadFile(filename))
1721 // event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
1722 // event.user.data1 = (void *)ResetJaguar;
1723 // SDL_PushEvent(&event);
1724 // Make it so that if passed in on the command line, we quit right
1725 // away when pressing ESC
1726 WriteLog("GUI: Bypassing GUI since ROM passed in on command line...\n");
1732 // Create error dialog...
1734 sprintf(errText, "The file %40s could not be loaded.", filename);
1736 mainWindow = new Window(8, 16, 304, 160);
1737 mainWindow->AddElement(new Text(8, 8, "Error!"));
1738 mainWindow->AddElement(new Text(8, 24, errText));
1742 WriteLog("GUI: Entering main loop...\n");
1745 if (SDL_PollEvent(&event))
1747 if (event.type == SDL_USEREVENT)
1749 if (event.user.code == WINDOW_CLOSE)
1754 else if (event.user.code == MENU_ITEM_CHOSEN)
1756 // Confused? Let me enlighten... What we're doing here is casting
1757 // data1 as a pointer to a function which returns a Window pointer and
1758 // which takes no parameters (the "(Window *(*)(void))" part), then
1759 // derefencing it (the "*" in front of that) in order to call the
1760 // function that it points to. Clear as mud? Yeah, I hate function
1761 // pointers too, but what else are you gonna do?
1762 mainWindow = (*(Window *(*)(void))event.user.data1)();
1764 while (SDL_PollEvent(&event)); // Flush the event queue...
1765 event.type = SDL_MOUSEMOTION;
1767 SDL_GetMouseState(&mx, &my);
1768 event.motion.x = mx, event.motion.y = my;
1769 SDL_PushEvent(&event); // & update mouse position...!
1771 oldMouseX = mouseX, oldMouseY = mouseY;
1772 mouseX = mx, mouseY = my; // This prevents "mouse flash"...
1775 else if (event.type == SDL_ACTIVEEVENT)
1777 if (event.active.state == SDL_APPMOUSEFOCUS)
1778 showMouse = (event.active.gain ? true : false);
1780 else if (event.type == SDL_KEYDOWN)
1783 mainWindow->HandleKey(event.key.keysym.sym);
1785 mainMenu.HandleKey(event.key.keysym.sym);
1787 else if (event.type == SDL_MOUSEMOTION)
1789 oldMouseX = mouseX, oldMouseY = mouseY;
1790 mouseX = event.motion.x, mouseY = event.motion.y;
1793 mainWindow->HandleMouseMove(mouseX, mouseY);
1795 mainMenu.HandleMouseMove(mouseX, mouseY);
1797 else if (event.type == SDL_MOUSEBUTTONDOWN)
1799 uint32 mx = event.button.x, my = event.button.y;
1802 mainWindow->HandleMouseButton(mx, my, true);
1804 mainMenu.HandleMouseButton(mx, my, true);
1806 else if (event.type == SDL_MOUSEBUTTONUP)
1808 uint32 mx = event.button.x, my = event.button.y;
1811 mainWindow->HandleMouseButton(mx, my, false);
1813 mainMenu.HandleMouseButton(mx, my, false);
1816 //PROBLEM: In order to use the dirty rectangle approach here, we need some way of
1817 // handling it in mainMenu.Draw() and mainWindow->Draw(). !!! FIX !!!
1818 //POSSIBLE SOLUTION:
1819 // When mouse is moving and not on menu or window, can do straight dirty rect.
1820 // When mouse is on menu, need to update screen. Same for buttons on windows...
1821 // What the menu & windows should do is only redraw on a state change. IOW, they
1822 // should call their own/child window's Draw() function instead of doing it top
1824 //#define NEW_BACKSTORE_METHOD
1827 // The way we do things here is kinda stupid (redrawing the screen every frame), but
1828 // it's simple. Perhaps there may be a reason down the road to be more selective with
1829 // our clearing, but for now, this will suffice.
1830 // memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
1831 // memcpy(backbuffer, background, tom_getVideoModeWidth() * 256 * 2);
1832 // memcpy(backbuffer, background, tom_getVideoModeWidth() * 256 * 4);
1833 #ifndef NEW_BACKSTORE_METHOD
1834 memset(sdlemuGetOverlayPixels(), 0, sdlemuGetOverlayWidthInPixels() * 480 * 4);
1837 //Could do multiple windows here by using a vector + priority info...
1838 //Though the way ZSNES does it seems to be by a bool (i.e., they're always active, just not shown)
1843 /*uint32 pBGS[6 * 8 + 3] = { 6, 8, 4,
1853 //This isn't working... Why????
1854 //It's because DrawTransparentBitmap does alpha blending if it detects zero in the alpha channel.
1855 //So why do it that way? Hm.
1856 overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
1858 #ifdef NEW_BACKSTORE_METHOD
1859 // DrawTransparentBitmapDeprecated(overlayPixels, oldMouseX, oldMouseY, pointerBGSave);
1860 // DrawTransparentBitmap(overlayPixels, oldMouseX, oldMouseY, pBGS);
1861 for(uint32 y=0; y<pointerBGSave[1]; y++)
1862 for(uint32 x=0; x<pointerBGSave[0]; x++)
1863 overlayPixels[((oldMouseY + y) * sdlemuGetOverlayWidthInPixels()) + (oldMouseX + x)] = 0x00000000;
1867 for(uint32 y=0; y<pointerBGSave[1]; y++)
1868 for(uint32 x=0; x<pointerBGSave[0]; x++)
1869 pointerBGSave[count++] = overlayPixels[((mouseY + y) * sdlemuGetOverlayWidthInPixels()) + (mouseX + x)];
1873 // DrawTransparentBitmapDeprecated(backbuffer, mouseX, mouseY, mousePic);
1874 DrawTransparentBitmapDeprecated(overlayPixels, mouseX, mouseY, mousePic);
1884 // GUI "action" functions
1887 Window * LoadROM(void)
1889 FileList * fileList = new FileList(20, 20, 600, 440);
1891 return (Window *)fileList;
1894 Window * ResetJaguar(void)
1901 Window * ResetJaguarCD(void)
1903 memcpy(jaguar_mainRom, jaguar_CDBootROM, 0x40000);
1904 jaguarRunAddress = 0x802000;
1905 jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, 0x40000);
1907 //This is a quick kludge to get the CDBIOS to boot properly...
1908 //Wild speculation: It could be that this memory location is wired into the CD unit
1909 //somehow, which lets it know whether or not a cart is present in the unit...
1910 jaguar_mainRom[0x0040B] = 0x03;
1918 bool debounceRunKey = true;
1919 Window * RunEmu(void)
1921 extern uint32 * backbuffer;
1922 //Temporary, to test the new timer based code...
1923 sdlemuDisableOverlay();
1925 sdlemuEnableOverlay();
1926 // Save the background for the GUI...
1927 // In this case, we squash the color to monochrome, then force it to blue + green...
1928 for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
1930 uint32 pixel = backbuffer[i];
1931 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
1932 pixel = ((r + g + b) / 3) & 0x00FF;
1933 backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
1937 //This is crappy... !!! FIX !!!
1938 extern bool finished, showGUI;
1940 // uint32 nFrame = 0, nFrameskip = 0;
1941 uint32 totalFrames = 0;
1943 bool showMessage = true;
1944 uint32 showMsgFrames = 120;
1945 uint8 transparency = 0;
1946 // Pass a message to the "joystick" code to debounce the ESC key...
1947 debounceRunKey = true;
1949 uint32 cartType = 4;
1950 if (jaguarRomSize == 0x200000)
1952 else if (jaguarRomSize == 0x400000)
1954 else if (jaguar_mainRom_crc32 == 0x687068D5)
1956 else if (jaguar_mainRom_crc32 == 0x55A0669C)
1959 char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
1960 uint32 elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
1964 // Set up new backbuffer with new pixels and data
1965 JaguarExecute(backbuffer, true);
1966 // JaguarExecuteNew();
1968 //WriteLog("Frame #%u...\n", totalFrames);
1969 //extern bool doDSPDis;
1970 //if (totalFrames == 373)
1973 //This sucks... !!! FIX !!!
1975 //This is done here so that the crud below doesn't get on our GUI background...
1979 // Some QnD GUI stuff here...
1982 extern uint32 gpu_pc, dsp_pc;
1983 DrawString(backbuffer, 8, 8, false, "GPU PC: %08X", gpu_pc);
1984 DrawString(backbuffer, 8, 16, false, "DSP PC: %08X", dsp_pc);
1985 DrawString(backbuffer, 8, 32, false, "%u FPS", framesPerSecond);
1990 // FF0F -> 1111 11 11 000 0 1111 -> 3F 18 0F
1991 // 3FE3 -> 0011 11 11 111 0 0011 -> 0F 3F 03
1992 /* DrawStringTrans((uint32 *)backbuffer, 8, 24*8, 0xFF0F, transparency, "Running...");
1993 DrawStringTrans((uint32 *)backbuffer, 8, 26*8, 0x3FE3, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
1994 DrawStringTrans((uint32 *)backbuffer, 8, 27*8, 0x3FE3, transparency, "CRC: %08X", jaguar_mainRom_crc32);//*/
1995 //first has wrong color. !!! FIX !!!
1996 DrawStringTrans(backbuffer, 8, 24*8, 0xFF7F63FF, transparency, "Running...");
1997 DrawStringTrans(backbuffer, 8, 26*8, 0xFF1FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
1998 DrawStringTrans(backbuffer, 8, 27*8, 0xFF1FFF3F, transparency, "CRC: %08X", jaguar_mainRom_crc32);
2000 if (showMsgFrames == 0)
2004 if (transparency == 33)
2006 showMessage = false;
2007 /*extern bool doGPUDis;
2008 doGPUDis = true;//*/
2019 if (SDL_GetTicks() - elapsedTicks > 250)
2020 elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
2023 // Reset the pitch, since it may have been changed in-game...
2024 Element::SetScreenAndPitch((uint32 *)backbuffer, GetSDLScreenWidthInPixels());
2026 // Save the background for the GUI...
2027 // memcpy(background, backbuffer, tom_getVideoModeWidth() * 240 * 2);
2028 // In this case, we squash the color to monochrome, then force it to blue + green...
2029 for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
2031 uint32 pixel = backbuffer[i];
2032 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
2033 pixel = ((r + g + b) / 3) & 0x00FF;
2034 background[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
2042 bool debounceRunKey = true;
2043 Window * RunEmu(void)
2045 extern uint32 * backbuffer;
2046 uint32 * overlayPixels = (uint32 *)sdlemuGetOverlayPixels();
2047 memset(overlayPixels, 0x00, 640 * 480 * 4); // Clear out overlay...
2049 //This is crappy... !!! FIX !!!
2050 extern bool finished, showGUI;
2052 sdlemuDisableOverlay();
2054 // uint32 nFrame = 0, nFrameskip = 0;
2055 uint32 totalFrames = 0;
2057 bool showMessage = true;
2058 uint32 showMsgFrames = 120;
2059 uint8 transparency = 0xFF;
2060 // Pass a message to the "joystick" code to debounce the ESC key...
2061 debounceRunKey = true;
2063 uint32 cartType = 4;
2064 if (jaguarRomSize == 0x200000)
2066 else if (jaguarRomSize == 0x400000)
2068 else if (jaguar_mainRom_crc32 == 0x687068D5)
2070 else if (jaguar_mainRom_crc32 == 0x55A0669C)
2073 char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
2074 uint32 elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
2078 // Set up new backbuffer with new pixels and data
2081 //WriteLog("Frame #%u...\n", totalFrames);
2082 //extern bool doDSPDis;
2083 //if (totalFrames == 373)
2086 //Problem: Need to do this *only* when the state changes from visible to not...
2087 //Also, need to clear out the GUI when not on (when showMessage is active...)
2088 if (showGUI || showMessage)
2089 sdlemuEnableOverlay();
2091 sdlemuDisableOverlay();
2093 //Add in a new function for clearing patches of screen (ClearOverlayRect)
2095 // Some QnD GUI stuff here...
2098 FillScreenRectangle(overlayPixels, 8, 1*FONT_HEIGHT, 128, 4*FONT_HEIGHT, 0x00000000);
2099 extern uint32 gpu_pc, dsp_pc;
2100 DrawString(overlayPixels, 8, 1*FONT_HEIGHT, false, "GPU PC: %08X", gpu_pc);
2101 DrawString(overlayPixels, 8, 2*FONT_HEIGHT, false, "DSP PC: %08X", dsp_pc);
2102 DrawString(overlayPixels, 8, 4*FONT_HEIGHT, false, "%u FPS", framesPerSecond);
2107 DrawString2(overlayPixels, 8, 24*FONT_HEIGHT, 0x007F63FF, transparency, "Running...");
2108 DrawString2(overlayPixels, 8, 26*FONT_HEIGHT, 0x001FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
2109 DrawString2(overlayPixels, 8, 27*FONT_HEIGHT, 0x001FFF3F, transparency, "CRC: %08X", jaguar_mainRom_crc32);
2111 if (showMsgFrames == 0)
2115 if (transparency == 0)
2117 showMessage = false;
2118 /*extern bool doGPUDis;
2119 doGPUDis = true;//*/
2129 if (SDL_GetTicks() - elapsedTicks > 250)
2130 elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
2133 // Save the background for the GUI...
2134 // In this case, we squash the color to monochrome, then force it to blue + green...
2135 for(uint32 i=0; i<tom_getVideoModeWidth() * 256; i++)
2137 uint32 pixel = backbuffer[i];
2138 uint8 b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
2139 pixel = ((r + g + b) / 3) & 0x00FF;
2140 backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
2143 sdlemuEnableOverlay();
2153 WriteLog("GUI: Quitting due to user request.\n");
2159 Window * About(void)
2162 // sprintf(buf, "Virtual Jaguar CVS %s", __DATE__);
2163 sprintf(buf, "CVS %s", __DATE__);
2164 //fprintf(fp, "VirtualJaguar v1.0.8 (Last full build was on %s %s)\n", __DATE__, __TIME__);
2165 //VirtualJaguar v1.0.8 (Last full build was on Dec 30 2004 20:01:31)
2166 //Hardwired, bleh... !!! FIX !!!
2167 uint32 width = 55 * FONT_WIDTH, height = 18 * FONT_HEIGHT;
2168 uint32 xpos = (640 - width) / 2, ypos = (480 - height) / 2;
2169 // Window * window = new Window(8, 16, 50 * FONT_WIDTH, 21 * FONT_HEIGHT);
2170 Window * window = new Window(xpos, ypos, width, height);
2171 // window->AddElement(new Text(8, 8, "Virtual Jaguar 1.0.8"));
2172 // window->AddElement(new Text(8, 8, "Virtual Jaguar CVS 20050110", 0xFF3030FF, 0xFF000000));
2173 // window->AddElement(new Text(208, 8+0*FONT_HEIGHT, buf, 0xFF3030FF, 0xFF000000));
2174 window->AddElement(new Text(248, 8+4*FONT_HEIGHT+5, buf, 0xFF3030FF, 0xFF000000));
2175 window->AddElement(new Text(8, 8+0*FONT_HEIGHT, "Coders:"));
2176 window->AddElement(new Text(16, 8+1*FONT_HEIGHT, "James L. Hammons (shamus)"));
2177 window->AddElement(new Text(16, 8+2*FONT_HEIGHT, "Niels Wagenaar (nwagenaar)"));
2178 window->AddElement(new Text(16, 8+3*FONT_HEIGHT, "Carwin Jones (Caz)"));
2179 window->AddElement(new Text(16, 8+4*FONT_HEIGHT, "Adam Green"));
2180 window->AddElement(new Text(8, 8+6*FONT_HEIGHT, "Testers:"));
2181 window->AddElement(new Text(16, 8+7*FONT_HEIGHT, "Guruma"));
2182 window->AddElement(new Text(8, 8+9*FONT_HEIGHT, "Thanks go out to:"));
2183 window->AddElement(new Text(16, 8+10*FONT_HEIGHT, "Aaron Giles for the original CoJag"));
2184 window->AddElement(new Text(16, 8+11*FONT_HEIGHT, "David Raingeard for the original VJ"));
2185 window->AddElement(new Text(16, 8+12*FONT_HEIGHT, "Karl Stenerud for his Musashi 68K emu"));
2186 window->AddElement(new Text(16, 8+13*FONT_HEIGHT, "Sam Lantinga for his amazing SDL libs"));
2187 window->AddElement(new Text(16, 8+14*FONT_HEIGHT, "Ryan C. Gordon for VJ's web presence"));
2188 window->AddElement(new Text(16, 8+15*FONT_HEIGHT, "Curt Vendel for various Jaguar goodies"));
2189 window->AddElement(new Text(16, 8+16*FONT_HEIGHT, "The guys over at Atari Age ;-)"));
2190 // window->AddElement(new Image(8, 8, &vj_title_small));
2191 window->AddElement(new Image(width - (vj_title_small.width + 8), 8, &vj_title_small));
2196 Window * MiscOptions(void)
2198 Window * window = new Window(8, 16, 304, 192);
2199 window->AddElement(new PushButton(8, 8, &vjs.useJaguarBIOS, "BIOS"));
2200 window->AddElement(new SlideSwitch(8, 32, &vjs.hardwareTypeNTSC, "PAL", "NTSC"));
2201 window->AddElement(new PushButton(8, 64, &vjs.DSPEnabled, "DSP"));
2202 window->AddElement(new SlideSwitch(24, 88, &vjs.usePipelinedDSP, "Original", "Pipelined"));
2203 window->AddElement(new SlideSwitch(8, 120, (bool *)&vjs.glFilter, "Sharp", "Blurry"));
2204 window->AddElement(new SlideSwitch(8, 152, (bool *)&vjs.renderType, "Normal render", "TV style"));
2206 window->AddElement(new TextEdit(88, 8, vjs.ROMPath, 20, 0xFF8484FF, 0xFF000000));
2208 /*TextEdit(uint32 x, uint32 y, string s, uint32 mss = 10, uint32 fg = 0xFF8484FF,
2209 uint32 bg = 0xFF84FF4D): Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s),
2210 caretPos(0), maxScreenSize(mss) {}*/
2219 // * Window/fullscreen
2220 // * Key definitions
2227 // Generic ROM loading
2229 uint32 JaguarLoadROM(uint8 * rom, char * path)
2231 // We really should have some kind of sanity checking for the ROM size here to prevent
2232 // a buffer overflow... !!! FIX !!!
2235 WriteLog("JaguarLoadROM: Attempting to load file '%s'...", path);
2236 char * ext = strrchr(path, '.');
2238 WriteLog("FAILED!\n");
2240 WriteLog("Succeeded in finding extension (%s)!\n", ext);
2244 WriteLog("VJ: Loading \"%s\"...", path);
2246 if (strcasecmp(ext, ".zip") == 0)
2248 // Handle ZIP file loading here...
2249 WriteLog("(ZIPped)...");
2251 if (load_zipped_file(0, 0, path, NULL, &rom, &romSize) == -1)
2253 WriteLog("Failed!\n");
2259 /* FILE * fp = fopen(path, "rb");
2263 WriteLog("Failed!\n");
2267 fseek(fp, 0, SEEK_END);
2268 romSize = ftell(fp);
2269 fseek(fp, 0, SEEK_SET);
2270 fread(rom, 1, romSize, fp);
2273 // Handle gzipped files transparently [Adam Green]...
2275 gzFile fp = gzopen(path, "rb");
2279 WriteLog("Failed!\n");
2283 romSize = gzfilelength(fp);
2284 gzseek(fp, 0, SEEK_SET);
2285 gzread(fp, rom, romSize);
2289 WriteLog("OK (%i bytes)\n", romSize);
2296 // Jaguar file loading
2298 bool JaguarLoadFile(char * path)
2300 // jaguarRomSize = JaguarLoadROM(mem, path);
2301 jaguarRomSize = JaguarLoadROM(jaguar_mainRom, path);
2303 /*//This is not *nix friendly for some reason...
2304 // if (!UserSelectFile(path, newPath))
2305 if (!UserSelectFile((strlen(path) == 0 ? (char *)"." : path), newPath))
2307 WriteLog("VJ: Could not find valid ROM in directory \"%s\"...\nAborting!\n", path);
2312 if (jaguarRomSize == 0)
2314 // WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
2315 WriteLog("GUI: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
2316 // Need to do something else here, like throw up an error dialog instead of aborting. !!! FIX !!!
2319 return false; // This is a start...
2322 jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, jaguarRomSize);
2323 WriteLog("CRC: %08X\n", (unsigned int)jaguar_mainRom_crc32);
2326 jaguarRunAddress = 0x802000;
2328 char * ext = strrchr(path, '.'); // Get the file's extension for non-cartridge checking
2330 //NOTE: Should fix JaguarLoadROM() to replace .zip with what's *in* the zip (.abs, .j64, etc.)
2331 if (strcasecmp(ext, ".rom") == 0)
2333 // File extension ".ROM": Alpine image that loads/runs at $802000
2334 WriteLog("GUI: Setting up homebrew (ROM)... Run address: 00802000, length: %08X\n", jaguarRomSize);
2336 for(int i=jaguarRomSize-1; i>=0; i--)
2337 jaguar_mainRom[0x2000 + i] = jaguar_mainRom[i];
2339 memset(jaguar_mainRom, 0xFF, 0x2000);
2340 /* memcpy(jaguar_mainRam, jaguar_mainRom, jaguarRomSize);
2341 memset(jaguar_mainRom, 0xFF, 0x600000);
2342 memcpy(jaguar_mainRom + 0x2000, jaguar_mainRam, jaguarRomSize);
2343 memset(jaguar_mainRam, 0x00, 0x400000);*/
2346 Stubulator ROM vectors...
2347 handler 001 at $00E00008
2348 handler 002 at $00E008DE
2349 handler 003 at $00E008E2
2350 handler 004 at $00E008E6
2351 handler 005 at $00E008EA
2352 handler 006 at $00E008EE
2353 handler 007 at $00E008F2
2354 handler 008 at $00E0054A
2355 handler 009 at $00E008FA
2356 handler 010 at $00000000
2357 handler 011 at $00000000
2358 handler 012 at $00E008FE
2359 handler 013 at $00E00902
2360 handler 014 at $00E00906
2361 handler 015 at $00E0090A
2362 handler 016 at $00E0090E
2363 handler 017 at $00E00912
2364 handler 018 at $00E00916
2365 handler 019 at $00E0091A
2366 handler 020 at $00E0091E
2367 handler 021 at $00E00922
2368 handler 022 at $00E00926
2369 handler 023 at $00E0092A
2370 handler 024 at $00E0092E
2371 handler 025 at $00E0107A
2372 handler 026 at $00E0107A
2373 handler 027 at $00E0107A
2374 handler 028 at $00E008DA
2375 handler 029 at $00E0107A
2376 handler 030 at $00E0107A
2377 handler 031 at $00E0107A
2378 handler 032 at $00000000
2380 Let's try setting up the illegal instruction vector for a stubulated jaguar...
2382 /* SET32(jaguar_mainRam, 0x08, 0x00E008DE);
2383 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
2384 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
2385 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
2387 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
2388 // This kludge works! Yeah!
2389 SET32(jaguar_mainRam, 0x10, 0x00001000);
2390 SET16(jaguar_mainRam, 0x1000, 0x60FE); // Here: bra Here
2392 else if (strcasecmp(ext, ".abs") == 0)
2394 // File extension ".ABS": Atari linker output file with header (w/o is useless to us here)
2397 ABS Format sleuthing (LBUGDEMO.ABS):
2399 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
2400 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
2403 DRI-format file detected...
2404 Text segment size = 0x0000050c bytes
2405 Data segment size = 0x000462c0 bytes
2406 BSS Segment size = 0x00000428 bytes
2407 Symbol Table size = 0x000012a6 bytes
2408 Absolute Address for text segment = 0x00802000
2409 Absolute Address for data segment = 0x0080250c
2410 Absolute Address for BSS segment = 0x00004000
2413 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
2414 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
2415 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
2417 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
2418 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
2419 000050 00 00 00 00 00 00 00 20
2420 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
2421 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
2422 000078 00 00 00 00 00 00 00 40
2423 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
2424 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
2425 0000a0 00 00 00 00 00 00 00 80
2427 Header size is $A8 bytes...
2429 BSD/COFF format file detected...
2430 3 sections specified
2431 Symbol Table offset = 230160 ($00038310)
2432 Symbol Table contains 1339 symbol entries ($0000053B)
2433 The additional header size is 28 bytes ($001C)
2434 Magic Number for RUN_HDR = 0x00000107
2435 Text Segment Size = 7632 ($00001DD0)
2436 Data Segment Size = 222360 ($00036498)
2437 BSS Segment Size = 428928 ($00068B80)
2438 Starting Address for executable = 0x00802000
2439 Start of Text Segment = 0x00802000
2440 Start of Data Segment = 0x00803dd0
2442 if (jaguar_mainRom[0] == 0x60 && jaguar_mainRom[1] == 0x1B)
2444 uint32 loadAddress = GET32(jaguar_mainRom, 0x16), //runAddress = GET32(jaguar_mainRom, 0x2A),
2445 codeSize = GET32(jaguar_mainRom, 0x02) + GET32(jaguar_mainRom, 0x06);
2446 WriteLog("GUI: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
2448 if (loadAddress < 0x800000)
2449 memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x24, codeSize);
2452 for(int i=codeSize-1; i>=0; i--)
2453 jaguar_mainRom[(loadAddress - 0x800000) + i] = jaguar_mainRom[i + 0x24];
2454 /* memcpy(jaguar_mainRam, jaguar_mainRom + 0x24, codeSize);
2455 memset(jaguar_mainRom, 0xFF, 0x600000);
2456 memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
2457 memset(jaguar_mainRam, 0x00, 0x400000);*/
2460 jaguarRunAddress = loadAddress;
2462 else if (jaguar_mainRom[0] == 0x01 && jaguar_mainRom[1] == 0x50)
2464 uint32 loadAddress = GET32(jaguar_mainRom, 0x28), runAddress = GET32(jaguar_mainRom, 0x24),
2465 codeSize = GET32(jaguar_mainRom, 0x18) + GET32(jaguar_mainRom, 0x1C);
2466 WriteLog("GUI: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
2468 if (loadAddress < 0x800000)
2469 memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0xA8, codeSize);
2472 for(int i=codeSize-1; i>=0; i--)
2473 jaguar_mainRom[(loadAddress - 0x800000) + i] = jaguar_mainRom[i + 0xA8];
2474 /* memcpy(jaguar_mainRam, jaguar_mainRom + 0xA8, codeSize);
2475 memset(jaguar_mainRom, 0xFF, 0x600000);
2476 memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
2477 memset(jaguar_mainRam, 0x00, 0x400000);*/
2480 jaguarRunAddress = runAddress;
2484 WriteLog("GUI: Couldn't find correct ABS format: %02X %02X\n", jaguar_mainRom[0], jaguar_mainRom[1]);
2488 else if (strcasecmp(ext, ".jag") == 0)
2490 // File extension ".JAG": Atari server file with header
2491 //NOTE: The bytes 'JAGR' should also be at position $1C...
2492 // Also, there's *always* a $601A header at position $00...
2493 if (jaguar_mainRom[0] == 0x60 && jaguar_mainRom[1] == 0x1A)
2495 uint32 loadAddress = GET32(jaguar_mainRom, 0x22), runAddress = GET32(jaguar_mainRom, 0x2A);
2496 //This is not always right! Especially when converted via bin2jag1!!!
2497 //We should have access to the length of the furshlumiger file that was loaded anyway!
2499 // uint32 progLength = GET32(jaguar_mainRom, 0x02);
2502 // WriteLog("Jaguar: Setting up PD ROM... Run address: %08X, length: %08X\n", runAddress, progLength);
2503 // memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, progLength);
2504 WriteLog("GUI: Setting up homebrew (JAG)... Run address: %08X, length: %08X\n", runAddress, jaguarRomSize - 0x2E);
2505 memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, jaguarRomSize - 0x2E);
2506 // SET32(jaguar_mainRam, 4, runAddress);
2507 jaguarRunAddress = runAddress;
2512 // .J64 (Jaguar cartridge ROM image) is implied by the FileList object...
2518 // Get the length of a (possibly) gzipped file
2520 int gzfilelength(gzFile gd)
2522 int size = 0, length = 0;
2523 unsigned char buffer[0x10000];
2529 // Read in chunks until EOF
2530 size = gzread(gd, buffer, 0x10000);