robmcmullen-apple2/src/gui/guimisc.cpp

2823 lines
98 KiB
C++
Raw Blame History

//
// GUIMISC.CPP
//
// Graphical User Interface support functions
// by James Hammons
//
// JLH = James Hammons <jlhamm@acm.org>
//
// WHO WHEN WHAT
// --- ---------- ------------------------------------------------------------
// JLH 02/02/2006 Created this file
// JLH 03/13/2006 Abstracted out font to allow external fonts
//
#include "guimisc.h"
#include "font14pt.h"
//Can't do this...!
//#include "charset.h" // For Apple II font (small)
#include <string>
#include <vector>
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define MASK_R 0xFF000000
#define MASK_G 0x00FF0000
#define MASK_B 0x0000FF00
#define MASK_A 0x000000FF
#else
#define MASK_R 0x000000FF
#define MASK_G 0x0000FF00
#define MASK_B 0x00FF0000
#define MASK_A 0xFF000000
#endif
// Local variables
// This will enable us to set up any font without having it embedded here...
static Font font((uint8_t *)font2, FONT_WIDTH, FONT_HEIGHT);
static std::vector<Font> oldFontList;
void SetNewFont(Font newFont)
{
oldFontList.push_back(font);
font.data = newFont.data, font.width = newFont.width, font.height = newFont.height;
}
void RestoreOldFont(void)
{
if (oldFontList.size() == 0)
return;
font = oldFontList.back();
oldFontList.pop_back();
}
uint32_t GetFontWidth(void)
{
return font.width;
}
uint32_t GetFontHeight(void)
{
return font.height;
}
//
// Draw text at the given x/y coordinates with transparency (255 is fully opaque, 0 is fully transparent).
//
void DrawStringTrans(SDL_Surface * screen, uint32_t x, uint32_t y, uint32_t color, const char * text, ...)
{
char string[4096];
va_list arg;
va_start(arg, text);
vsprintf(string, text, arg);
va_end(arg);
uint8_t * esColor = (uint8_t *)&color; // Do things endian safe...!
uint8_t trans = esColor[3];
uint32_t length = strlen(string);
// Make a "stamp" surface (with built in alpha!) for constructing our font chars...
SDL_Surface * chr = SDL_CreateRGBSurface(SDL_SWSURFACE, font.width, font.height, 32,
MASK_R, MASK_G, MASK_B, MASK_A);
SDL_Rect rect;
rect.x = x, rect.y = y;
//bleh
uint8_t r1, g1, b1, a1;
SDL_GetRGBA(color, screen->format, &r1, &g1, &b1, &a1);
color = SDL_MapRGBA(chr->format, r1, g1, b1, a1);
//helb
for(uint32_t i=0; i<length; i++)
{
uint8_t c = string[i];
uint32_t fontAddr = (uint32_t)(c < 32 ? 0 : c - 32) * font.width * font.height;
for(uint32_t yy=0; yy<font.height; yy++)
{
for(uint32_t xx=0; xx<font.width; xx++)
{
esColor[3] = (font.data[fontAddr++] * trans) / 255;
((uint32_t *)chr->pixels)[xx + (yy * (chr->pitch / 4))] = color;
}
}
SDL_BlitSurface(chr, NULL, screen, &rect);
rect.x += font.width;
}
SDL_FreeSurface(chr);
}
//
// Draw text at given x/y coords using foreground/background color combination
//
void DrawStringOpaque(SDL_Surface * screen, uint32_t x, uint32_t y, uint32_t fg, uint32_t bg, const char * text, ...)
{
char string[4096];
va_list arg;
va_start(arg, text);
vsprintf(string, text, arg);
va_end(arg);
uint8_t * esColor = (uint8_t *)&fg; // Do things endian safe...!
uint32_t length = strlen(string);
SDL_Rect destRect;
destRect.x = x, destRect.y = y;
destRect.w = length * font.width, destRect.h = font.height;
SDL_FillRect(screen, &destRect, bg);
// Make a "stamp" surface (with built in alpha!) for constructing our font chars...
SDL_Surface * chr = SDL_CreateRGBSurface(SDL_SWSURFACE, font.width, font.height, 32,
MASK_R, MASK_G, MASK_B, MASK_A);
SDL_Rect rect;
rect.x = x, rect.y = y;
//bleh (we have to map colors from the HW surface to the SW surface)
uint8_t r1, g1, b1, a1;
SDL_GetRGBA(fg, screen->format, &r1, &g1, &b1, &a1);
fg = SDL_MapRGBA(chr->format, r1, g1, b1, a1);
SDL_GetRGBA(bg, screen->format, &r1, &g1, &b1, &a1);
bg = SDL_MapRGBA(chr->format, r1, g1, b1, a1);
//helb
for(uint32_t i=0; i<length; i++)
{
uint8_t c = string[i];
uint32_t fontAddr = (uint32_t)(c < 32 ? 0 : c - 32) * font.width * font.height;
for(uint32_t yy=0; yy<font.height; yy++)
{
for(uint32_t xx=0; xx<font.width; xx++)
{
esColor[3] = font.data[fontAddr++];
((uint32_t *)chr->pixels)[xx + (yy * (chr->pitch / 4))] = fg;
}
}
SDL_BlitSurface(chr, NULL, screen, &rect);
rect.x += font.width;
}
SDL_FreeSurface(chr);
}
bool RectanglesIntersect(SDL_Rect r1, SDL_Rect r2)
{
// The strategy here is to see if any of the sides of the smaller rect
// fall within the larger.
/*
+-----------------+ r1
| |
| +------+ r2 |
| | | |
| | | |
| +------+ |
| |
+-----------------+
*/
//This approach fails if r2 is inside of r1. !!! FIX !!! [DONE]
if (RectangleFirstInsideSecond(r2, r1))
return true;
if ((r1.x > r2.x && r1.x < (r2.x + r2.w))
|| ((r1.x + r1.w) > r2.x && (r1.x + r1.w) < (r2.x + r2.w))
|| (r1.y > r2.y && r1.y < (r2.y + r2.h))
|| ((r1.y + r1.h) > r2.y && (r1.y + r1.h) < (r2.y + r2.h)))
return true;
return false;
}
bool RectangleFirstInsideSecond(SDL_Rect r1, SDL_Rect r2)
{
if ((r1.x > r2.x && (r1.x + r1.w) > r2.x)
&& (r1.x < (r2.x + r2.w) && (r1.x + r1.w) < (r2.x + r2.w))
&& (r1.y > r2.y && (r1.y + r1.h) > r2.y)
&& (r1.y < (r2.y + r2.h) && (r1.y + r1.h) < (r2.y + r2.h)))
return true;
return false;
}
//
// Various GUI bitmaps
//
// These representations *should* be endian safe.
uint8_t closeBox[] = {
15 / 256, 15 % 256, // width (HI byte, LO byte)
15 / 256, 15 % 256, // height (HI byte, LO byte)
0x00, 0x00, 0x00, 0x00, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xED, 0x38, 0x38, 0xFF, 0xE7, 0x58, 0x58, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xE7, 0x58, 0x58, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xE7, 0x58, 0x58, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xE7, 0x58, 0x58, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0x00
};
uint8_t closeBoxHover[] = {
15 / 256, 15 % 256, // width (HI byte, LO byte)
15 / 256, 15 % 256, // height (HI byte, LO byte)
0x00, 0x00, 0x00, 0x00, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xED, 0x38, 0x38, 0xFF, 0xE7, 0x58, 0x58, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xE7, 0x58, 0x58, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xB7, 0x52, 0xFF, 0xFF, 0xB7, 0x52, 0xFF, 0xFF, 0xB7, 0x52, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xBE, 0x63, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xB7, 0x52, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xB7, 0x52, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xB7, 0x52, 0xFF, 0xFF, 0xBE, 0x63, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xBE, 0x63, 0xFF, 0xFF, 0xB7, 0x52, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xB7, 0x52, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xB7, 0x52, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xBE, 0x63, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xB7, 0x52, 0xFF, 0xFF, 0xB7, 0x52, 0xFF, 0xFF, 0xB7, 0x52, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xFF, 0xAF, 0x40, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFF, 0xA2, 0x20, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xE7, 0x58, 0x58, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xFB, 0x6B, 0x6B, 0xFF, 0xE7, 0x58, 0x58, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0x00
};
uint8_t closeBoxDown[] = {
15 / 256, 15 % 256, // width (HI byte, LO byte)
15 / 256, 15 % 256, // height (HI byte, LO byte)
0x00, 0x00, 0x00, 0x00, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xED, 0x38, 0x38, 0xFF, 0xE2, 0x1D, 0x1D, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xE2, 0x1D, 0x1D, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xCC, 0x77, 0x00, 0xFF, 0xCC, 0x77, 0x00, 0xFF, 0xCC, 0x77, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xBD, 0x6E, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xCC, 0x77, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xCC, 0x77, 0x00, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xCC, 0x77, 0x00, 0xFF, 0xBD, 0x6E, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xBD, 0x6E, 0x00, 0xFF, 0xCC, 0x77, 0x00, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xCC, 0x77, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xCC, 0x77, 0x00, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xBD, 0x6E, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xCC, 0x77, 0x00, 0xFF, 0xCC, 0x77, 0x00, 0xFF, 0xCC, 0x77, 0x00, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xE2, 0x86, 0x07, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xF7, 0x9D, 0x1F, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0xED, 0x38, 0x38, 0xFF, 0xE2, 0x1D, 0x1D, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xC0, 0x2B, 0x2B, 0xFF, 0xE2, 0x1D, 0x1D, 0xFF, 0xED, 0x38, 0x38, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0xED, 0x38, 0x38, 0xFF, 0x00, 0x00, 0x00, 0x00
};
#include "fd-img-128x128.c"
#if 0
#include <stdarg.h>
#include <sys/types.h> // For MacOS <dirent.h> dependency
#include <dirent.h>
#include <SDL.h>
#include <string>
#include <vector>
#include <algorithm>
#include <ctype.h> // For toupper()
#include "settings.h"
#include "tom.h"
#include "video.h"
#include "clock.h"
#include "font1.h"
#include "font14pt.h" // Also 15, 16, 17, 18
#include "guielements.h"
#include "crc32.h"
#include "zlib.h"
#include "unzip.h"
#include "sdlemu_opengl.h"
#include "gui.h"
using namespace std; // For STL stuff
// Private function prototypes
class Window; // Forward declaration...
//void DrawTransparentBitmap(uint32_t * screen, uint32_t x, uint32_t y, uint32_t * bitmap, uint8_t * alpha = NULL);
void DrawTransparentBitmapDeprecated(uint32_t * screen, uint32_t x, uint32_t y, uint32_t * bitmap);
void DrawTransparentBitmap(uint32_t * screen, uint32_t x, uint32_t y, const void * bitmap);
void DrawBitmap(uint32_t * screen, uint32_t x, uint32_t y, const void * bitmap);
//Should call this FillScreenRectangle with a number representing the RGBA value to fill. !!! FIX !!!
//void ClearScreenRectangle(uint32_t * screen, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void FillScreenRectangle(uint32_t * screen, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color);
void DrawStringTrans(uint32_t * screen, uint32_t x, uint32_t y, uint32_t color, uint8_t opacity, const char * text, ...);
void DrawStringOpaque(uint32_t * screen, uint32_t x, uint32_t y, uint32_t color1, uint32_t color2, const char * text, ...);
void DrawString(uint32_t * screen, uint32_t x, uint32_t y, bool invert, const char * text, ...);
void DrawString2(uint32_t * screen, uint32_t x, uint32_t y, uint32_t color, uint8_t transparency, const char * text, ...);
Window * LoadROM(void);
Window * ResetJaguar(void);
Window * ResetJaguarCD(void);
Window * RunEmu(void);
Window * Quit(void);
Window * About(void);
Window * MiscOptions(void);
int gzfilelength(gzFile gd);
// External variables
extern uint8_t * jaguar_mainRam;
extern uint8_t * jaguar_mainRom;
extern uint8_t * jaguar_bootRom;
extern uint8_t * jaguar_CDBootROM;
extern bool BIOSLoaded;
extern bool CDBIOSLoaded;
// Local global variables
bool exitGUI = false; // GUI (emulator) done variable
int mouseX = 0, mouseY = 0;
uint32_t background[1280 * 256]; // GUI background buffer
char separator[] = "--------------------------------------------------------";
//
// Case insensitive string compare function
// Taken straight out of Thinking In C++ by Bruce Eckel. Thanks Bruce!
//
int stringCmpi(const string &s1, const string &s2)
{
// Select the first element of each string:
string::const_iterator p1 = s1.begin(), p2 = s2.begin();
while (p1 != s1.end() && p2 != s2.end()) // Don<6F>t run past the end
{
if (toupper(*p1) != toupper(*p2)) // Compare upper-cased chars
return (toupper(*p1) < toupper(*p2) ? -1 : 1);// Report which was lexically greater
p1++;
p2++;
}
// If they match up to the detected eos, say which was longer. Return 0 if the same.
return s2.size() - s1.size();
}
//
// Local GUI classes
//
enum { WINDOW_CLOSE, MENU_ITEM_CHOSEN };
class Element
{
public:
Element(uint32_t x = 0, uint32_t y = 0, uint32_t w = 0, uint32_t h = 0)
{ extents.x = x, extents.y = y, extents.w = w, extents.h = h; }
virtual void HandleKey(SDL_Scancode key) = 0; // These are "pure" virtual functions...
virtual void HandleMouseMove(uint32_t x, uint32_t y) = 0;
virtual void HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown) = 0;
virtual void Draw(uint32_t, uint32_t) = 0;
virtual void Notify(Element *) = 0;
//Needed? virtual ~Element() = 0;
//We're not allocating anything in the base class, so the answer would be NO.
bool Inside(uint32_t x, uint32_t y);
// Class method
// static void SetScreenAndPitch(int16_t * s, uint32_t p) { screenBuffer = s, pitch = p; }
static void SetScreenAndPitch(uint32_t * s, uint32_t p) { screenBuffer = s, pitch = p; }
protected:
SDL_Rect extents;
uint32_t state;
// Class variables...
// static int16_t * screenBuffer;
static uint32_t * screenBuffer;
static uint32_t pitch;
};
// Initialize class variables (Element)
//int16_t * Element::screenBuffer = NULL;
uint32_t * Element::screenBuffer = NULL;
uint32_t Element::pitch = 0;
bool Element::Inside(uint32_t x, uint32_t y)
{
return (x >= (uint32_t)extents.x && x < (uint32_t)(extents.x + extents.w)
&& y >= (uint32_t)extents.y && y < (uint32_t)(extents.y + extents.h) ? true : false);
}
//
// Button class
//
class Button: public Element
{
public:
Button(uint32_t x = 0, uint32_t y = 0, uint32_t w = 0, uint32_t h = 0): Element(x, y, w, h),
activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
bgColor(0xFF00FF00), pic(NULL), elementToTell(NULL) {}
Button(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t * p): Element(x, y, w, h),
activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
bgColor(0xFF00FF00), pic(p), elementToTell(NULL) {}
// Button(uint32_t x, uint32_t y, uint32_t * p): Element(x, y, 0, 0),
Button(uint32_t x, uint32_t y, uint32_t * p, uint32_t * pH = NULL, uint32_t * pD = NULL): Element(x, y, 0, 0),
activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
bgColor(0xFF00FF00), pic(p), picHover(pH), picDown(pD), elementToTell(NULL)
{ if (pic) extents.w = pic[0], extents.h = pic[1]; }
Button(uint32_t x, uint32_t y, uint32_t w, uint32_t h, string s): Element(x, y, w, h),
activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
bgColor(0xFF00FF00), pic(NULL), text(s), elementToTell(NULL) {}
Button(uint32_t x, uint32_t y, string s): Element(x, y, 0, FONT_HEIGHT),
activated(false), clicked(false), inside(false), fgColor(0xFFFFFFFF),
bgColor(0xFF00FF00), pic(NULL), text(s), elementToTell(NULL)
{ extents.w = s.length() * FONT_WIDTH; }
virtual void HandleKey(SDL_Scancode key) {}
virtual void HandleMouseMove(uint32_t x, uint32_t y);
virtual void HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown);
virtual void Draw(uint32_t offsetX = 0, uint32_t offsetY = 0);
virtual void Notify(Element *) {}
bool ButtonClicked(void) { return activated; }
void SetNotificationElement(Element * e) { elementToTell = e; }
protected:
bool activated, clicked, inside;
uint32_t fgColor, bgColor;
uint32_t * pic, * picHover, * picDown;
string text;
Element * elementToTell;
};
void Button::HandleMouseMove(uint32_t x, uint32_t y)
{
inside = Inside(x, y);
}
void Button::HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown)
{
if (inside)
{
if (mouseDown)
clicked = true;
if (clicked && !mouseDown)
{
clicked = false, activated = true;
// Send a message that we're activated (if there's someone to tell, that is)
if (elementToTell)
elementToTell->Notify(this);
}
}
else
clicked = activated = false;
}
void Button::Draw(uint32_t offsetX/*= 0*/, uint32_t offsetY/*= 0*/)
{
uint32_t addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
if (text.length() > 0) // Simple text button
// if (pic == NULL)
{
for(uint32_t y=0; y<extents.h; y++)
{
for(uint32_t x=0; x<extents.w; x++)
{
// Doesn't clip in y axis! !!! FIX !!!
if (extents.x + x < pitch)
screenBuffer[addr + x + (y * pitch)]
// = (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
//43F0 -> 010000 11111 10000 -> 0100 0001 1111 1111 1000 0100 -> 41 FF 84
= (clicked && inside ? fgColor : (inside ? 0xFF84FF41 : bgColor));
}
}
DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
}
else // Graphical button
{
uint32_t * picToShow = pic;
if (picHover != NULL && inside && !clicked)
picToShow = picHover;
if (picDown != NULL && inside && clicked)
picToShow = picDown;
DrawTransparentBitmapDeprecated(screenBuffer, extents.x + offsetX, extents.y + offsetY, picToShow);
}
}
//
// PushButton class
//
class PushButton: public Element
{
// How to handle?
// Save state externally?
//We pass in a state variable if we want to track it externally, otherwise we use our own
//internal state var. Still need to do some kind of callback for pushbuttons that do things
//like change from fullscreen to windowed... !!! FIX !!!
public:
// PushButton(uint32_t x = 0, uint32_t y = 0, uint32_t w = 0, uint32_t h = 0): Element(x, y, w, h),
// activated(false), clicked(false), inside(false), fgColor(0xFFFF),
// bgColor(0x03E0), pic(NULL), elementToTell(NULL) {}
// PushButton(uint32_t x, uint32_t y, bool * st, string s): Element(x, y, 8, 8), state(st),
// inside(false), text(s) { if (st == NULL) state = &internalState; }
PushButton(uint32_t x, uint32_t y, bool * st, string s): Element(x, y, 16, 16), state(st),
inside(false), text(s) { if (st == NULL) state = &internalState; }
/* Button(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t * p): Element(x, y, w, h),
activated(false), clicked(false), inside(false), fgColor(0xFFFF),
bgColor(0x03E0), pic(p), elementToTell(NULL) {}
Button(uint32_t x, uint32_t y, uint32_t * p): Element(x, y, 0, 0),
activated(false), clicked(false), inside(false), fgColor(0xFFFF),
bgColor(0x03E0), pic(p), elementToTell(NULL)
{ if (pic) extents.w = pic[0], extents.h = pic[1]; }
Button(uint32_t x, uint32_t y, uint32_t w, uint32_t h, string s): Element(x, y, w, h),
activated(false), clicked(false), inside(false), fgColor(0xFFFF),
bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL) {}
PushButton(uint32_t x, uint32_t y, string s): Element(x, y, 0, 8),
activated(false), clicked(false), inside(false), fgColor(0xFFFF),
bgColor(0x03E0), pic(NULL), text(s), elementToTell(NULL)
{ extents.w = s.length() * 8; }*/
virtual void HandleKey(SDL_Scancode key) {}
virtual void HandleMouseMove(uint32_t x, uint32_t y);
virtual void HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown);
virtual void Draw(uint32_t offsetX = 0, uint32_t offsetY = 0);
virtual void Notify(Element *) {}
// bool ButtonClicked(void) { return activated; }
// void SetNotificationElement(Element * e) { elementToTell = e; }
protected:
bool * state;
bool inside;
// bool activated, clicked, inside;
// uint16_t fgColor, bgColor;
// uint32_t * pic;
string text;
// Element * elementToTell;
bool internalState;
};
void PushButton::HandleMouseMove(uint32_t x, uint32_t y)
{
inside = Inside(x, y);
}
void PushButton::HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown)
{
if (inside && mouseDown)
{
/* if (mouseDown)
clicked = true;
if (clicked && !mouseDown)
{
clicked = false, activated = true;
// Send a message that we're activated (if there's someone to tell, that is)
if (elementToTell)
elementToTell->Notify(this);
}*/
*state = !(*state);
}
// else
// clicked = activated = false;
}
void PushButton::Draw(uint32_t offsetX/*= 0*/, uint32_t offsetY/*= 0*/)
{
/* uint32_t addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
for(uint32_t y=0; y<extents.h; y++)
{
for(uint32_t x=0; x<extents.w; x++)
{
// Doesn't clip in y axis! !!! FIX !!!
if (extents.x + x < pitch)
screenBuffer[addr + x + (y * pitch)]
= (clicked && inside ? fgColor : (inside ? 0x43F0 : bgColor));
}
}*/
if (*state)
DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, &pbDown);
else
DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, &pbUp);
if (text.length() > 0)
DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY, false, "%s", text.c_str());
}
//
// SlideSwitch class
//
class SlideSwitch: public Element
{
// How to handle?
// Save state externally?
//Seems to be handled the same as PushButton, but without sanity checks. !!! FIX !!!
public:
SlideSwitch(uint32_t x, uint32_t y, bool * st, string s1, string s2): Element(x, y, 16, 32), state(st),
inside(false), text1(s1), text2(s2) {}
virtual void HandleKey(SDL_Scancode key) {}
virtual void HandleMouseMove(uint32_t x, uint32_t y);
virtual void HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown);
virtual void Draw(uint32_t offsetX = 0, uint32_t offsetY = 0);
virtual void Notify(Element *) {}
// bool ButtonClicked(void) { return activated; }
// void SetNotificationElement(Element * e) { elementToTell = e; }
protected:
bool * state;
bool inside;
// bool activated, clicked, inside;
// uint16_t fgColor, bgColor;
// uint32_t * pic;
string text1, text2;
// Element * elementToTell;
};
void SlideSwitch::HandleMouseMove(uint32_t x, uint32_t y)
{
inside = Inside(x, y);
}
void SlideSwitch::HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown)
{
if (inside && mouseDown)
{
/* if (mouseDown)
clicked = true;
if (clicked && !mouseDown)
{
clicked = false, activated = true;
// Send a message that we're activated (if there's someone to tell, that is)
if (elementToTell)
elementToTell->Notify(this);
}*/
*state = !(*state);
}
// else
// clicked = activated = false;
}
void SlideSwitch::Draw(uint32_t offsetX/*= 0*/, uint32_t offsetY/*= 0*/)
{
DrawTransparentBitmapDeprecated(screenBuffer, extents.x + offsetX, extents.y + offsetY, (*state ? slideSwitchDown : slideSwitchUp));
if (text1.length() > 0)
DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY, false, "%s", text1.c_str());
if (text2.length() > 0)
DrawString(screenBuffer, extents.x + offsetX + 24, extents.y + offsetY + 16, false, "%s", text2.c_str());
}
//
// Window class
//
class Window: public Element
{
public:
/* Window(uint32_t x = 0, uint32_t y = 0, uint32_t w = 0, uint32_t h = 0): Element(x, y, w, h),
fgColor(0x4FF0), bgColor(0xFE10)
{ close = new Button(w - 8, 1, closeBox); list.push_back(close); }*/
Window(uint32_t x = 0, uint32_t y = 0, uint32_t w = 0, uint32_t h = 0,
void (* f)(Element *) = NULL): Element(x, y, w, h),
// /*clicked(false), inside(false),*/ fgColor(0x4FF0), bgColor(0x1E10),
//4FF0 -> 010011 11111 10000 -> 0100 1101 1111 1111 1000 0100 -> 4D FF 84
//1E10 -> 000111 10000 10000 -> 0001 1111 1000 0100 1000 0100 -> 1F 84 84
/*clicked(false), inside(false),*/ fgColor(0xFF84FF4D), bgColor(0xFF84841F),
handler(f)
{ close = new Button(w - (CLOSEBOX_WIDTH + 1), 1, closeBox, closeBoxHover, closeBoxDown);
list.push_back(close);
close->SetNotificationElement(this); }
virtual ~Window();
virtual void HandleKey(SDL_Scancode key);
virtual void HandleMouseMove(uint32_t x, uint32_t y);
virtual void HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown);
virtual void Draw(uint32_t offsetX = 0, uint32_t offsetY = 0);
virtual void Notify(Element * e);
void AddElement(Element * e);
// bool WindowActive(void) { return true; }//return !close->ButtonClicked(); }
protected:
// bool clicked, inside;
uint32_t fgColor, bgColor;
void (* handler)(Element *);
Button * close;
//We have to use a list of Element *pointers* because we can't make a list that will hold
//all the different object types in the same list...
vector<Element *> list;
};
Window::~Window()
{
for(uint32_t i=0; i<list.size(); i++)
if (list[i])
delete list[i];
}
void Window::HandleKey(SDL_Scancode key)
{
if (key == SDLK_ESCAPE)
{
SDL_Event event;
event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
SDL_PushEvent(&event);
}
// Handle the items this window contains...
for(uint32_t i=0; i<list.size(); i++)
// Make coords relative to upper right corner of this window...
list[i]->HandleKey(key);
}
void Window::HandleMouseMove(uint32_t x, uint32_t y)
{
// Handle the items this window contains...
for(uint32_t i=0; i<list.size(); i++)
// Make coords relative to upper right corner of this window...
list[i]->HandleMouseMove(x - extents.x, y - extents.y);
}
void Window::HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown)
{
// Handle the items this window contains...
for(uint32_t i=0; i<list.size(); i++)
// Make coords relative to upper right corner of this window...
list[i]->HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
}
void Window::Draw(uint32_t offsetX/*= 0*/, uint32_t offsetY/*= 0*/)
{
uint32_t addr = (extents.x + offsetX) + ((extents.y + offsetY) * pitch);
for(uint32_t y=0; y<extents.h; y++)
{
for(uint32_t x=0; x<extents.w; x++)
{
// Doesn't clip in y axis! !!! FIX !!!
if (extents.x + x < pitch)
screenBuffer[addr + x + (y * pitch)] = bgColor;
}
}
// Handle the items this window contains...
for(uint32_t i=0; i<list.size(); i++)
list[i]->Draw(extents.x, extents.y);
}
void Window::AddElement(Element * e)
{
list.push_back(e);
}
void Window::Notify(Element * e)
{
if (e == close)
{
SDL_Event event;
event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
SDL_PushEvent(&event);
}
}
//
// Static text class
//
class Text: public Element
{
public:
// Text(uint32_t x = 0, uint32_t y = 0, uint32_t w = 0, uint32_t h = 0): Element(x, y, w, h),
// fgColor(0x4FF0), bgColor(0xFE10) {}
// Text(uint32_t x, uint32_t y, string s, uint16_t fg = 0x4FF0, uint16_t bg = 0xFE10): Element(x, y, 0, 0),
// fgColor(fg), bgColor(bg), text(s) {}
//4FF0 -> 010011 11111 10000 -> 0100 1101 1111 1111 1000 0100 -> 4D FF 84
//FE10 -> 111111 10000 10000 -> 1111 1111 1000 0100 1000 0100 -> FF 84 84
Text(uint32_t x = 0, uint32_t y = 0, uint32_t w = 0, uint32_t h = 0): Element(x, y, w, h),
fgColor(0xFF8484FF), bgColor(0xFF84FF4D) {}
Text(uint32_t x, uint32_t y, string s, uint32_t fg = 0xFF8484FF, uint32_t bg = 0xFF84FF4D):
Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s) {}
virtual void HandleKey(SDL_Scancode key) {}
virtual void HandleMouseMove(uint32_t x, uint32_t y) {}
virtual void HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown) {}
virtual void Draw(uint32_t offsetX = 0, uint32_t offsetY = 0);
virtual void Notify(Element *) {}
protected:
uint32_t fgColor, bgColor;
string text;
};
void Text::Draw(uint32_t offsetX/*= 0*/, uint32_t offsetY/*= 0*/)
{
if (text.length() > 0)
// DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
DrawStringOpaque(screenBuffer, extents.x + offsetX, extents.y + offsetY, fgColor, bgColor, "%s", text.c_str());
}
//
// Static image class
//
class Image: public Element
{
public:
Image(uint32_t x, uint32_t y, const void * img): Element(x, y, 0, 0), image(img) {}
virtual void HandleKey(SDL_Scancode key) {}
virtual void HandleMouseMove(uint32_t x, uint32_t y) {}
virtual void HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown) {}
virtual void Draw(uint32_t offsetX = 0, uint32_t offsetY = 0);
virtual void Notify(Element *) {}
protected:
uint32_t fgColor, bgColor;
const void * image;
};
void Image::Draw(uint32_t offsetX/*= 0*/, uint32_t offsetY/*= 0*/)
{
if (image != NULL)
DrawTransparentBitmap(screenBuffer, extents.x + offsetX, extents.y + offsetY, image);
}
//
// TextEdit class
//
class TextEdit: public Element
{
public:
TextEdit(uint32_t x = 0, uint32_t y = 0, uint32_t w = 0, uint32_t h = 0): Element(x, y, w, h),
fgColor(0xFF8484FF), bgColor(0xFF84FF4D), text(""), caretPos(0),
maxScreenSize(10) {}
TextEdit(uint32_t x, uint32_t y, string s, uint32_t mss = 10, uint32_t fg = 0xFF8484FF,
uint32_t bg = 0xFF84FF4D): Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s),
caretPos(0), maxScreenSize(mss) {}
virtual void HandleKey(SDL_Scancode key);
virtual void HandleMouseMove(uint32_t x, uint32_t y) {}
virtual void HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown) {}
virtual void Draw(uint32_t offsetX = 0, uint32_t offsetY = 0);
virtual void Notify(Element *) {}
protected:
uint32_t fgColor, bgColor;
string text;
uint32_t caretPos;
uint32_t maxScreenSize;
};
//Set different filters depending on type passed in on construction, e.g., filename, amount, etc...?
void TextEdit::HandleKey(SDL_Scancode key)
{
if ((key >= SDLK_a && key <= SDLK_z) || (key >= SDLK_0 && key <= SDLK_9) || key == SDLK_PERIOD
|| key == SDLK_SLASH)
{
//Need to handle shift key as well...
text[caretPos++] = key;
Draw();
}
else if (key == SDLK_BACKSPACE)
{
}
else if (key == SDLK_DELETE)
{
}
//left, right arrow
}
void TextEdit::Draw(uint32_t offsetX/*= 0*/, uint32_t offsetY/*= 0*/)
{
if (text.length() > 0)
{
FillScreenRectangle(screenBuffer, extents.x + offsetX, extents.y + offsetY, FONT_WIDTH * maxScreenSize, FONT_HEIGHT, bgColor);
// DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY, false, "%s", text.c_str());
DrawStringOpaque(screenBuffer, extents.x + offsetX, extents.y + offsetY, fgColor, bgColor, "%s", text.c_str());
}
// Draw the caret (underscore? or vertical line?)
}
//
// ListBox class
//
class ListBox: public Element
//class ListBox: public Window
{
public:
// ListBox(uint32_t x = 0, uint32_t y = 0, uint32_t w = 0, uint32_t h = 0): Element(x, y, w, h),
ListBox(uint32_t x = 0, uint32_t y = 0, uint32_t w = 0, uint32_t h = 0);//: Window(x, y, w, h),
// windowPtr(0), cursor(0), limit(0), charWidth((w / 8) - 1), charHeight(h / 8),
// elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
// downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox) {}
virtual void HandleKey(SDL_Scancode key);
virtual void HandleMouseMove(uint32_t x, uint32_t y);
virtual void HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown);
virtual void Draw(uint32_t offsetX = 0, uint32_t offsetY = 0);
virtual void Notify(Element * e);
void SetNotificationElement(Element * e) { elementToTell = e; }
void AddItem(string s);
string GetSelectedItem(void);
protected:
bool thumbClicked;
uint32_t windowPtr, cursor, limit;
uint32_t charWidth, charHeight; // Box width/height in characters
Element * elementToTell;
Button upArrow, downArrow, upArrow2;
vector<string> item;
private:
uint32_t yRelativePoint;
};
ListBox::ListBox(uint32_t x, uint32_t y, uint32_t w, uint32_t h): Element(x, y, w, h),
thumbClicked(false), windowPtr(0), cursor(0), limit(0), charWidth((w / FONT_WIDTH) - 1),
charHeight(h / FONT_HEIGHT), elementToTell(NULL), upArrow(w - 8, 0, upArrowBox),
downArrow(w - 8, h - 8, downArrowBox), upArrow2(w - 8, h - 16, upArrowBox)
{
upArrow.SetNotificationElement(this);
downArrow.SetNotificationElement(this);
upArrow2.SetNotificationElement(this);
extents.w -= 8; // Make room for scrollbar...
}
void ListBox::HandleKey(SDL_Scancode key)
{
if (key == SDLK_DOWN)
{
if (cursor != limit - 1) // Cursor is within its window
cursor++;
else // Otherwise, scroll the window...
{
if (cursor + windowPtr != item.size() - 1)
windowPtr++;
}
}
else if (key == SDLK_UP)
{
if (cursor != 0)
cursor--;
else
{
if (windowPtr != 0)
windowPtr--;
}
}
else if (key == SDLK_PAGEDOWN)
{
if (cursor != limit - 1)
cursor = limit - 1;
else
{
windowPtr += limit;
if (windowPtr > item.size() - limit)
windowPtr = item.size() - limit;
}
}
else if (key == SDLK_PAGEUP)
{
if (cursor != 0)
cursor = 0;
else
{
if (windowPtr < limit)
windowPtr = 0;
else
windowPtr -= limit;
}
}
else if (key >= SDLK_a && key <= SDLK_z)
{
// Advance cursor to filename with first letter pressed...
uint8_t which = (key - SDLK_a) + 65; // Convert key to A-Z char
for(uint32_t i=0; i<item.size(); i++)
{
if ((item[i][0] & 0xDF) == which)
{
cursor = i - windowPtr;
if (i > windowPtr + limit - 1)
windowPtr = i - limit + 1, cursor = limit - 1;
if (i < windowPtr)
windowPtr = i, cursor = 0;
break;
}
}
}
}
void ListBox::HandleMouseMove(uint32_t x, uint32_t y)
{
upArrow.HandleMouseMove(x - extents.x, y - extents.y);
downArrow.HandleMouseMove(x - extents.x, y - extents.y);
upArrow2.HandleMouseMove(x - extents.x, y - extents.y);
if (thumbClicked)
{
uint32_t sbHeight = extents.h - 24,
thumb = (uint32_t)(((float)limit / (float)item.size()) * (float)sbHeight);
//yRelativePoint is the spot on the thumb where we clicked...
int32_t newThumbStart = y - yRelativePoint;
if (newThumbStart < 0)
newThumbStart = 0;
if ((uint32_t)newThumbStart > sbHeight - thumb)
newThumbStart = sbHeight - thumb;
windowPtr = (uint32_t)(((float)newThumbStart / (float)sbHeight) * (float)item.size());
//Check for cursor bounds as well... Or do we need to???
//Actually, we don't...!
}
}
void ListBox::HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown)
{
if (Inside(x, y) && mouseDown)
{
// Why do we have to do this??? (- extents.y?)
// I guess it's because only the Window class has offsetting implemented... !!! FIX !!!
// cursor = (y - extents.y) / 8;
cursor = (y - extents.y) / FONT_HEIGHT;
}
// Check for a hit on the scrollbar...
if (x > (uint32_t)(extents.x + extents.w) && x <= (uint32_t)(extents.x + extents.w + 8)
&& y > (uint32_t)(extents.y + 8) && y <= (uint32_t)(extents.y + extents.h - 16))
{
if (mouseDown)
{
// This shiaut should be calculated in AddItem(), not here... (or in Draw() for that matter)
uint32_t sbHeight = extents.h - 24,
thumb = (uint32_t)(((float)limit / (float)item.size()) * (float)sbHeight),
thumbStart = (uint32_t)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
// Did we hit the thumb?
if (y >= (extents.y + 8 + thumbStart) && y < (extents.y + 8 + thumbStart + thumb))
thumbClicked = true, yRelativePoint = y - thumbStart;
}
//Seems that this is useless--never reached except in rare cases and that the code outside is
//more effective...
// else
// thumbClicked = false;
}
if (!mouseDown)
thumbClicked = false;
upArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
downArrow.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
upArrow2.HandleMouseButton(x - extents.x, y - extents.y, mouseDown);
}
void ListBox::Draw(uint32_t offsetX/*= 0*/, uint32_t offsetY/*= 0*/)
{
for(uint32_t i=0; i<limit; i++)
{
// Strip off the extension
// (extension stripping should be an option, not default!)
string s(item[windowPtr + i], 0, item[windowPtr + i].length() - 4);
// DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*8,
DrawString(screenBuffer, extents.x + offsetX, extents.y + offsetY + i*FONT_HEIGHT,
(cursor == i ? true : false), "%-*.*s", charWidth, charWidth, s.c_str());
}
upArrow.Draw(extents.x + offsetX, extents.y + offsetY);
downArrow.Draw(extents.x + offsetX, extents.y + offsetY);
upArrow2.Draw(extents.x + offsetX, extents.y + offsetY);
uint32_t sbHeight = extents.h - 24,
thumb = (uint32_t)(((float)limit / (float)item.size()) * (float)sbHeight),
thumbStart = (uint32_t)(((float)windowPtr / (float)item.size()) * (float)sbHeight);
for(uint32_t y=extents.y+offsetY+8; y<extents.y+offsetY+extents.h-16; y++)
{
// for(uint32_t x=extents.x+offsetX+extents.w-8; x<extents.x+offsetX+extents.w; x++)
for(uint32_t x=extents.x+offsetX+extents.w; x<extents.x+offsetX+extents.w+8; x++)
{
if (y >= thumbStart + (extents.y+offsetY+8) && y < thumbStart + thumb + (extents.y+offsetY+8))
// screenBuffer[x + (y * pitch)] = (thumbClicked ? 0x458E : 0xFFFF);
//458E -> 01 0001 0 1100 0 1110 -> 0100 0101 0110 0011 0111 0011 -> 45 63 73
screenBuffer[x + (y * pitch)] = (thumbClicked ? 0xFF736345 : 0xFFFFFFFF);
else
// screenBuffer[x + (y * pitch)] = 0x0200;
//0200 -> 000000 10000 00000 -> 00 1000 0100 00
screenBuffer[x + (y * pitch)] = 0xFF008400;
}
}
}
void ListBox::Notify(Element * e)
{
if (e == &upArrow || e == &upArrow2)
{
if (windowPtr != 0)
{
windowPtr--;
if (cursor < limit - 1)
cursor++;
}
}
else if (e == &downArrow)
{
if (windowPtr < item.size() - limit)
{
windowPtr++;
if (cursor != 0)
cursor--;
}
}
}
void ListBox::AddItem(string s)
{
// Do a simple insertion sort
bool inserted = false;
for(vector<string>::iterator i=item.begin(); i<item.end(); i++)
{
if (stringCmpi(s, *i) == -1)
{
item.insert(i, s);
inserted = true;
break;
}
}
if (!inserted)
item.push_back(s);
limit = (item.size() > charHeight ? charHeight : item.size());
}
string ListBox::GetSelectedItem(void)
{
return item[windowPtr + cursor];
}
//
// FileList class
//
class FileList: public Window
{
public:
FileList(uint32_t x = 0, uint32_t y = 0, uint32_t w = 0, uint32_t h = 0);
virtual ~FileList() {}
virtual void HandleKey(SDL_Scancode key);
virtual void HandleMouseMove(uint32_t x, uint32_t y) { Window::HandleMouseMove(x, y); }
virtual void HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown) { Window::HandleMouseButton(x, y, mouseDown); }
virtual void Draw(uint32_t offsetX = 0, uint32_t offsetY = 0) { Window::Draw(offsetX, offsetY); }
virtual void Notify(Element * e);
protected:
ListBox * files;
Button * load;
};
//Need 4 buttons, one scrollbar...
FileList::FileList(uint32_t x, uint32_t y, uint32_t w, uint32_t h): Window(x, y, w, h)
{
files = new ListBox(8, 8, w - 16, h - 32);
AddElement(files);
load = new Button(8, h - 16, " Load ");
AddElement(load);
load->SetNotificationElement(this);
//!!! FIX !!! Directory might not exist--this shouldn't cause VJ to crash!
DIR * dp = opendir(vjs.ROMPath);
dirent * de;
if (dp != NULL)
{
while ((de = readdir(dp)) != NULL)
{
char * ext = strrchr(de->d_name, '.');
if (ext != NULL)
if (strcasecmp(ext, ".zip") == 0 || strcasecmp(ext, ".j64") == 0
|| strcasecmp(ext, ".abs") == 0 || strcasecmp(ext, ".jag") == 0
|| strcasecmp(ext, ".rom") == 0)
files->AddItem(string(de->d_name));
}
closedir(dp);
}
else
{
//Give a diagnostic message here so that the (l)user can figure out what went wrong. !!! FIX !!!
}
}
void FileList::HandleKey(SDL_Scancode key)
{
if (key == SDLK_RETURN)
Notify(load);
else
Window::HandleKey(key);
}
void FileList::Notify(Element * e)
{
if (e == load)
{
char filename[MAX_PATH];
strcpy(filename, vjs.ROMPath);
if (strlen(filename) > 0)
if (filename[strlen(filename) - 1] != '/')
strcat(filename, "/");
strcat(filename, files->GetSelectedItem().c_str());
// uint32_t romSize = JaguarLoadROM(jaguar_mainRom, filename);
// JaguarLoadCart(jaguar_mainRom, filename);
if (JaguarLoadFile(filename))
{
SDL_Event event;
event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
SDL_PushEvent(&event);
event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
event.user.data1 = (void *)ResetJaguar;
SDL_PushEvent(&event);
}
else
{
SDL_Event event;
event.type = SDL_USEREVENT, event.user.code = WINDOW_CLOSE;
SDL_PushEvent(&event);
// Handle the error, but don't run...
// Tell the user that we couldn't run their file for some reason... !!! FIX !!!
//how to kludge: Make a function like ResetJaguar which creates the dialog window
}
}
else
Window::Notify(e);
}
//
// Menu class & supporting structs/classes
//
struct NameAction
{
string name;
Window * (* action)(void);
SDL_Scancode hotKey;
NameAction(string n, Window * (* a)(void) = NULL, SDL_Scancode k = SDLK_UNKNOWN): name(n),
action(a), hotKey(k) {}
};
class MenuItems
{
public:
MenuItems(): charLength(0) {}
bool Inside(uint32_t x, uint32_t y)
{ return (x >= (uint32_t)extents.x && x < (uint32_t)(extents.x + extents.w)
&& y >= (uint32_t)extents.y && y < (uint32_t)(extents.y + extents.h) ? true : false); }
string title;
vector<NameAction> item;
uint32_t charLength;
SDL_Rect extents;
};
class Menu: public Element
{
public:
// 1CFF -> 0 001 11 00 111 1 1111
// 421F -> 0 100 00 10 000 1 1111
Menu(uint32_t x = 0, uint32_t y = 0, uint32_t w = 0, uint32_t h = FONT_HEIGHT,
/* uint16_t fgc = 0x1CFF, uint16_t bgc = 0x000F, uint16_t fgch = 0x421F,
uint16_t bgch = 0x1CFF): Element(x, y, w, h), activated(false), clicked(false),*/
/* uint32_t fgc = 0xFF3F3F00, uint32_t bgc = 0x7F000000, uint32_t fgch = 0xFF878700,
uint32_t bgch = 0xFF3F3F00): Element(x, y, w, h), activated(false), clicked(false),*/
/* uint32_t fgc = 0xFFFF3F3F, uint32_t bgc = 0xFF7F0000, uint32_t fgch = 0xFFFF8787,
uint32_t bgch = 0xFFFF3F3F): Element(x, y, w, h), activated(false), clicked(false),*/
uint32_t fgc = 0xFF7F0000, uint32_t bgc = 0xFFFF3F3F, uint32_t fgch = 0xFFFF3F3F,
uint32_t bgch = 0xFFFF8787): Element(x, y, w, h), activated(false), clicked(false),
inside(0), insidePopup(0), fgColor(fgc), bgColor(bgc), fgColorHL(fgch),
bgColorHL(bgch), menuChosen(-1), menuItemChosen(-1) {}
virtual void HandleKey(SDL_Scancode key);
virtual void HandleMouseMove(uint32_t x, uint32_t y);
virtual void HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown);
virtual void Draw(uint32_t offsetX = 0, uint32_t offsetY = 0);
virtual void Notify(Element *) {}
void Add(MenuItems mi);
protected:
bool activated, clicked;
uint32_t inside, insidePopup;
// uint16_t fgColor, bgColor, fgColorHL, bgColorHL;
uint32_t fgColor, bgColor, fgColorHL, bgColorHL;
int menuChosen, menuItemChosen;
private:
vector<MenuItems> itemList;
};
void Menu::HandleKey(SDL_Scancode key)
{
for(uint32_t i=0; i<itemList.size(); i++)
{
for(uint32_t j=0; j<itemList[i].item.size(); j++)
{
if (itemList[i].item[j].hotKey == key)
{
SDL_Event event;
event.type = SDL_USEREVENT;
event.user.code = MENU_ITEM_CHOSEN;
event.user.data1 = (void *)itemList[i].item[j].action;
SDL_PushEvent(&event);
clicked = false, menuChosen = menuItemChosen = -1;
break;
}
}
}
}
void Menu::HandleMouseMove(uint32_t x, uint32_t y)
{
inside = insidePopup = 0;
if (Inside(x, y))
{
// Find out *where* we are inside the menu bar
uint32_t xpos = extents.x;
for(uint32_t i=0; i<itemList.size(); i++)
{
uint32_t width = (itemList[i].title.length() + 2) * FONT_WIDTH;
if (x >= xpos && x < xpos + width)
{
inside = i + 1;
menuChosen = i;
break;
}
xpos += width;
}
}
if (!Inside(x, y) && !clicked)
{
menuChosen = -1;
}
if (itemList[menuChosen].Inside(x, y) && clicked)
{
insidePopup = ((y - itemList[menuChosen].extents.y) / FONT_HEIGHT) + 1;
menuItemChosen = insidePopup - 1;
}
}
void Menu::HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown)
{
if (!clicked)
{
if (mouseDown)
{
if (inside)
clicked = true;
else
menuChosen = -1; // clicked is already false...!
}
}
else // clicked == true
{
if (insidePopup && !mouseDown) // I.e., mouse-button-up
{
activated = true;
if (itemList[menuChosen].item[menuItemChosen].action != NULL)
{
// itemList[menuChosen].item[menuItemChosen].action();
SDL_Event event;
event.type = SDL_USEREVENT;
event.user.code = MENU_ITEM_CHOSEN;
event.user.data1 = (void *)itemList[menuChosen].item[menuItemChosen].action;
SDL_PushEvent(&event);
clicked = false, menuChosen = menuItemChosen = -1;
/* SDL_Event event;
while (SDL_PollEvent(&event)); // Flush the event queue...
event.type = SDL_MOUSEMOTION;
int mx, my;
SDL_GetMouseState(&mx, &my);
event.motion.x = mx, event.motion.y = my;
SDL_PushEvent(&event); // & update mouse position...!
*/ }
}
if (!inside && !insidePopup && mouseDown)
clicked = false, menuChosen = menuItemChosen = -1;
}
}
void Menu::Draw(uint32_t offsetX/*= 0*/, uint32_t offsetY/*= 0*/)
{
uint32_t xpos = extents.x + offsetX;
for(uint32_t i=0; i<itemList.size(); i++)
{
// uint16_t color1 = fgColor, color2 = bgColor;
uint32_t color1 = fgColor, color2 = bgColor;
if (inside == (i + 1) || (menuChosen != -1 && (uint32_t)menuChosen == i))
color1 = fgColorHL, color2 = bgColorHL;
DrawStringOpaque(screenBuffer, xpos, extents.y + offsetY, color1, color2,
" %s ", itemList[i].title.c_str());
xpos += (itemList[i].title.length() + 2) * FONT_WIDTH;
}
// Draw sub menu (but only if active)
if (clicked)
{
uint32_t ypos = extents.y + FONT_HEIGHT + 1;
for(uint32_t i=0; i<itemList[menuChosen].item.size(); i++)
{
// uint16_t color1 = fgColor, color2 = bgColor;
uint32_t color1 = fgColor, color2 = bgColor;
if (insidePopup == i + 1)
color1 = fgColorHL, color2 = bgColorHL, menuItemChosen = i;
if (itemList[menuChosen].item[i].name.length() > 0)
DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
color1, color2, " %-*.*s ", itemList[menuChosen].charLength,
itemList[menuChosen].charLength, itemList[menuChosen].item[i].name.c_str());
else
DrawStringOpaque(screenBuffer, itemList[menuChosen].extents.x, ypos,
fgColor, bgColor, "%.*s", itemList[menuChosen].charLength + 2, separator);
ypos += FONT_HEIGHT;
}
}
}
void Menu::Add(MenuItems mi)
{
for(uint32_t i=0; i<mi.item.size(); i++)
if (mi.item[i].name.length() > mi.charLength)
mi.charLength = mi.item[i].name.length();
// Set extents here as well...
mi.extents.x = extents.x + extents.w, mi.extents.y = extents.y + FONT_HEIGHT + 1;
mi.extents.w = (mi.charLength + 2) * FONT_WIDTH, mi.extents.h = mi.item.size() * FONT_HEIGHT;
itemList.push_back(mi);
extents.w += (mi.title.length() + 2) * FONT_WIDTH;
}
//Do we even *need* this?
//Doesn't seem like it...
/*class RootWindow: public Window
{
public:
RootWindow(Menu * m, Window * w = NULL): menu(m), window(w) {}
//Do we even need to care about this crap?
// { extents.x = extents.y = 0, extents.w = 320, extents.h = 240; }
virtual void HandleKey(SDL_Scancode key) {}
virtual void HandleMouseMove(uint32_t x, uint32_t y) {}
virtual void HandleMouseButton(uint32_t x, uint32_t y, bool mouseDown) {}
virtual void Draw(uint32_t offsetX = 0, uint32_t offsetY = 0) {}
virtual void Notify(Element *) {}
private:
Menu * menu;
Window * window;
int16_t * rootImage[1280 * 240 * 2];
};//*/
//
// Draw text at the given x/y coordinates. Can invert text as well.
//
void DrawString(uint32_t * screen, uint32_t x, uint32_t y, bool invert, const char * text, ...)
{
char string[4096];
va_list arg;
va_start(arg, text);
vsprintf(string, text, arg);
va_end(arg);
uint32_t pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
uint32_t length = strlen(string), address = x + (y * pitch);
uint32_t color1 = 0x0080FF;
uint8_t nBlue = (color1 >> 16) & 0xFF, nGreen = (color1 >> 8) & 0xFF, nRed = color1 & 0xFF;
uint8_t xorMask = (invert ? 0xFF : 0x00);
for(uint32_t i=0; i<length; i++)
{
uint8_t c = string[i];
uint32_t fontAddr = (uint32_t)(c < 32 ? 0 : c - 32) * FONT_WIDTH * FONT_HEIGHT;
for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
{
for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
{
uint32_t existingColor = *(screen + address + xx + (yy * pitch));
uint8_t eBlue = (existingColor >> 16) & 0xFF,
eGreen = (existingColor >> 8) & 0xFF,
eRed = existingColor & 0xFF;
uint8_t trans = font2[fontAddr] ^ xorMask;
uint8_t invTrans = trans ^ 0xFF;
uint32_t bRed = (eRed * invTrans + nRed * trans) / 255,
bGreen = (eGreen * invTrans + nGreen * trans) / 255,
bBlue = (eBlue * invTrans + nBlue * trans) / 255;
*(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
fontAddr++;
}
}
address += FONT_WIDTH;
}
}
//
// Draw text at the given x/y coordinates, using FG/BG colors.
//
void DrawStringOpaque(uint32_t * screen, uint32_t x, uint32_t y, uint32_t color1, uint32_t color2, const char * text, ...)
{
char string[4096];
va_list arg;
va_start(arg, text);
vsprintf(string, text, arg);
va_end(arg);
uint32_t pitch = sdlemuGetOverlayWidthInPixels();
uint32_t length = strlen(string), address = x + (y * pitch);
uint8_t eBlue = (color2 >> 16) & 0xFF, eGreen = (color2 >> 8) & 0xFF, eRed = color2 & 0xFF,
nBlue = (color1 >> 16) & 0xFF, nGreen = (color1 >> 8) & 0xFF, nRed = color1 & 0xFF;
for(uint32_t i=0; i<length; i++)
{
uint8_t c = string[i];
c = (c < 32 ? 0 : c - 32);
uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
{
for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
{
uint8_t trans = font2[fontAddr++];
uint8_t invTrans = trans ^ 0xFF;
uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
*(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
}
}
address += FONT_WIDTH;
}
}
//
// Draw text at the given x/y coordinates with transparency (0 is fully opaque, 32 is fully transparent).
//
void DrawStringTrans(uint32_t * screen, uint32_t x, uint32_t y, uint32_t color, uint8_t trans, const char * text, ...)
{
char string[4096];
va_list arg;
va_start(arg, text);
vsprintf(string, text, arg);
va_end(arg);
uint32_t pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
uint32_t length = strlen(string), address = x + (y * pitch);
for(uint32_t i=0; i<length; i++)
{
uint32_t fontAddr = (uint32_t)string[i] * 64;
for(uint32_t yy=0; yy<8; yy++)
{
for(uint32_t xx=0; xx<8; xx++)
{
if (font1[fontAddr])
{
uint32_t existingColor = *(screen + address + xx + (yy * pitch));
uint8_t eBlue = (existingColor >> 16) & 0xFF,
eGreen = (existingColor >> 8) & 0xFF,
eRed = existingColor & 0xFF,
//This could be done ahead of time, instead of on each pixel...
nBlue = (color >> 16) & 0xFF,
nGreen = (color >> 8) & 0xFF,
nRed = color & 0xFF;
//This could be sped up by using a table of 5 + 5 + 5 bits (32 levels transparency -> 32768 entries)
//Here we've modified it to have 33 levels of transparency (could have any # we want!)
//because dividing by 32 is faster than dividing by 31...!
uint8_t invTrans = 32 - trans;
uint32_t bRed = (eRed * trans + nRed * invTrans) / 32;
uint32_t bGreen = (eGreen * trans + nGreen * invTrans) / 32;
uint32_t bBlue = (eBlue * trans + nBlue * invTrans) / 32;
*(screen + address + xx + (yy * pitch)) = 0xFF000000 | (bBlue << 16) | (bGreen << 8) | bRed;
}
fontAddr++;
}
}
address += 8;
}
}
//
// Draw text at the given x/y coordinates, using FG color and overlay alpha blending.
//
void DrawString2(uint32_t * screen, uint32_t x, uint32_t y, uint32_t color, uint8_t transparency, const char * text, ...)
{
char string[4096];
va_list arg;
va_start(arg, text);
vsprintf(string, text, arg);
va_end(arg);
uint32_t pitch = sdlemuGetOverlayWidthInPixels();
uint32_t length = strlen(string), address = x + (y * pitch);
color &= 0x00FFFFFF; // Just in case alpha was passed in...
for(uint32_t i=0; i<length; i++)
{
uint8_t c = string[i];
c = (c < 32 ? 0 : c - 32);
uint32_t fontAddr = (uint32_t)c * FONT_WIDTH * FONT_HEIGHT;
for(uint32_t yy=0; yy<FONT_HEIGHT; yy++)
{
for(uint32_t xx=0; xx<FONT_WIDTH; xx++)
{
uint8_t fontTrans = font2[fontAddr++];
uint32_t newTrans = (fontTrans * transparency / 255) << 24;
uint32_t pixel = newTrans | color;
*(screen + address + xx + (yy * pitch)) = pixel;
}
}
address += FONT_WIDTH;
}
}
//
// Draw "picture"
// Uses zero as transparent color
// Can also use an optional alpha channel
// Alpha channel is now mandatory! ;-)
//
//void DrawTransparentBitmap(int16_t * screen, uint32_t x, uint32_t y, uint16_t * bitmap, uint8_t * alpha/*=NULL*/)
/*void DrawTransparentBitmap(uint32_t * screen, uint32_t x, uint32_t y, uint32_t * bitmap, uint8_t * alpha)
{
uint32_t width = bitmap[0], height = bitmap[1];
bitmap += 2;
// uint32_t pitch = GetSDLScreenPitch() / 2; // Returns pitch in bytes but we need words...
uint32_t pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
uint32_t address = x + (y * pitch);
for(uint32_t yy=0; yy<height; yy++)
{
for(uint32_t xx=0; xx<width; xx++)
{
if (alpha == NULL)
{
if (*bitmap && x + xx < pitch) // NOTE: Still doesn't clip the Y val...
*(screen + address + xx + (yy * pitch)) = *bitmap;
}
else
{
uint8_t trans = *alpha;
uint32_t color = *bitmap;
uint32_t existingColor = *(screen + address + xx + (yy * pitch));
uint8_t eRed = existingColor & 0xFF,
eGreen = (existingColor >> 8) & 0xFF,
eBlue = (existingColor >> 16) & 0xFF,
nRed = color & 0xFF,
nGreen = (color >> 8) & 0xFF,
nBlue = (color >> 16) & 0xFF;
uint8_t invTrans = 255 - trans;
uint32_t bRed = (eRed * trans + nRed * invTrans) / 255;
uint32_t bGreen = (eGreen * trans + nGreen * invTrans) / 255;
uint32_t bBlue = (eBlue * trans + nBlue * invTrans) / 255;
uint32_t blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
*(screen + address + xx + (yy * pitch)) = blendedColor;
alpha++;
}
bitmap++;
}
}
}*/
void DrawTransparentBitmapDeprecated(uint32_t * screen, uint32_t x, uint32_t y, uint32_t * bitmap)
{
uint32_t width = bitmap[0], height = bitmap[1];
bitmap += 2;
uint32_t pitch = sdlemuGetOverlayWidthInPixels();//GetSDLScreenWidthInPixels();
uint32_t address = x + (y * pitch);
for(uint32_t yy=0; yy<height; yy++)
{
for(uint32_t xx=0; xx<width; xx++)
{
uint32_t color = *bitmap;
uint32_t blendedColor = color;
uint32_t existingColor = *(screen + address + xx + (yy * pitch));
if (existingColor >> 24 != 0x00) // Pixel needs blending
{
uint8_t trans = color >> 24;
uint8_t invTrans = trans ^ 0xFF;//255 - trans;
uint8_t eRed = existingColor & 0xFF,
eGreen = (existingColor >> 8) & 0xFF,
eBlue = (existingColor >> 16) & 0xFF,
nRed = color & 0xFF,
nGreen = (color >> 8) & 0xFF,
nBlue = (color >> 16) & 0xFF;
uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
}
*(screen + address + xx + (yy * pitch)) = blendedColor;
bitmap++;
}
}
}
void DrawTransparentBitmap(uint32_t * screen, uint32_t x, uint32_t y, const void * bitmap)
{
uint32_t pitch = sdlemuGetOverlayWidthInPixels();
uint32_t address = x + (y * pitch);
uint32_t count = 0;
for(uint32_t yy=0; yy<((Bitmap *)bitmap)->height; yy++)
{
for(uint32_t xx=0; xx<((Bitmap *)bitmap)->width; xx++)
{
uint32_t color = ((uint32_t *)((Bitmap *)bitmap)->pixelData)[count];
uint32_t blendedColor = color;
uint32_t existingColor = *(screen + address + xx + (yy * pitch));
if (existingColor >> 24 != 0x00) // Pixel needs blending
{
uint8_t trans = color >> 24;
uint8_t invTrans = trans ^ 0xFF;
uint8_t eRed = existingColor & 0xFF,
eGreen = (existingColor >> 8) & 0xFF,
eBlue = (existingColor >> 16) & 0xFF,
nRed = color & 0xFF,
nGreen = (color >> 8) & 0xFF,
nBlue = (color >> 16) & 0xFF;
uint32_t bRed = (eRed * invTrans + nRed * trans) / 255;
uint32_t bGreen = (eGreen * invTrans + nGreen * trans) / 255;
uint32_t bBlue = (eBlue * invTrans + nBlue * trans) / 255;
// Instead of $FF, should use the alpha from the destination pixel as the final alpha value...
blendedColor = 0xFF000000 | bRed | (bGreen << 8) | (bBlue << 16);
}
*(screen + address + xx + (yy * pitch)) = blendedColor;
count++;
}
}
}
//
// Draw a bitmap without using blending
//
void DrawBitmap(uint32_t * screen, uint32_t x, uint32_t y, const void * bitmap)
{
uint32_t pitch = sdlemuGetOverlayWidthInPixels();
uint32_t address = x + (y * pitch);
uint32_t count = 0;
for(uint32_t yy=0; yy<((Bitmap *)bitmap)->height; yy++)
{
for(uint32_t xx=0; xx<((Bitmap *)bitmap)->width; xx++)
{
*(screen + address + xx + (yy * pitch)) = ((uint32_t *)((Bitmap *)bitmap)->pixelData)[count];
count++;
}
}
}
//
// Fill a portion of the screen with the passed in color
//
void FillScreenRectangle(uint32_t * screen, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color)
//void ClearScreenRectangle(uint32_t * screen, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
uint32_t pitch = sdlemuGetOverlayWidthInPixels();
uint32_t address = x + (y * pitch);
for(uint32_t yy=0; yy<h; yy++)
for(uint32_t xx=0; xx<w; xx++)
*(screen + address + xx + (yy * pitch)) = color;
}
//
// GUI stuff--it's not crunchy, it's GUI! ;-)
//
void InitGUI(void)
{
SDL_ShowCursor(SDL_DISABLE);
SDL_GetMouseState(&mouseX, &mouseY);
}
void GUIDone(void)
{
}
//
// GUI main loop
//
//bool GUIMain(void)
bool GUIMain(char * filename)
{
WriteLog("GUI: Inside GUIMain...\n");
uint32_t pointerBGSave[6 * 8 + 2];
pointerBGSave[0] = 6;
pointerBGSave[1] = 8;
// Need to set things up so that it loads and runs a file if given on the command line. !!! FIX !!! [DONE]
extern uint32_t * backbuffer;
// bool done = false;
SDL_Event event;
Window * mainWindow = NULL;
// Set up the GUI classes...
// Element::SetScreenAndPitch(backbuffer, GetSDLScreenWidthInPixels());
Element::SetScreenAndPitch((uint32_t *)sdlemuGetOverlayPixels(), sdlemuGetOverlayWidthInPixels());
sdlemuEnableOverlay();
Menu mainMenu;
MenuItems mi;
mi.title = "Jaguar";
mi.item.push_back(NameAction("Load...", LoadROM, SDLK_l));
mi.item.push_back(NameAction("Reset", ResetJaguar, SDLK_r));
if (CDBIOSLoaded)
mi.item.push_back(NameAction("Reset CD", ResetJaguarCD, SDLK_c));
mi.item.push_back(NameAction("Run", RunEmu, SDLK_ESCAPE));
mi.item.push_back(NameAction(""));
mi.item.push_back(NameAction("Quit", Quit, SDLK_q));
mainMenu.Add(mi);
mi.title = "Settings";
mi.item.clear();
mi.item.push_back(NameAction("Video..."));
mi.item.push_back(NameAction("Audio..."));
mi.item.push_back(NameAction("Misc...", MiscOptions, SDLK_m));
mainMenu.Add(mi);
mi.title = "Info";
mi.item.clear();
mi.item.push_back(NameAction("About...", About));
mainMenu.Add(mi);
bool showMouse = true;
// Grab the BG where the mouse will be painted (prime the backstore)
/*
DISNOWOK
Bitmap ptr = { 6, 8, 4,
""//"000011112222333344445555"
//"000011112222333344445555"
//"000011112222333344445555"
//"000011112222333344445555"
//"000011112222333344445555"
//"000011112222333344445555"
//"000011112222333344445555"
//"000011112222333344445555"
};//*/
uint32_t * overlayPixels = (uint32_t *)sdlemuGetOverlayPixels();
uint32_t count = 2;
for(uint32_t y=0; y<pointerBGSave[1]; y++)
for(uint32_t x=0; x<pointerBGSave[0]; x++)
pointerBGSave[count++] = overlayPixels[((mouseY + y) * sdlemuGetOverlayWidthInPixels()) + (mouseX + x)];
uint32_t oldMouseX = mouseX, oldMouseY = mouseY;
//This is crappy!!! !!! FIX !!!
//Is this even needed any more? Hmm. Maybe. Dunno.
WriteLog("GUI: Resetting Jaguar...\n");
jaguar_reset();
WriteLog("GUI: Clearing BG save...\n");
// Set up our background save...
// memset(background, 0x11, tom_getVideoModeWidth() * 240 * 2);
//1111 -> 000100 01000 10001 -> 0001 0000 0100 0010 1000 1100 -> 10 42 8C
for(uint32_t i=0; i<tom_getVideoModeWidth()*240; i++)
// background[i] = 0xFF8C4210;
backbuffer[i] = 0xFF8C4210;
/* uint32_t * overlayPix = (uint32_t *)sdlemuGetOverlayPixels();
for(uint32_t i=0; i<sdlemuGetOverlayWidthInPixels()*480; i++)
overlayPix[i] = 0x00000000;*/
// Handle loading file passed in on the command line...! [DONE]
if (filename)
{
if (JaguarLoadFile(filename))
{
// event.type = SDL_USEREVENT, event.user.code = MENU_ITEM_CHOSEN;
// event.user.data1 = (void *)ResetJaguar;
// SDL_PushEvent(&event);
// Make it so that if passed in on the command line, we quit right
// away when pressing ESC
WriteLog("GUI: Bypassing GUI since ROM passed in on command line...\n");
ResetJaguar();
return true;
}
else
{
// Create error dialog...
char errText[1024];
sprintf(errText, "The file %40s could not be loaded.", filename);
mainWindow = new Window(8, 16, 304, 160);
mainWindow->AddElement(new Text(8, 8, "Error!"));
mainWindow->AddElement(new Text(8, 24, errText));
}
}
WriteLog("GUI: Entering main loop...\n");
while (!exitGUI)
{
if (SDL_PollEvent(&event))
{
if (event.type == SDL_USEREVENT)
{
if (event.user.code == WINDOW_CLOSE)
{
delete mainWindow;
mainWindow = NULL;
}
else if (event.user.code == MENU_ITEM_CHOSEN)
{
// Confused? Let me enlighten... What we're doing here is casting
// data1 as a pointer to a function which returns a Window pointer and
// which takes no parameters (the "(Window *(*)(void))" part), then
// derefencing it (the "*" in front of that) in order to call the
// function that it points to. Clear as mud? Yeah, I hate function
// pointers too, but what else are you gonna do?
mainWindow = (*(Window *(*)(void))event.user.data1)();
while (SDL_PollEvent(&event)); // Flush the event queue...
event.type = SDL_MOUSEMOTION;
int mx, my;
SDL_GetMouseState(&mx, &my);
event.motion.x = mx, event.motion.y = my;
SDL_PushEvent(&event); // & update mouse position...!
oldMouseX = mouseX, oldMouseY = mouseY;
mouseX = mx, mouseY = my; // This prevents "mouse flash"...
}
}
else if (event.type == SDL_ACTIVEEVENT)
{
if (event.active.state == SDL_APPMOUSEFOCUS)
showMouse = (event.active.gain ? true : false);
}
else if (event.type == SDL_KEYDOWN)
{
if (mainWindow)
mainWindow->HandleKey(event.key.keysym.sym);
else
mainMenu.HandleKey(event.key.keysym.sym);
}
else if (event.type == SDL_MOUSEMOTION)
{
oldMouseX = mouseX, oldMouseY = mouseY;
mouseX = event.motion.x, mouseY = event.motion.y;
if (mainWindow)
mainWindow->HandleMouseMove(mouseX, mouseY);
else
mainMenu.HandleMouseMove(mouseX, mouseY);
}
else if (event.type == SDL_MOUSEBUTTONDOWN)
{
uint32_t mx = event.button.x, my = event.button.y;
if (mainWindow)
mainWindow->HandleMouseButton(mx, my, true);
else
mainMenu.HandleMouseButton(mx, my, true);
}
else if (event.type == SDL_MOUSEBUTTONUP)
{
uint32_t mx = event.button.x, my = event.button.y;
if (mainWindow)
mainWindow->HandleMouseButton(mx, my, false);
else
mainMenu.HandleMouseButton(mx, my, false);
}
//PROBLEM: In order to use the dirty rectangle approach here, we need some way of
// handling it in mainMenu.Draw() and mainWindow->Draw(). !!! FIX !!!
//POSSIBLE SOLUTION:
// When mouse is moving and not on menu or window, can do straight dirty rect.
// When mouse is on menu, need to update screen. Same for buttons on windows...
// What the menu & windows should do is only redraw on a state change. IOW, they
// should call their own/child window's Draw() function instead of doing it top
// level.
//#define NEW_BACKSTORE_METHOD
// Draw the GUI...
// The way we do things here is kinda stupid (redrawing the screen every frame), but
// it's simple. Perhaps there may be a reason down the road to be more selective with
// our clearing, but for now, this will suffice.
// memset(backbuffer, 0x11, tom_getVideoModeWidth() * 240 * 2);
// memcpy(backbuffer, background, tom_getVideoModeWidth() * 256 * 2);
// memcpy(backbuffer, background, tom_getVideoModeWidth() * 256 * 4);
#ifndef NEW_BACKSTORE_METHOD
memset(sdlemuGetOverlayPixels(), 0, sdlemuGetOverlayWidthInPixels() * 480 * 4);
mainMenu.Draw();
//Could do multiple windows here by using a vector + priority info...
//Though the way ZSNES does it seems to be by a bool (i.e., they're always active, just not shown)
if (mainWindow)
mainWindow->Draw();
#endif
/*uint32_t pBGS[6 * 8 + 3] = { 6, 8, 4,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0
};*/
//This isn't working... Why????
//It's because DrawTransparentBitmap does alpha blending if it detects zero in the alpha channel.
//So why do it that way? Hm.
overlayPixels = (uint32_t *)sdlemuGetOverlayPixels();
#ifdef NEW_BACKSTORE_METHOD
// DrawTransparentBitmapDeprecated(overlayPixels, oldMouseX, oldMouseY, pointerBGSave);
// DrawTransparentBitmap(overlayPixels, oldMouseX, oldMouseY, pBGS);
for(uint32_t y=0; y<pointerBGSave[1]; y++)
for(uint32_t x=0; x<pointerBGSave[0]; x++)
overlayPixels[((oldMouseY + y) * sdlemuGetOverlayWidthInPixels()) + (oldMouseX + x)] = 0x00000000;
count = 2;
for(uint32_t y=0; y<pointerBGSave[1]; y++)
for(uint32_t x=0; x<pointerBGSave[0]; x++)
pointerBGSave[count++] = overlayPixels[((mouseY + y) * sdlemuGetOverlayWidthInPixels()) + (mouseX + x)];
#endif
if (showMouse)
// DrawTransparentBitmapDeprecated(backbuffer, mouseX, mouseY, mousePic);
DrawTransparentBitmapDeprecated(overlayPixels, mouseX, mouseY, mousePic);
RenderBackbuffer();
}
}
return true;
}
//
// GUI "action" functions
//
Window * LoadROM(void)
{
FileList * fileList = new FileList(20, 20, 600, 440);
return (Window *)fileList;
}
Window * ResetJaguar(void)
{
jaguar_reset();
return RunEmu();
}
Window * ResetJaguarCD(void)
{
memcpy(jaguar_mainRom, jaguar_CDBootROM, 0x40000);
jaguarRunAddress = 0x802000;
jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, 0x40000);
jaguar_reset();
//This is a quick kludge to get the CDBIOS to boot properly...
//Wild speculation: It could be that this memory location is wired into the CD unit
//somehow, which lets it know whether or not a cart is present in the unit...
jaguar_mainRom[0x0040B] = 0x03;
return RunEmu();
}
#if 0
bool debounceRunKey = true;
Window * RunEmu(void)
{
extern uint32_t * backbuffer;
//Temporary, to test the new timer based code...
sdlemuDisableOverlay();
JaguarExecuteNew();
sdlemuEnableOverlay();
// Save the background for the GUI...
// In this case, we squash the color to monochrome, then force it to blue + green...
for(uint32_t i=0; i<tom_getVideoModeWidth() * 256; i++)
{
uint32_t pixel = backbuffer[i];
uint8_t b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
pixel = ((r + g + b) / 3) & 0x00FF;
backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
}
return NULL;//*/
//This is crappy... !!! FIX !!!
extern bool finished, showGUI;
// uint32_t nFrame = 0, nFrameskip = 0;
uint32_t totalFrames = 0;
finished = false;
bool showMessage = true;
uint32_t showMsgFrames = 120;
uint8_t transparency = 0;
// Pass a message to the "joystick" code to debounce the ESC key...
debounceRunKey = true;
uint32_t cartType = 4;
if (jaguarRomSize == 0x200000)
cartType = 0;
else if (jaguarRomSize == 0x400000)
cartType = 1;
else if (jaguar_mainRom_crc32 == 0x687068D5)
cartType = 2;
else if (jaguar_mainRom_crc32 == 0x55A0669C)
cartType = 3;
char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
uint32_t elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
while (true)
{
// Set up new backbuffer with new pixels and data
JaguarExecute(backbuffer, true);
// JaguarExecuteNew();
totalFrames++;
//WriteLog("Frame #%u...\n", totalFrames);
//extern bool doDSPDis;
//if (totalFrames == 373)
// doDSPDis = true;
//This sucks... !!! FIX !!!
joystick_exec();
//This is done here so that the crud below doesn't get on our GUI background...
if (finished)
break;
// Some QnD GUI stuff here...
if (showGUI)
{
extern uint32_t gpu_pc, dsp_pc;
DrawString(backbuffer, 8, 8, false, "GPU PC: %08X", gpu_pc);
DrawString(backbuffer, 8, 16, false, "DSP PC: %08X", dsp_pc);
DrawString(backbuffer, 8, 32, false, "%u FPS", framesPerSecond);
}
if (showMessage)
{
// FF0F -> 1111 11 11 000 0 1111 -> 3F 18 0F
// 3FE3 -> 0011 11 11 111 0 0011 -> 0F 3F 03
/* DrawStringTrans((uint32_t *)backbuffer, 8, 24*8, 0xFF0F, transparency, "Running...");
DrawStringTrans((uint32_t *)backbuffer, 8, 26*8, 0x3FE3, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
DrawStringTrans((uint32_t *)backbuffer, 8, 27*8, 0x3FE3, transparency, "CRC: %08X", jaguar_mainRom_crc32);//*/
//first has wrong color. !!! FIX !!!
DrawStringTrans(backbuffer, 8, 24*8, 0xFF7F63FF, transparency, "Running...");
DrawStringTrans(backbuffer, 8, 26*8, 0xFF1FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
DrawStringTrans(backbuffer, 8, 27*8, 0xFF1FFF3F, transparency, "CRC: %08X", jaguar_mainRom_crc32);
if (showMsgFrames == 0)
{
transparency++;
if (transparency == 33)
{
showMessage = false;
/*extern bool doGPUDis;
doGPUDis = true;//*/
}
}
else
showMsgFrames--;
}
RenderBackbuffer();
frameCount++;
if (SDL_GetTicks() - elapsedTicks > 250)
elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
}
// Reset the pitch, since it may have been changed in-game...
Element::SetScreenAndPitch((uint32_t *)backbuffer, GetSDLScreenWidthInPixels());
// Save the background for the GUI...
// memcpy(background, backbuffer, tom_getVideoModeWidth() * 240 * 2);
// In this case, we squash the color to monochrome, then force it to blue + green...
for(uint32_t i=0; i<tom_getVideoModeWidth() * 256; i++)
{
uint32_t pixel = backbuffer[i];
uint8_t b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
pixel = ((r + g + b) / 3) & 0x00FF;
background[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
}
return NULL;
}
#else
bool debounceRunKey = true;
Window * RunEmu(void)
{
extern uint32_t * backbuffer;
uint32_t * overlayPixels = (uint32_t *)sdlemuGetOverlayPixels();
memset(overlayPixels, 0x00, 640 * 480 * 4); // Clear out overlay...
//This is crappy... !!! FIX !!!
extern bool finished, showGUI;
sdlemuDisableOverlay();
// uint32_t nFrame = 0, nFrameskip = 0;
uint32_t totalFrames = 0;
finished = false;
bool showMessage = true;
uint32_t showMsgFrames = 120;
uint8_t transparency = 0xFF;
// Pass a message to the "joystick" code to debounce the ESC key...
debounceRunKey = true;
uint32_t cartType = 4;
if (jaguarRomSize == 0x200000)
cartType = 0;
else if (jaguarRomSize == 0x400000)
cartType = 1;
else if (jaguar_mainRom_crc32 == 0x687068D5)
cartType = 2;
else if (jaguar_mainRom_crc32 == 0x55A0669C)
cartType = 3;
char * cartTypeName[5] = { "2M Cartridge", "4M Cartridge", "CD BIOS", "CD Dev BIOS", "Homebrew" };
uint32_t elapsedTicks = SDL_GetTicks(), frameCount = 0, framesPerSecond = 0;
while (!finished)
{
// Set up new backbuffer with new pixels and data
JaguarExecuteNew();
totalFrames++;
//WriteLog("Frame #%u...\n", totalFrames);
//extern bool doDSPDis;
//if (totalFrames == 373)
// doDSPDis = true;
//Problem: Need to do this *only* when the state changes from visible to not...
//Also, need to clear out the GUI when not on (when showMessage is active...)
if (showGUI || showMessage)
sdlemuEnableOverlay();
else
sdlemuDisableOverlay();
//Add in a new function for clearing patches of screen (ClearOverlayRect)
// Some QnD GUI stuff here...
if (showGUI)
{
FillScreenRectangle(overlayPixels, 8, 1*FONT_HEIGHT, 128, 4*FONT_HEIGHT, 0x00000000);
extern uint32_t gpu_pc, dsp_pc;
DrawString(overlayPixels, 8, 1*FONT_HEIGHT, false, "GPU PC: %08X", gpu_pc);
DrawString(overlayPixels, 8, 2*FONT_HEIGHT, false, "DSP PC: %08X", dsp_pc);
DrawString(overlayPixels, 8, 4*FONT_HEIGHT, false, "%u FPS", framesPerSecond);
}
if (showMessage)
{
DrawString2(overlayPixels, 8, 24*FONT_HEIGHT, 0x007F63FF, transparency, "Running...");
DrawString2(overlayPixels, 8, 26*FONT_HEIGHT, 0x001FFF3F, transparency, "%s, run address: %06X", cartTypeName[cartType], jaguarRunAddress);
DrawString2(overlayPixels, 8, 27*FONT_HEIGHT, 0x001FFF3F, transparency, "CRC: %08X", jaguar_mainRom_crc32);
if (showMsgFrames == 0)
{
transparency--;
if (transparency == 0)
{
showMessage = false;
/*extern bool doGPUDis;
doGPUDis = true;//*/
}
}
else
showMsgFrames--;
}
frameCount++;
if (SDL_GetTicks() - elapsedTicks > 250)
elapsedTicks += 250, framesPerSecond = frameCount * 4, frameCount = 0;
}
// Save the background for the GUI...
// In this case, we squash the color to monochrome, then force it to blue + green...
for(uint32_t i=0; i<tom_getVideoModeWidth() * 256; i++)
{
uint32_t pixel = backbuffer[i];
uint8_t b = (pixel >> 16) & 0xFF, g = (pixel >> 8) & 0xFF, r = pixel & 0xFF;
pixel = ((r + g + b) / 3) & 0x00FF;
backbuffer[i] = 0xFF000000 | (pixel << 16) | (pixel << 8);
}
sdlemuEnableOverlay();
return NULL;
}
#endif
Window * Quit(void)
{
WriteLog("GUI: Quitting due to user request.\n");
exitGUI = true;
return NULL;
}
Window * About(void)
{
char buf[512];
// sprintf(buf, "Virtual Jaguar CVS %s", __DATE__);
sprintf(buf, "CVS %s", __DATE__);
//fprintf(fp, "VirtualJaguar v1.0.8 (Last full build was on %s %s)\n", __DATE__, __TIME__);
//VirtualJaguar v1.0.8 (Last full build was on Dec 30 2004 20:01:31)
//Hardwired, bleh... !!! FIX !!!
uint32_t width = 55 * FONT_WIDTH, height = 18 * FONT_HEIGHT;
uint32_t xpos = (640 - width) / 2, ypos = (480 - height) / 2;
// Window * window = new Window(8, 16, 50 * FONT_WIDTH, 21 * FONT_HEIGHT);
Window * window = new Window(xpos, ypos, width, height);
// window->AddElement(new Text(8, 8, "Virtual Jaguar 1.0.8"));
// window->AddElement(new Text(8, 8, "Virtual Jaguar CVS 20050110", 0xFF3030FF, 0xFF000000));
// window->AddElement(new Text(208, 8+0*FONT_HEIGHT, buf, 0xFF3030FF, 0xFF000000));
window->AddElement(new Text(248, 8+4*FONT_HEIGHT+5, buf, 0xFF3030FF, 0xFF000000));
window->AddElement(new Text(8, 8+0*FONT_HEIGHT, "Coders:"));
window->AddElement(new Text(16, 8+1*FONT_HEIGHT, "James L. Hammons (shamus)"));
window->AddElement(new Text(16, 8+2*FONT_HEIGHT, "Niels Wagenaar (nwagenaar)"));
window->AddElement(new Text(16, 8+3*FONT_HEIGHT, "Carwin Jones (Caz)"));
window->AddElement(new Text(16, 8+4*FONT_HEIGHT, "Adam Green"));
window->AddElement(new Text(8, 8+6*FONT_HEIGHT, "Testers:"));
window->AddElement(new Text(16, 8+7*FONT_HEIGHT, "Guruma"));
window->AddElement(new Text(8, 8+9*FONT_HEIGHT, "Thanks go out to:"));
window->AddElement(new Text(16, 8+10*FONT_HEIGHT, "Aaron Giles for the original CoJag"));
window->AddElement(new Text(16, 8+11*FONT_HEIGHT, "David Raingeard for the original VJ"));
window->AddElement(new Text(16, 8+12*FONT_HEIGHT, "Karl Stenerud for his Musashi 68K emu"));
window->AddElement(new Text(16, 8+13*FONT_HEIGHT, "Sam Lantinga for his amazing SDL libs"));
window->AddElement(new Text(16, 8+14*FONT_HEIGHT, "Ryan C. Gordon for VJ's web presence"));
window->AddElement(new Text(16, 8+15*FONT_HEIGHT, "Curt Vendel for various Jaguar goodies"));
window->AddElement(new Text(16, 8+16*FONT_HEIGHT, "The guys over at Atari Age ;-)"));
// window->AddElement(new Image(8, 8, &vj_title_small));
window->AddElement(new Image(width - (vj_title_small.width + 8), 8, &vj_title_small));
return window;
}
Window * MiscOptions(void)
{
Window * window = new Window(8, 16, 304, 192);
window->AddElement(new PushButton(8, 8, &vjs.useJaguarBIOS, "BIOS"));
window->AddElement(new SlideSwitch(8, 32, &vjs.hardwareTypeNTSC, "PAL", "NTSC"));
window->AddElement(new PushButton(8, 64, &vjs.DSPEnabled, "DSP"));
window->AddElement(new SlideSwitch(24, 88, &vjs.usePipelinedDSP, "Original", "Pipelined"));
window->AddElement(new SlideSwitch(8, 120, (bool *)&vjs.glFilter, "Sharp", "Blurry"));
window->AddElement(new SlideSwitch(8, 152, (bool *)&vjs.renderType, "Normal render", "TV style"));
window->AddElement(new TextEdit(88, 8, vjs.ROMPath, 20, 0xFF8484FF, 0xFF000000));
/*TextEdit(uint32_t x, uint32_t y, string s, uint32_t mss = 10, uint32_t fg = 0xFF8484FF,
uint32_t bg = 0xFF84FF4D): Element(x, y, 0, 0), fgColor(fg), bgColor(bg), text(s),
caretPos(0), maxScreenSize(mss) {}*/
// Missing:
// * BIOS path
// * ROM path
// * EEPROM path
// * joystick
// * joystick port
// * OpenGL?
// * GL Filter type
// * Window/fullscreen
// * Key definitions
return window;
}
//
// Generic ROM loading
//
uint32_t JaguarLoadROM(uint8_t * rom, char * path)
{
// We really should have some kind of sanity checking for the ROM size here to prevent
// a buffer overflow... !!! FIX !!!
uint32_t romSize = 0;
WriteLog("JaguarLoadROM: Attempting to load file '%s'...", path);
char * ext = strrchr(path, '.');
if (ext == NULL)
WriteLog("FAILED!\n");
else
WriteLog("Succeeded in finding extension (%s)!\n", ext);
if (ext != NULL)
{
WriteLog("VJ: Loading \"%s\"...", path);
if (strcasecmp(ext, ".zip") == 0)
{
// Handle ZIP file loading here...
WriteLog("(ZIPped)...");
if (load_zipped_file(0, 0, path, NULL, &rom, &romSize) == -1)
{
WriteLog("Failed!\n");
return 0;
}
}
else
{
/* FILE * fp = fopen(path, "rb");
if (fp == NULL)
{
WriteLog("Failed!\n");
return 0;
}
fseek(fp, 0, SEEK_END);
romSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
fread(rom, 1, romSize, fp);
fclose(fp);*/
// Handle gzipped files transparently [Adam Green]...
gzFile fp = gzopen(path, "rb");
if (fp == NULL)
{
WriteLog("Failed!\n");
return 0;
}
romSize = gzfilelength(fp);
gzseek(fp, 0, SEEK_SET);
gzread(fp, rom, romSize);
gzclose(fp);
}
WriteLog("OK (%i bytes)\n", romSize);
}
return romSize;
}
//
// Jaguar file loading
//
bool JaguarLoadFile(char * path)
{
// jaguarRomSize = JaguarLoadROM(mem, path);
jaguarRomSize = JaguarLoadROM(jaguar_mainRom, path);
/*//This is not *nix friendly for some reason...
// if (!UserSelectFile(path, newPath))
if (!UserSelectFile((strlen(path) == 0 ? (char *)"." : path), newPath))
{
WriteLog("VJ: Could not find valid ROM in directory \"%s\"...\nAborting!\n", path);
log_done();
exit(0);
}*/
if (jaguarRomSize == 0)
{
// WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
WriteLog("GUI: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
// Need to do something else here, like throw up an error dialog instead of aborting. !!! FIX !!!
// log_done();
// exit(0);
return false; // This is a start...
}
jaguar_mainRom_crc32 = crc32_calcCheckSum(jaguar_mainRom, jaguarRomSize);
WriteLog("CRC: %08X\n", (unsigned int)jaguar_mainRom_crc32);
eeprom_init();
jaguarRunAddress = 0x802000;
char * ext = strrchr(path, '.'); // Get the file's extension for non-cartridge checking
//NOTE: Should fix JaguarLoadROM() to replace .zip with what's *in* the zip (.abs, .j64, etc.)
if (strcasecmp(ext, ".rom") == 0)
{
// File extension ".ROM": Alpine image that loads/runs at $802000
WriteLog("GUI: Setting up homebrew (ROM)... Run address: 00802000, length: %08X\n", jaguarRomSize);
for(int i=jaguarRomSize-1; i>=0; i--)
jaguar_mainRom[0x2000 + i] = jaguar_mainRom[i];
memset(jaguar_mainRom, 0xFF, 0x2000);
/* memcpy(jaguar_mainRam, jaguar_mainRom, jaguarRomSize);
memset(jaguar_mainRom, 0xFF, 0x600000);
memcpy(jaguar_mainRom + 0x2000, jaguar_mainRam, jaguarRomSize);
memset(jaguar_mainRam, 0x00, 0x400000);*/
/*
Stubulator ROM vectors...
handler 001 at $00E00008
handler 002 at $00E008DE
handler 003 at $00E008E2
handler 004 at $00E008E6
handler 005 at $00E008EA
handler 006 at $00E008EE
handler 007 at $00E008F2
handler 008 at $00E0054A
handler 009 at $00E008FA
handler 010 at $00000000
handler 011 at $00000000
handler 012 at $00E008FE
handler 013 at $00E00902
handler 014 at $00E00906
handler 015 at $00E0090A
handler 016 at $00E0090E
handler 017 at $00E00912
handler 018 at $00E00916
handler 019 at $00E0091A
handler 020 at $00E0091E
handler 021 at $00E00922
handler 022 at $00E00926
handler 023 at $00E0092A
handler 024 at $00E0092E
handler 025 at $00E0107A
handler 026 at $00E0107A
handler 027 at $00E0107A
handler 028 at $00E008DA
handler 029 at $00E0107A
handler 030 at $00E0107A
handler 031 at $00E0107A
handler 032 at $00000000
Let's try setting up the illegal instruction vector for a stubulated jaguar...
*/
/* SET32(jaguar_mainRam, 0x08, 0x00E008DE);
SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
// Try setting the vector to say, $1000 and putting an instruction there that loops forever:
// This kludge works! Yeah!
SET32(jaguar_mainRam, 0x10, 0x00001000);
SET16(jaguar_mainRam, 0x1000, 0x60FE); // Here: bra Here
}
else if (strcasecmp(ext, ".abs") == 0)
{
// File extension ".ABS": Atari linker output file with header (w/o is useless to us here)
/*
ABS Format sleuthing (LBUGDEMO.ABS):
000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
000020 00 00 40 00
DRI-format file detected...
Text segment size = 0x0000050c bytes
Data segment size = 0x000462c0 bytes
BSS Segment size = 0x00000428 bytes
Symbol Table size = 0x000012a6 bytes
Absolute Address for text segment = 0x00802000
Absolute Address for data segment = 0x0080250c
Absolute Address for BSS segment = 0x00004000
(CRZDEMO.ABS):
000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
000050 00 00 00 00 00 00 00 20
000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
000078 00 00 00 00 00 00 00 40
000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
0000a0 00 00 00 00 00 00 00 80
Header size is $A8 bytes...
BSD/COFF format file detected...
3 sections specified
Symbol Table offset = 230160 ($00038310)
Symbol Table contains 1339 symbol entries ($0000053B)
The additional header size is 28 bytes ($001C)
Magic Number for RUN_HDR = 0x00000107
Text Segment Size = 7632 ($00001DD0)
Data Segment Size = 222360 ($00036498)
BSS Segment Size = 428928 ($00068B80)
Starting Address for executable = 0x00802000
Start of Text Segment = 0x00802000
Start of Data Segment = 0x00803dd0
*/
if (jaguar_mainRom[0] == 0x60 && jaguar_mainRom[1] == 0x1B)
{
uint32_t loadAddress = GET32(jaguar_mainRom, 0x16), //runAddress = GET32(jaguar_mainRom, 0x2A),
codeSize = GET32(jaguar_mainRom, 0x02) + GET32(jaguar_mainRom, 0x06);
WriteLog("GUI: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
if (loadAddress < 0x800000)
memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x24, codeSize);
else
{
for(int i=codeSize-1; i>=0; i--)
jaguar_mainRom[(loadAddress - 0x800000) + i] = jaguar_mainRom[i + 0x24];
/* memcpy(jaguar_mainRam, jaguar_mainRom + 0x24, codeSize);
memset(jaguar_mainRom, 0xFF, 0x600000);
memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
memset(jaguar_mainRam, 0x00, 0x400000);*/
}
jaguarRunAddress = loadAddress;
}
else if (jaguar_mainRom[0] == 0x01 && jaguar_mainRom[1] == 0x50)
{
uint32_t loadAddress = GET32(jaguar_mainRom, 0x28), runAddress = GET32(jaguar_mainRom, 0x24),
codeSize = GET32(jaguar_mainRom, 0x18) + GET32(jaguar_mainRom, 0x1C);
WriteLog("GUI: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
if (loadAddress < 0x800000)
memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0xA8, codeSize);
else
{
for(int i=codeSize-1; i>=0; i--)
jaguar_mainRom[(loadAddress - 0x800000) + i] = jaguar_mainRom[i + 0xA8];
/* memcpy(jaguar_mainRam, jaguar_mainRom + 0xA8, codeSize);
memset(jaguar_mainRom, 0xFF, 0x600000);
memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
memset(jaguar_mainRam, 0x00, 0x400000);*/
}
jaguarRunAddress = runAddress;
}
else
{
WriteLog("GUI: Couldn't find correct ABS format: %02X %02X\n", jaguar_mainRom[0], jaguar_mainRom[1]);
return false;
}
}
else if (strcasecmp(ext, ".jag") == 0)
{
// File extension ".JAG": Atari server file with header
//NOTE: The bytes 'JAGR' should also be at position $1C...
// Also, there's *always* a $601A header at position $00...
if (jaguar_mainRom[0] == 0x60 && jaguar_mainRom[1] == 0x1A)
{
uint32_t loadAddress = GET32(jaguar_mainRom, 0x22), runAddress = GET32(jaguar_mainRom, 0x2A);
//This is not always right! Especially when converted via bin2jag1!!!
//We should have access to the length of the furshlumiger file that was loaded anyway!
//Now, we do! ;-)
// uint32_t progLength = GET32(jaguar_mainRom, 0x02);
//jaguarRomSize
//jaguarRunAddress
// WriteLog("Jaguar: Setting up PD ROM... Run address: %08X, length: %08X\n", runAddress, progLength);
// memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, progLength);
WriteLog("GUI: Setting up homebrew (JAG)... Run address: %08X, length: %08X\n", runAddress, jaguarRomSize - 0x2E);
memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, jaguarRomSize - 0x2E);
// SET32(jaguar_mainRam, 4, runAddress);
jaguarRunAddress = runAddress;
}
else
return false;
}
// .J64 (Jaguar cartridge ROM image) is implied by the FileList object...
return true;
}
//
// Get the length of a (possibly) gzipped file
//
int gzfilelength(gzFile gd)
{
int size = 0, length = 0;
unsigned char buffer[0x10000];
gzrewind(gd);
do
{
// Read in chunks until EOF
size = gzread(gd, buffer, 0x10000);
if (size <= 0)
break;
length += size;
}
while (!gzeof(gd));
gzrewind(gd);
return length;
}
#endif