Added initial emulator configuration window, cleanup of settings code.
The settings code now actually saves updated settings to the config file. Also wrote the beginnings of a proper Config window, and a few of the options actually work (which ones work is left as an exercise for the reader).
2
Makefile
@ -101,6 +101,7 @@ LIBS = $(SDL_LIBS) -lstdc++ -lz -lm $(GLLIB) -pg
|
||||
INCS = -I. -I./src
|
||||
|
||||
OBJS = \
|
||||
obj/config.o \
|
||||
obj/diskselector.o \
|
||||
obj/font10pt.o \
|
||||
obj/font12pt.o \
|
||||
@ -122,7 +123,6 @@ OBJS = \
|
||||
obj/log.o \
|
||||
obj/mmu.o \
|
||||
obj/mockingboard.o \
|
||||
obj/sdlemu_config.o \
|
||||
obj/settings.o \
|
||||
obj/sound.o \
|
||||
obj/timing.o \
|
||||
|
33
apple2.cfg
@ -3,22 +3,28 @@
|
||||
#
|
||||
|
||||
# Apple ROM paths
|
||||
|
||||
#default
|
||||
#Not used anymore
|
||||
#BIOSROM = ./ROMs/apple2e-enhanced.rom
|
||||
#BIOSROM = ./ROMs/apple2e.rom
|
||||
#Not used anymore
|
||||
#diskROM = ./ROMs/disk.rom
|
||||
#ROMs = ./ROMs
|
||||
#default
|
||||
#disks = ./disks
|
||||
#harddrive = ./disks/Pitch-Dark-20180731.2mg
|
||||
|
||||
# Auto state loading/saving upon starting/quitting Apple2 (1 - use, 0 - don't use)
|
||||
# Defaults
|
||||
disks = ./disks/
|
||||
|
||||
#These are the defaults--we don't advertise it just yet... ;-)
|
||||
autoSaveState = 0
|
||||
#autoStateFilename = ./apple2auto.state
|
||||
harddrive1 = Pitch-Dark-20180731.2mg
|
||||
harddrive2 =
|
||||
harddrive3 =
|
||||
harddrive4 =
|
||||
harddrive5 =
|
||||
harddrive6 =
|
||||
harddrive7 =
|
||||
|
||||
# Auto state loading/saving upon starting/quitting Apple2 (1-use, 0-don't use)
|
||||
# These are the defaults--we don't advertise it just yet... ;-)
|
||||
|
||||
autoSaveState = 1
|
||||
autoStateFilename = ./apple2auto.state
|
||||
|
||||
# OpenGL filtering type: 1 - blurry, 0 - sharp
|
||||
|
||||
@ -36,3 +42,10 @@ useJoystick = 0
|
||||
|
||||
joyport = 0
|
||||
|
||||
windowX = 770
|
||||
windowY = 146
|
||||
|
||||
hardwareTypeNTSC = 1
|
||||
renderType = 0
|
||||
useOpenGL = 1
|
||||
|
||||
|
@ -57,8 +57,9 @@
|
||||
#include "sound.h"
|
||||
#include "timing.h"
|
||||
#include "video.h"
|
||||
#include "gui/gui.h"
|
||||
#include "gui/diskselector.h"
|
||||
#include "gui/config.h"
|
||||
#include "gui/gui.h"
|
||||
|
||||
// Debug and misc. defines
|
||||
|
||||
@ -673,9 +674,9 @@ while (pc < 0x9FF)
|
||||
}
|
||||
#endif
|
||||
|
||||
SaveSettings();
|
||||
SoundDone();
|
||||
VideoDone();
|
||||
SaveSettings();
|
||||
LogDone();
|
||||
|
||||
return 0;
|
||||
@ -752,6 +753,10 @@ static void FrameCallback(void)
|
||||
if (event.key.repeat != 0)
|
||||
break;
|
||||
|
||||
// This breaks IMEs and the like, but we'll do simple for now
|
||||
if (GUI::KeyDown(event.key.keysym.sym))
|
||||
break;
|
||||
|
||||
// Use CTRL+SHIFT+Q to exit, as well as the usual window decoration
|
||||
// method
|
||||
if ((event.key.keysym.mod & KMOD_CTRL)
|
||||
@ -1033,7 +1038,7 @@ else if (event.key.keysym.sym == SDLK_F9)
|
||||
|
||||
// Hide the mouse if it's been 1s since the last time it was moved
|
||||
// N.B.: Should disable mouse hiding if it's over the GUI...
|
||||
if ((hideMouseTimeout > 0) && !(GUI::sidebarState == SBS_SHOWN || DiskSelector::showWindow == true))
|
||||
if ((hideMouseTimeout > 0) && !(GUI::sidebarState == SBS_SHOWN || DiskSelector::showWindow == true || Config::showWindow == true))
|
||||
hideMouseTimeout--;
|
||||
else if (hideMouseTimeout == 0)
|
||||
{
|
||||
|
@ -31,9 +31,9 @@ uint8_t standardTMAP[160] = {
|
||||
|
||||
|
||||
//
|
||||
// Note that size *must* be a valid pointer, otherwise this will go BOOM
|
||||
// sizePtr is optional
|
||||
//
|
||||
uint8_t * ReadFile(const char * filename, uint32_t * size)
|
||||
uint8_t * ReadFile(const char * filename, uint32_t * sizePtr/*= NULL*/)
|
||||
{
|
||||
FILE * fp = fopen(filename, "rb");
|
||||
|
||||
@ -41,13 +41,16 @@ uint8_t * ReadFile(const char * filename, uint32_t * size)
|
||||
return NULL;
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
*size = ftell(fp);
|
||||
uint32_t size = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
uint8_t * buffer = (uint8_t *)malloc(*size);
|
||||
fread(buffer, 1, *size, fp);
|
||||
uint8_t * buffer = (uint8_t *)malloc(size);
|
||||
fread(buffer, 1, size, fp);
|
||||
fclose(fp);
|
||||
|
||||
if (sizePtr != NULL)
|
||||
*sizePtr = size;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ struct WOZ2
|
||||
};
|
||||
|
||||
// Exported functions
|
||||
uint8_t * ReadFile(const char * filename, uint32_t * size);
|
||||
uint8_t * ReadFile(const char * filename, uint32_t * sizePtr = NULL);
|
||||
void InitWOZ2Headers(WOZ2 &);
|
||||
uint8_t * InitWOZ(uint32_t * pSize = NULL);
|
||||
uint8_t * UpconvertWOZ1ToWOZ2(uint8_t * woz1Data, uint32_t woz1Size, uint32_t * newSize);
|
||||
|
@ -52,19 +52,19 @@ enum { DSS_SHOWING, DSS_HIDING, DSS_SHOWN, DSS_HIDDEN, DSS_LSB_SHOWING, DSS_LSB_
|
||||
#define DS_YPOS ((VIRTUAL_SCREEN_HEIGHT - DS_HEIGHT) / 2)
|
||||
|
||||
|
||||
bool entered = false;
|
||||
int driveNumber;
|
||||
int diskSelectorState = DSS_HIDDEN;
|
||||
int diskSelected = -1;
|
||||
int lastDiskSelected = -1;
|
||||
int numColumns;
|
||||
int colStart = 0;
|
||||
int dxLeft = 0;
|
||||
int dxRight = 0;
|
||||
int rsbPos = DS_WIDTH;
|
||||
int lsbPos = -40;
|
||||
int textScrollCount = 0;
|
||||
bool refresh = false;
|
||||
static bool entered = false;
|
||||
static int driveNumber;
|
||||
static int diskSelectorState = DSS_HIDDEN;
|
||||
static int diskSelected = -1;
|
||||
static int lastDiskSelected = -1;
|
||||
static int numColumns;
|
||||
static int colStart = 0;
|
||||
static int dxLeft = 0;
|
||||
static int dxRight = 0;
|
||||
static int rsbPos = DS_WIDTH;
|
||||
static int lsbPos = -40;
|
||||
static int textScrollCount = 0;
|
||||
static bool refresh = false;
|
||||
|
||||
/*
|
||||
So, how this will work for multiple columns, where the number of columns is greater than 3, is to have an arrow button pop up on the left or right hand side (putting the mouse on the left or right side of the disk selector activates (shows) the button, if such a move can be made. Button hides when the mouse moves out of the hot zone or when it has no more effect.
|
||||
@ -110,9 +110,7 @@ struct FileStruct
|
||||
|
||||
|
||||
static SDL_Texture * window = NULL;
|
||||
static SDL_Texture * charStamp = NULL;
|
||||
static uint32_t windowPixels[DS_WIDTH * DS_HEIGHT];
|
||||
static uint32_t stamp[FONT_WIDTH * FONT_HEIGHT];
|
||||
SDL_Texture * scrollLeftIcon = NULL;
|
||||
SDL_Texture * scrollRightIcon = NULL;
|
||||
bool DiskSelector::showWindow = false;
|
||||
@ -123,8 +121,6 @@ void DiskSelector::Init(SDL_Renderer * renderer)
|
||||
{
|
||||
window = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
|
||||
SDL_TEXTUREACCESS_TARGET, DS_WIDTH, DS_HEIGHT);
|
||||
charStamp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
|
||||
SDL_TEXTUREACCESS_TARGET, FONT_WIDTH, FONT_HEIGHT);
|
||||
|
||||
if (!window)
|
||||
{
|
||||
@ -135,16 +131,13 @@ void DiskSelector::Init(SDL_Renderer * renderer)
|
||||
if (SDL_SetTextureBlendMode(window, SDL_BLENDMODE_BLEND) == -1)
|
||||
WriteLog("GUI (DiskSelector): Could not set blend mode for window.\n");
|
||||
|
||||
if (SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND) == -1)
|
||||
WriteLog("GUI (DiskSelector): Could not set blend mode for charStamp.\n");
|
||||
|
||||
scrollLeftIcon = GUI::CreateTexture(renderer, &scroll_left);
|
||||
scrollRightIcon = GUI::CreateTexture(renderer, &scroll_right);
|
||||
|
||||
for(uint32_t i=0; i<DS_WIDTH*DS_HEIGHT; i++)
|
||||
windowPixels[i] = 0xEF007F00;
|
||||
|
||||
SDL_UpdateTexture(window, NULL, windowPixels, 128 * sizeof(Uint32));
|
||||
SDL_UpdateTexture(window, NULL, windowPixels, DS_WIDTH * sizeof(Uint32));
|
||||
FindDisks();
|
||||
DrawFilenames(renderer);
|
||||
}
|
||||
@ -393,7 +386,7 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
|
||||
if (i >= fsList[partialColStart + y].image.length())
|
||||
break;
|
||||
|
||||
DrawCharacter(renderer, x + 1, y + 1, fsList[partialColStart + y].image[i], false);
|
||||
GUI::DrawCharacter(renderer, x + 1, y + 1, fsList[partialColStart + y].image[i], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -408,7 +401,7 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
|
||||
if (i >= fsList[fsStart + y].image.length())
|
||||
break;
|
||||
|
||||
DrawCharacter(renderer, x + 1, y + 1, fsList[fsStart + y].image[i], false);
|
||||
GUI::DrawCharacter(renderer, x + 1, y + 1, fsList[fsStart + y].image[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -432,7 +425,7 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
|
||||
break;
|
||||
|
||||
bool invert = (diskSelected == (int)fsStart ? true : false);
|
||||
DrawCharacter(renderer, currentX + i + 1 + offset, currentY + 1, fsList[fsStart].image[i], invert);
|
||||
GUI::DrawCharacter(renderer, currentX + i + 1 + offset, currentY + 1, fsList[fsStart].image[i], invert);
|
||||
}
|
||||
|
||||
count++;
|
||||
@ -452,7 +445,7 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
|
||||
if (i >= fsList[diskSelected].image.length())
|
||||
break;
|
||||
|
||||
DrawCharacter(renderer, i + 1, 0, fsList[diskSelected].image[i], true);
|
||||
GUI::DrawCharacter(renderer, i + 1, 0, fsList[diskSelected].image[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -461,22 +454,6 @@ void DiskSelector::DrawFilenames(SDL_Renderer * renderer)
|
||||
}
|
||||
|
||||
|
||||
void DiskSelector::DrawCharacter(SDL_Renderer * renderer, int x, int y, uint8_t c, bool invert/*=false*/)
|
||||
{
|
||||
uint32_t inv = (invert ? 0x000000FF : 0x00000000);
|
||||
uint32_t pixel = 0xFFFFC000; // RRGGBBAA
|
||||
uint8_t * ptr = (uint8_t *)&font10pt[(c - 0x20) * FONT_WIDTH * FONT_HEIGHT];
|
||||
SDL_Rect dst;
|
||||
dst.x = x * FONT_WIDTH, dst.y = y * FONT_HEIGHT, dst.w = FONT_WIDTH, dst.h = FONT_HEIGHT;
|
||||
|
||||
for(int i=0; i<FONT_WIDTH*FONT_HEIGHT; i++)
|
||||
stamp[i] = (pixel | ptr[i]) ^ inv;
|
||||
|
||||
SDL_UpdateTexture(charStamp, NULL, stamp, FONT_WIDTH * sizeof(Uint32));
|
||||
SDL_RenderCopy(renderer, charStamp, NULL, &dst);
|
||||
}
|
||||
|
||||
|
||||
void DiskSelector::ShowWindow(int drive)
|
||||
{
|
||||
diskSelectorState = DSS_SHOWN;
|
||||
@ -486,6 +463,18 @@ void DiskSelector::ShowWindow(int drive)
|
||||
}
|
||||
|
||||
|
||||
void DiskSelector::HideWindow(void)
|
||||
{
|
||||
diskSelectorState = DSS_HIDDEN;
|
||||
dxLeft = 0;
|
||||
dxRight = 0;
|
||||
rsbPos = DS_WIDTH;
|
||||
lsbPos = -40;
|
||||
showWindow = false;
|
||||
refresh = true;
|
||||
}
|
||||
|
||||
|
||||
void DiskSelector::MouseDown(int32_t x, int32_t y, uint32_t buttons)
|
||||
{
|
||||
if (!showWindow || !entered)
|
||||
@ -689,7 +678,7 @@ void DiskSelector::HandleGUIState(void)
|
||||
|
||||
void DiskSelector::HandleSelection(SDL_Renderer * renderer)
|
||||
{
|
||||
SDL_UpdateTexture(window, NULL, windowPixels, 128 * sizeof(Uint32));
|
||||
SDL_UpdateTexture(window, NULL, windowPixels, DS_WIDTH * sizeof(Uint32));
|
||||
DrawFilenames(renderer);
|
||||
refresh = false;
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ class DiskSelector
|
||||
static bool CheckManifest(const char *, DiskSet *);
|
||||
static bool HasLegalExtension(const char *);
|
||||
static void DrawFilenames(SDL_Renderer *);
|
||||
static void DrawCharacter(SDL_Renderer *, int, int, uint8_t, bool inv=false);
|
||||
static void ShowWindow(int);
|
||||
static void HideWindow(void);
|
||||
static void MouseDown(int32_t, int32_t, uint32_t);
|
||||
static void MouseUp(int32_t, int32_t, uint32_t);
|
||||
static void MouseMove(int32_t, int32_t, uint32_t);
|
||||
|
152
src/gui/gui.cpp
@ -3,7 +3,7 @@
|
||||
//
|
||||
// Graphical User Interface support
|
||||
// by James Hammons
|
||||
// © 2014 Underground Software
|
||||
// © 2014-2019 Underground Software
|
||||
//
|
||||
// JLH = James Hammons <jlhamm@acm.org>
|
||||
//
|
||||
@ -21,9 +21,13 @@
|
||||
//
|
||||
|
||||
#include "gui.h"
|
||||
#include <vector>
|
||||
#include "apple2.h"
|
||||
#include "config.h"
|
||||
#include "diskselector.h"
|
||||
#include "elements.h"
|
||||
#include "floppydrive.h"
|
||||
#include "font10pt.h"
|
||||
#include "log.h"
|
||||
#include "video.h"
|
||||
|
||||
@ -80,6 +84,14 @@ const char ejectIcon[(8 * 7) + 1] =
|
||||
"@@@@@@@@"
|
||||
"@@@@@@@@";
|
||||
|
||||
const char newDiskIcon[(30 * 6) + 1] =
|
||||
"@@ @@ @@@@@ @@ @@ @@ "
|
||||
"@@@ @@ @@ @@ @@ @@@@ "
|
||||
"@@@@ @@ @@@@ @@ @ @@ @@@@@@"
|
||||
"@@ @@@@ @@ @@@@@@@ @@ "
|
||||
"@@ @@@ @@ @@@@@@@ @@@@@ "
|
||||
"@@ @@ @@@@@ @@ @@ @@@@ ";
|
||||
|
||||
const char driveLight[(5 * 5) + 1] =
|
||||
" @@@ "
|
||||
"@@@@@"
|
||||
@ -88,7 +100,6 @@ const char driveLight[(5 * 5) + 1] =
|
||||
" @@@ ";
|
||||
|
||||
|
||||
|
||||
SDL_Texture * GUI::overlay = NULL;
|
||||
SDL_Rect GUI::olDst;
|
||||
int GUI::sidebarState = SBS_HIDDEN;
|
||||
@ -117,10 +128,16 @@ const char iconHelp[7][80] = { "Turn emulated Apple off/on",
|
||||
"Configure Apple2" };
|
||||
bool disk1EjectHovered = false;
|
||||
bool disk2EjectHovered = false;
|
||||
bool disk1NewDiskHovered = false;
|
||||
bool disk2NewDiskHovered = false;
|
||||
|
||||
SDL_Texture * GUI::charStamp = NULL;
|
||||
uint32_t GUI::stamp[FONT_WIDTH * FONT_HEIGHT];
|
||||
|
||||
#define SIDEBAR_X_POS (VIRTUAL_SCREEN_WIDTH - 80)
|
||||
|
||||
//std::vector<void *> objList;
|
||||
|
||||
|
||||
GUI::GUI(void)
|
||||
{
|
||||
@ -136,6 +153,8 @@ void GUI::Init(SDL_Renderer * renderer)
|
||||
{
|
||||
overlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
|
||||
SDL_TEXTUREACCESS_TARGET, 128, 380);
|
||||
charStamp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
|
||||
SDL_TEXTUREACCESS_TARGET, FONT_WIDTH, FONT_HEIGHT);
|
||||
|
||||
if (!overlay)
|
||||
{
|
||||
@ -146,6 +165,9 @@ void GUI::Init(SDL_Renderer * renderer)
|
||||
if (SDL_SetTextureBlendMode(overlay, SDL_BLENDMODE_BLEND) == -1)
|
||||
WriteLog("GUI: Could not set blend mode for overlay.\n");
|
||||
|
||||
if (SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND) == -1)
|
||||
WriteLog("GUI: Could not set blend mode for charStamp.\n");
|
||||
|
||||
for(uint32_t i=0; i<128*380; i++)
|
||||
texturePointer[i] = 0xB0A000A0;
|
||||
|
||||
@ -185,6 +207,7 @@ void GUI::Init(SDL_Renderer * renderer)
|
||||
}
|
||||
|
||||
DiskSelector::Init(renderer);
|
||||
Config::Init(renderer);
|
||||
WriteLog("GUI: Successfully initialized.\n");
|
||||
}
|
||||
|
||||
@ -206,6 +229,7 @@ SDL_Texture * GUI::CreateTexture(SDL_Renderer * renderer, const void * source)
|
||||
void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
|
||||
{
|
||||
DiskSelector::MouseDown(x, y, buttons);
|
||||
Config::MouseDown(x, y, buttons);
|
||||
|
||||
if (sidebarState != SBS_SHOWN)
|
||||
return;
|
||||
@ -231,9 +255,10 @@ void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
|
||||
SpawnMessage("*** DISK #1 EJECTED ***");
|
||||
}
|
||||
|
||||
if (!disk1EjectHovered)
|
||||
if (!disk1EjectHovered && !disk1NewDiskHovered)
|
||||
{
|
||||
// Load the disk selector
|
||||
Config::HideWindow();
|
||||
DiskSelector::ShowWindow(0);
|
||||
}
|
||||
|
||||
@ -248,9 +273,10 @@ void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
|
||||
SpawnMessage("*** DISK #2 EJECTED ***");
|
||||
}
|
||||
|
||||
if (!disk2EjectHovered)
|
||||
if (!disk2EjectHovered && !disk2NewDiskHovered)
|
||||
{
|
||||
// Load the disk selector
|
||||
Config::HideWindow();
|
||||
DiskSelector::ShowWindow(1);
|
||||
}
|
||||
|
||||
@ -271,6 +297,9 @@ void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
|
||||
// Configuration
|
||||
case 6:
|
||||
SpawnMessage("*** CONFIGURATION ***");
|
||||
// Load the configuration window
|
||||
DiskSelector::HideWindow();
|
||||
Config::ShowWindow();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -279,12 +308,14 @@ void GUI::MouseDown(int32_t x, int32_t y, uint32_t buttons)
|
||||
void GUI::MouseUp(int32_t x, int32_t y, uint32_t buttons)
|
||||
{
|
||||
DiskSelector::MouseUp(x, y, buttons);
|
||||
Config::MouseUp(x, y, buttons);
|
||||
}
|
||||
|
||||
|
||||
void GUI::MouseMove(int32_t x, int32_t y, uint32_t buttons)
|
||||
{
|
||||
DiskSelector::MouseMove(x, y, buttons);
|
||||
Config::MouseMove(x, y, buttons);
|
||||
|
||||
if (sidebarState != SBS_SHOWN)
|
||||
{
|
||||
@ -335,6 +366,16 @@ void GUI::MouseMove(int32_t x, int32_t y, uint32_t buttons)
|
||||
&& (y >= (117 + 31 + 2))
|
||||
&& (y < (117 + 31 + 2 + 7)) ? true : false);
|
||||
|
||||
disk1NewDiskHovered = ((x >= (SIDEBAR_X_POS + 24 + 6))
|
||||
&& (x < (SIDEBAR_X_POS + 24 + 6 + 30))
|
||||
&& (y >= (63 + 31 + 2))
|
||||
&& (y < (63 + 31 + 2 + 6)) ? true : false);
|
||||
|
||||
disk2NewDiskHovered = ((x >= (SIDEBAR_X_POS + 24 + 6))
|
||||
&& (x < (SIDEBAR_X_POS + 24 + 6 + 30))
|
||||
&& (y >= (117 + 31 + 2))
|
||||
&& (y < (117 + 31 + 2 + 6)) ? true : false);
|
||||
|
||||
if (iconSelected != lastIconSelected)
|
||||
{
|
||||
HandleIconSelection(sdlRenderer);
|
||||
@ -355,6 +396,12 @@ void GUI::MouseMove(int32_t x, int32_t y, uint32_t buttons)
|
||||
}
|
||||
|
||||
|
||||
bool GUI::KeyDown(uint32_t key)
|
||||
{
|
||||
return Config::KeyDown(key);
|
||||
}
|
||||
|
||||
|
||||
void GUI::HandleIconSelection(SDL_Renderer * renderer)
|
||||
{
|
||||
// Set up drive icons in their current states
|
||||
@ -409,6 +456,7 @@ void GUI::AssembleDriveIcon(SDL_Renderer * renderer, int driveNumber)
|
||||
DrawCharArray(renderer, number[driveNumber], 30, 20, 7, 7, 0xD0, 0xE0, 0xF0);
|
||||
DrawDriveLight(renderer, driveNumber);
|
||||
DrawEjectButton(renderer, driveNumber);
|
||||
DrawNewDiskButton(renderer, driveNumber);
|
||||
|
||||
// Set render target back to default
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
@ -430,6 +478,21 @@ void GUI::DrawEjectButton(SDL_Renderer * renderer, int driveNumber)
|
||||
}
|
||||
|
||||
|
||||
void GUI::DrawNewDiskButton(SDL_Renderer * renderer, int driveNumber)
|
||||
{
|
||||
if (!floppyDrive[0].IsEmpty(driveNumber))
|
||||
return;
|
||||
|
||||
uint8_t r = 0x00, g = 0xAA, b = 0x00;
|
||||
|
||||
if ((driveNumber == 0 && disk1NewDiskHovered)
|
||||
|| (driveNumber == 1 && disk2NewDiskHovered))
|
||||
r = 0x20, g = 0xFF, b = 0x20;
|
||||
|
||||
DrawCharArray(renderer, newDiskIcon, 6, 31, 30, 6, r, g, b);
|
||||
}
|
||||
|
||||
|
||||
void GUI::DrawDriveLight(SDL_Renderer * renderer, int driveNumber)
|
||||
{
|
||||
int lightState = floppyDrive[0].DriveLightStatus(driveNumber);
|
||||
@ -463,6 +526,85 @@ void GUI::DrawCharArray(SDL_Renderer * renderer, const char * array, int x,
|
||||
}
|
||||
|
||||
|
||||
void GUI::DrawCharacter(SDL_Renderer * renderer, int x, int y, uint8_t c, bool invert/*= false*/)
|
||||
{
|
||||
uint32_t inv = (invert ? 0x000000FF : 0x00000000);
|
||||
uint32_t pixel = 0xFFFFC000; // RRGGBBAA
|
||||
uint8_t * ptr = (uint8_t *)&font10pt[(c - 0x20) * FONT_WIDTH * FONT_HEIGHT];
|
||||
SDL_Rect dst;
|
||||
dst.x = x * FONT_WIDTH, dst.y = y * FONT_HEIGHT, dst.w = FONT_WIDTH, dst.h = FONT_HEIGHT;
|
||||
|
||||
for(int i=0; i<FONT_WIDTH*FONT_HEIGHT; i++)
|
||||
stamp[i] = (pixel | ptr[i]) ^ inv;
|
||||
|
||||
SDL_UpdateTexture(charStamp, NULL, stamp, FONT_WIDTH * sizeof(Uint32));
|
||||
SDL_RenderCopy(renderer, charStamp, NULL, &dst);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// N.B.: This draws a char at an abosulte X/Y position, not on a grid
|
||||
//
|
||||
void GUI::DrawCharacterVert(SDL_Renderer * renderer, int x, int y, uint8_t c, bool invert/*= false*/)
|
||||
{
|
||||
uint32_t inv = (invert ? 0x000000FF : 0x00000000);
|
||||
uint32_t pixel = 0xFFFFC000; // RRGGBBAA
|
||||
uint8_t * ptr = (uint8_t *)&font10pt[(c - 0x20) * FONT_WIDTH * FONT_HEIGHT];
|
||||
SDL_Rect dst;
|
||||
dst.x = x, dst.y = y, dst.w = FONT_WIDTH, dst.h = FONT_HEIGHT;
|
||||
SDL_Point pt = { FONT_WIDTH - 1, FONT_HEIGHT - 1 };
|
||||
|
||||
for(int i=0; i<FONT_WIDTH*FONT_HEIGHT; i++)
|
||||
stamp[i] = (pixel | ptr[i]) ^ inv;
|
||||
|
||||
SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_NONE);
|
||||
SDL_UpdateTexture(charStamp, NULL, stamp, FONT_WIDTH * sizeof(Uint32));
|
||||
SDL_RenderCopyEx(renderer, charStamp, NULL, &dst, 270.0, &pt, SDL_FLIP_NONE);
|
||||
SDL_SetTextureBlendMode(charStamp, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
|
||||
|
||||
void GUI::DrawString(SDL_Renderer * renderer, int x, int y, const char * s, bool invert/*= false*/)
|
||||
{
|
||||
int len = strlen(s);
|
||||
|
||||
for(int i=0; i<len; i++)
|
||||
DrawCharacter(renderer, x + i, y, s[i], invert);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// N.B.: This draws a char at an abosulte X/Y position, not on a grid
|
||||
//
|
||||
void GUI::DrawStringVert(SDL_Renderer * renderer, int x, int y, const char * s, bool invert/*= false*/)
|
||||
{
|
||||
int len = strlen(s);
|
||||
|
||||
for(int i=0; i<len; i++)
|
||||
DrawCharacterVert(renderer, x, y - (FONT_WIDTH * i), s[i], invert);
|
||||
}
|
||||
|
||||
|
||||
void GUI::DrawBox(SDL_Renderer * renderer, int x, int y, int w, int h, int r/*= 0x00*/, int g/*= 0xAA*/, int b/*= 0x00*/)
|
||||
{
|
||||
SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
|
||||
|
||||
for(int i=0; i<w; i++)
|
||||
{
|
||||
SDL_RenderDrawPoint(renderer, x + i, y);
|
||||
SDL_RenderDrawPoint(renderer, x + i, y + h - 1);
|
||||
}
|
||||
|
||||
for(int i=0; i<h; i++)
|
||||
{
|
||||
SDL_RenderDrawPoint(renderer, x, y + i);
|
||||
SDL_RenderDrawPoint(renderer, x + w - 1, y + i);
|
||||
}
|
||||
|
||||
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
|
||||
}
|
||||
|
||||
|
||||
void GUI::HandleGUIState(void)
|
||||
{
|
||||
olDst.x += dx;
|
||||
@ -500,6 +642,7 @@ void GUI::DrawSidebarIcons(SDL_Renderer * renderer)
|
||||
|
||||
void GUI::Render(SDL_Renderer * renderer)
|
||||
{
|
||||
// Sanity check
|
||||
if (!overlay)
|
||||
return;
|
||||
|
||||
@ -512,6 +655,7 @@ void GUI::Render(SDL_Renderer * renderer)
|
||||
|
||||
// Hmm.
|
||||
DiskSelector::Render(renderer);
|
||||
Config::Render(renderer);
|
||||
}
|
||||
|
||||
|
||||
|
@ -23,12 +23,19 @@ class GUI
|
||||
static void MouseDown(int32_t, int32_t, uint32_t);
|
||||
static void MouseUp(int32_t, int32_t, uint32_t);
|
||||
static void MouseMove(int32_t, int32_t, uint32_t);
|
||||
static bool KeyDown(uint32_t);
|
||||
static void HandleIconSelection(SDL_Renderer *);
|
||||
static void AssembleDriveIcon(SDL_Renderer *, int);
|
||||
static void DrawEjectButton(SDL_Renderer *, int);
|
||||
static void DrawNewDiskButton(SDL_Renderer *, int);
|
||||
static void DrawDriveLight(SDL_Renderer *, int);
|
||||
static void DrawCharArray(SDL_Renderer *, const char *, int x,
|
||||
int y, int w, int h, int r, int g, int b);
|
||||
static void DrawCharacter(SDL_Renderer *, int, int, uint8_t, bool inv = false);
|
||||
static void DrawCharacterVert(SDL_Renderer *, int, int, uint8_t, bool inv = false);
|
||||
static void DrawString(SDL_Renderer *, int, int, const char *, bool inv = false);
|
||||
static void DrawStringVert(SDL_Renderer *, int, int, const char *, bool inv = false);
|
||||
static void DrawBox(SDL_Renderer *, int, int, int, int, int r = 0x00, int g = 0xAA, int b = 0x00);
|
||||
static void HandleGUIState(void);
|
||||
static void DrawSidebarIcons(SDL_Renderer *);
|
||||
static void Render(SDL_Renderer *);
|
||||
@ -41,6 +48,10 @@ class GUI
|
||||
static int32_t iconSelected;
|
||||
static bool hasKeyboardFocus;
|
||||
static bool powerOnState;
|
||||
|
||||
private:
|
||||
static SDL_Texture * charStamp;
|
||||
static uint32_t stamp[];
|
||||
};
|
||||
|
||||
#endif // __GUI_H__
|
||||
|
@ -618,10 +618,12 @@ void InstallHardDrive(uint8_t slot)
|
||||
{
|
||||
SlotData hd = { SlotIOR, SlotIOW, SlotROM, 0, SlotIOExtraR, SlotIOExtraW };
|
||||
InstallSlotHandler(slot, &hd);
|
||||
char fnBuf[MAX_PATH + 1];
|
||||
|
||||
// If this fails to read the file, the pointer is set to NULL
|
||||
uint32_t size = 0;
|
||||
hdData = ReadFile(settings.hdPath, &size);
|
||||
sprintf(fnBuf, "%s%s", settings.disksPath, settings.hd[0]);
|
||||
hdData = ReadFile(fnBuf, &size);
|
||||
|
||||
if (hdData)
|
||||
WriteLog("HD: Read Hard Drive image file, %u bytes ($%X)\n", size - 0x40, size - 0x40);
|
||||
|
@ -1,151 +0,0 @@
|
||||
/*
|
||||
* SDLEMU library - Free sdl related functions library
|
||||
* Copyrigh(c) 1999-2002 sdlemu development crew
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include "sdlemu_config.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class token_list
|
||||
{
|
||||
public:
|
||||
token_list(const string &name) : m_name(name), m_value(""), m_token("") {}
|
||||
void add_token_variable(const string &var) { m_token = var; }
|
||||
void add_token_value(const string &value) { m_value = value; }
|
||||
const string &LineName() const { return m_name; }
|
||||
const string &Token() const { return m_token; }
|
||||
const string &Value() const { return m_value; }
|
||||
private:
|
||||
std::string m_name;
|
||||
std::string m_value;
|
||||
std::string m_token;
|
||||
};
|
||||
|
||||
std::list<token_list> vec;
|
||||
|
||||
void string_tokenize_variable()
|
||||
{
|
||||
list<token_list>::iterator p;
|
||||
const string delim = " ";
|
||||
for(p = vec.begin(); p != vec.end(); p++) {
|
||||
string::size_type lastPos = (*p).LineName().find_first_not_of(delim, 0);
|
||||
string::size_type pos = (*p).LineName().find_first_of(delim, lastPos);
|
||||
|
||||
if(string::npos != pos && string::npos != lastPos) {
|
||||
string s = (*p).LineName().substr(lastPos, pos - lastPos);
|
||||
(*p).add_token_variable(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void string_tokenize_value()
|
||||
{
|
||||
list<token_list>::iterator p;
|
||||
const string delim = " =\n\t\r"; // "\r" needed for Win32 compatibility...
|
||||
|
||||
for(p = vec.begin(); p != vec.end(); p++) {
|
||||
string::size_type lastPos = (*p).LineName().find_first_of(delim, 0);
|
||||
string::size_type pos = (*p).LineName().find_first_not_of(delim, lastPos);
|
||||
|
||||
if(string::npos != pos && string::npos != lastPos) {
|
||||
string s = (*p).LineName().substr(pos);
|
||||
(*p).add_token_value(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sdlemu_init_config(const char *filename)
|
||||
{
|
||||
FILE *f = fopen(filename, "r");
|
||||
if(!f) return 0;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
int len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
char *s = new char[len];
|
||||
fread(s, 1, len, f);
|
||||
string str(s);
|
||||
|
||||
const string delim = "\n\r"; // "\r" needed for Win32 compatibility...
|
||||
string::size_type lastPos = str.find_first_not_of(delim, 0);
|
||||
string::size_type pos = str.find_first_of(delim, lastPos);
|
||||
|
||||
while (string::npos != pos || string::npos != lastPos) {
|
||||
string string = str.substr(lastPos, pos - lastPos);
|
||||
if(string[0] == '#')
|
||||
{
|
||||
}
|
||||
else if(string[0] == '[')
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
vec.push_back(string);
|
||||
}
|
||||
lastPos = str.find_first_not_of(delim, pos);
|
||||
pos = str.find_first_of(delim, lastPos);
|
||||
}
|
||||
string_tokenize_variable();
|
||||
string_tokenize_value();
|
||||
delete [] s;
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *sdlemu_getval_string(const char *key_string, const char *default_string)
|
||||
{
|
||||
list<token_list>::iterator p;
|
||||
for(p = vec.begin(); p != vec.end(); p++) {
|
||||
|
||||
if(strcmp((*p).Token().c_str(), key_string) == 0)
|
||||
return (*p).Value().c_str();
|
||||
}
|
||||
return default_string;
|
||||
}
|
||||
|
||||
int sdlemu_getval_int(const char *key_string, int default_int)
|
||||
{
|
||||
list<token_list>::iterator p;
|
||||
for(p = vec.begin(); p != vec.end(); p++) {
|
||||
|
||||
if(strcmp((*p).Token().c_str(), key_string) == 0) {
|
||||
const char *ret = (*p).Value().c_str();
|
||||
if(ret) return atoi(ret);
|
||||
}
|
||||
}
|
||||
return default_int;
|
||||
}
|
||||
|
||||
int sdlemu_getval_bool(const char *key_string, int default_int)
|
||||
{
|
||||
list<token_list>::iterator p;
|
||||
for(p = vec.begin(); p != vec.end(); p++) {
|
||||
|
||||
if(strcmp((*p).Token().c_str(), key_string) == 0) {
|
||||
const char *ret = (*p).Value().c_str();
|
||||
if(ret) return atoi(ret)>0;
|
||||
}
|
||||
}
|
||||
return default_int;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#ifndef __SDL_CONFIG_H__
|
||||
#define __SDL_CONFIG_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int sdlemu_init_config(const char * filename);
|
||||
const char * sdlemu_getval_string(const char * key_string, const char * default_string);
|
||||
int sdlemu_getval_int(const char * key_string, int default_int);
|
||||
int sdlemu_getval_bool(const char * key_string, int default_int);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
348
src/settings.cpp
@ -1,36 +1,50 @@
|
||||
//
|
||||
// SETTINGS.CPP: Game configuration loading/saving support
|
||||
// settings.cpp: Apple2 configuration loading/saving support
|
||||
//
|
||||
// by James Hammons
|
||||
// (C) 2005 Underground Software
|
||||
//
|
||||
// JLH = James Hammons <jlhamm@acm.org>
|
||||
//
|
||||
// WHO WHEN WHAT
|
||||
// --- ---------- -----------------------------------------------------------
|
||||
// JLH 01/04/2006 Added changelog ;-)
|
||||
// (C) 2019 Underground Software
|
||||
//
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <SDL2/SDL.h>
|
||||
#include "sdlemu_config.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
#include "video.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
// Global variables
|
||||
|
||||
Settings settings;
|
||||
|
||||
// Private variables
|
||||
|
||||
static const char configPath[5][32] = {
|
||||
"./apple2.cfg", // CWD
|
||||
"~/apple2.cfg", // Home directory
|
||||
"~/.apple2/apple2.cfg", // Home under .apple2 directory
|
||||
"/etc/apple2.cfg", // /etc
|
||||
"apple2.cfg" // Somewhere in the path
|
||||
};
|
||||
|
||||
static int8_t configLoc = -1;
|
||||
static std::map<std::string, std::string> keystore;
|
||||
|
||||
// Private function prototypes
|
||||
|
||||
static int8_t FindConfig(void);
|
||||
static void ParseConfigFile(void);
|
||||
static bool GetValue(const char *, bool);
|
||||
static int GetValue(const char *, int);
|
||||
static const char * GetValue(const char *, const char *);
|
||||
static void SetValue(const char * key, bool value);
|
||||
static void SetValue(const char * key, int value);
|
||||
static void SetValue(const char * key, unsigned int value);
|
||||
static void SetValue(const char * key, const char * value);
|
||||
static void CheckForTrailingSlash(char * path);
|
||||
static void UpdateConfigFile(void);
|
||||
|
||||
|
||||
//
|
||||
@ -38,74 +52,31 @@ static void CheckForTrailingSlash(char * path);
|
||||
//
|
||||
void LoadSettings(void)
|
||||
{
|
||||
if (sdlemu_init_config("./apple2.cfg") == 0 // CWD
|
||||
&& sdlemu_init_config("~/apple2.cfg") == 0 // Home
|
||||
&& sdlemu_init_config("~/.apple2/apple2.cfg") == 0 // Home under .apple2 directory
|
||||
&& sdlemu_init_config("apple2.cfg") == 0) // Somewhere in the path
|
||||
WriteLog("Settings: Couldn't find configuration file. Using defaults...\n");
|
||||
ParseConfigFile();
|
||||
|
||||
settings.useJoystick = sdlemu_getval_bool("useJoystick", false);
|
||||
settings.joyport = sdlemu_getval_int("joyport", 0);
|
||||
settings.hardwareTypeNTSC = sdlemu_getval_bool("hardwareTypeNTSC", true);
|
||||
settings.frameSkip = sdlemu_getval_int("frameSkip", 0);
|
||||
settings.fullscreen = sdlemu_getval_bool("fullscreen", false);
|
||||
settings.useOpenGL = sdlemu_getval_bool("useOpenGL", true);
|
||||
settings.glFilter = sdlemu_getval_int("glFilterType", 0);
|
||||
settings.renderType = sdlemu_getval_int("renderType", 0);
|
||||
settings.autoStateSaving = sdlemu_getval_bool("autoSaveState", true);
|
||||
settings.useJoystick = GetValue("useJoystick", false);
|
||||
settings.joyport = GetValue("joyport", 0);
|
||||
settings.hardwareTypeNTSC = GetValue("hardwareTypeNTSC", true);
|
||||
settings.fullscreen = GetValue("fullscreen", false);
|
||||
settings.useOpenGL = GetValue("useOpenGL", true);
|
||||
settings.glFilter = GetValue("glFilterType", 0);
|
||||
settings.renderType = GetValue("renderType", 0);
|
||||
settings.autoStateSaving = GetValue("autoSaveState", true);
|
||||
|
||||
settings.winX = sdlemu_getval_int("windowX", 250);
|
||||
settings.winY = sdlemu_getval_int("windowY", 100);
|
||||
settings.winX = GetValue("windowX", 250);
|
||||
settings.winY = GetValue("windowY", 100);
|
||||
|
||||
// Keybindings in order of U, D, L, R, C, B, A, Op, Pa, 0-9, #, *
|
||||
settings.p1KeyBindings[0] = sdlemu_getval_int("p1k_up", SDL_SCANCODE_UP);
|
||||
settings.p1KeyBindings[1] = sdlemu_getval_int("p1k_down", SDL_SCANCODE_DOWN);
|
||||
settings.p1KeyBindings[2] = sdlemu_getval_int("p1k_left", SDL_SCANCODE_LEFT);
|
||||
settings.p1KeyBindings[3] = sdlemu_getval_int("p1k_right", SDL_SCANCODE_RIGHT);
|
||||
settings.p1KeyBindings[4] = sdlemu_getval_int("p1k_c", SDL_SCANCODE_Z);
|
||||
settings.p1KeyBindings[5] = sdlemu_getval_int("p1k_b", SDL_SCANCODE_X);
|
||||
settings.p1KeyBindings[6] = sdlemu_getval_int("p1k_a", SDL_SCANCODE_C);
|
||||
settings.p1KeyBindings[7] = sdlemu_getval_int("p1k_option", SDL_SCANCODE_APOSTROPHE);
|
||||
settings.p1KeyBindings[8] = sdlemu_getval_int("p1k_pause", SDL_SCANCODE_RETURN);
|
||||
settings.p1KeyBindings[9] = sdlemu_getval_int("p1k_0", SDL_SCANCODE_KP_0);
|
||||
settings.p1KeyBindings[10] = sdlemu_getval_int("p1k_1", SDL_SCANCODE_KP_1);
|
||||
settings.p1KeyBindings[11] = sdlemu_getval_int("p1k_2", SDL_SCANCODE_KP_2);
|
||||
settings.p1KeyBindings[12] = sdlemu_getval_int("p1k_3", SDL_SCANCODE_KP_3);
|
||||
settings.p1KeyBindings[13] = sdlemu_getval_int("p1k_4", SDL_SCANCODE_KP_4);
|
||||
settings.p1KeyBindings[14] = sdlemu_getval_int("p1k_5", SDL_SCANCODE_KP_5);
|
||||
settings.p1KeyBindings[15] = sdlemu_getval_int("p1k_6", SDL_SCANCODE_KP_6);
|
||||
settings.p1KeyBindings[16] = sdlemu_getval_int("p1k_7", SDL_SCANCODE_KP_7);
|
||||
settings.p1KeyBindings[17] = sdlemu_getval_int("p1k_8", SDL_SCANCODE_KP_8);
|
||||
settings.p1KeyBindings[18] = sdlemu_getval_int("p1k_9", SDL_SCANCODE_KP_9);
|
||||
settings.p1KeyBindings[19] = sdlemu_getval_int("p1k_pound", SDL_SCANCODE_KP_DIVIDE);
|
||||
settings.p1KeyBindings[20] = sdlemu_getval_int("p1k_star", SDL_SCANCODE_KP_MULTIPLY);
|
||||
// strcpy(settings.BIOSPath, sdlemu_getval_string("BIOSROM", "./ROMs/apple2e-enhanced.rom"));
|
||||
strcpy(settings.disksPath, GetValue("disks", "./disks/"));
|
||||
strcpy(settings.hd[0], GetValue("harddrive1", "./disks/Pitch-Dark-20180731.2mg"));
|
||||
strcpy(settings.hd[1], GetValue("harddrive2", ""));
|
||||
strcpy(settings.hd[2], GetValue("harddrive3", ""));
|
||||
strcpy(settings.hd[3], GetValue("harddrive4", ""));
|
||||
strcpy(settings.hd[4], GetValue("harddrive5", ""));
|
||||
strcpy(settings.hd[5], GetValue("harddrive6", ""));
|
||||
strcpy(settings.hd[6], GetValue("harddrive7", ""));
|
||||
strcpy(settings.autoStatePath, GetValue("autoStateFilename", "./apple2auto.state"));
|
||||
|
||||
settings.p2KeyBindings[0] = sdlemu_getval_int("p2k_up", SDL_SCANCODE_UP);
|
||||
settings.p2KeyBindings[1] = sdlemu_getval_int("p2k_down", SDL_SCANCODE_DOWN);
|
||||
settings.p2KeyBindings[2] = sdlemu_getval_int("p2k_left", SDL_SCANCODE_LEFT);
|
||||
settings.p2KeyBindings[3] = sdlemu_getval_int("p2k_right", SDL_SCANCODE_RIGHT);
|
||||
settings.p2KeyBindings[4] = sdlemu_getval_int("p2k_c", SDL_SCANCODE_Z);
|
||||
settings.p2KeyBindings[5] = sdlemu_getval_int("p2k_b", SDL_SCANCODE_X);
|
||||
settings.p2KeyBindings[6] = sdlemu_getval_int("p2k_a", SDL_SCANCODE_C);
|
||||
settings.p2KeyBindings[7] = sdlemu_getval_int("p2k_option", SDL_SCANCODE_APOSTROPHE);
|
||||
settings.p2KeyBindings[8] = sdlemu_getval_int("p2k_pause", SDL_SCANCODE_RETURN);
|
||||
settings.p2KeyBindings[9] = sdlemu_getval_int("p2k_0", SDL_SCANCODE_KP_0);
|
||||
settings.p2KeyBindings[10] = sdlemu_getval_int("p2k_1", SDL_SCANCODE_KP_1);
|
||||
settings.p2KeyBindings[11] = sdlemu_getval_int("p2k_2", SDL_SCANCODE_KP_2);
|
||||
settings.p2KeyBindings[12] = sdlemu_getval_int("p2k_3", SDL_SCANCODE_KP_3);
|
||||
settings.p2KeyBindings[13] = sdlemu_getval_int("p2k_4", SDL_SCANCODE_KP_4);
|
||||
settings.p2KeyBindings[14] = sdlemu_getval_int("p2k_5", SDL_SCANCODE_KP_5);
|
||||
settings.p2KeyBindings[15] = sdlemu_getval_int("p2k_6", SDL_SCANCODE_KP_6);
|
||||
settings.p2KeyBindings[16] = sdlemu_getval_int("p2k_7", SDL_SCANCODE_KP_7);
|
||||
settings.p2KeyBindings[17] = sdlemu_getval_int("p2k_8", SDL_SCANCODE_KP_8);
|
||||
settings.p2KeyBindings[18] = sdlemu_getval_int("p2k_9", SDL_SCANCODE_KP_9);
|
||||
settings.p2KeyBindings[19] = sdlemu_getval_int("p2k_pound", SDL_SCANCODE_KP_DIVIDE);
|
||||
settings.p2KeyBindings[20] = sdlemu_getval_int("p2k_star", SDL_SCANCODE_KP_MULTIPLY);
|
||||
|
||||
strcpy(settings.BIOSPath, sdlemu_getval_string("BIOSROM", "./ROMs/apple2e-enhanced.rom"));
|
||||
strcpy(settings.disksPath, sdlemu_getval_string("disks", "./disks"));
|
||||
strcpy(settings.hdPath, sdlemu_getval_string("harddrive", "./disks/Pitch-Dark-20180731.2mg"));
|
||||
strcpy(settings.autoStatePath, sdlemu_getval_string("autoStateFilename", "./apple2auto.state"));
|
||||
CheckForTrailingSlash(settings.disksPath);
|
||||
}
|
||||
|
||||
@ -116,6 +87,148 @@ void LoadSettings(void)
|
||||
void SaveSettings(void)
|
||||
{
|
||||
SDL_GetWindowPosition(sdlWindow, &settings.winX, &settings.winY);
|
||||
|
||||
SetValue("autoSaveState", settings.autoStateSaving);
|
||||
SetValue("autoStateFilename", settings.autoStatePath);
|
||||
SetValue("useJoystick", settings.useJoystick);
|
||||
SetValue("joyport", settings.joyport);
|
||||
SetValue("hardwareTypeNTSC", settings.hardwareTypeNTSC);
|
||||
SetValue("fullscreen", settings.fullscreen);
|
||||
SetValue("useOpenGL", settings.useOpenGL);
|
||||
SetValue("glFilterType", settings.glFilter);
|
||||
SetValue("renderType", settings.renderType);
|
||||
SetValue("windowX", settings.winX);
|
||||
SetValue("windowY", settings.winY);
|
||||
SetValue("disks", settings.disksPath);
|
||||
SetValue("harddrive1", settings.hd[0]);
|
||||
SetValue("harddrive2", settings.hd[1]);
|
||||
SetValue("harddrive3", settings.hd[2]);
|
||||
SetValue("harddrive4", settings.hd[3]);
|
||||
SetValue("harddrive5", settings.hd[4]);
|
||||
SetValue("harddrive6", settings.hd[5]);
|
||||
SetValue("harddrive7", settings.hd[6]);
|
||||
|
||||
UpdateConfigFile();
|
||||
}
|
||||
|
||||
|
||||
static int8_t FindConfig()
|
||||
{
|
||||
for(uint8_t i=0; i<5; i++)
|
||||
{
|
||||
FILE * f = fopen(configPath[i], "r");
|
||||
|
||||
if (f != NULL)
|
||||
{
|
||||
fclose(f);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Read & parse the configuration file into our keystore
|
||||
//
|
||||
static void ParseConfigFile(void)
|
||||
{
|
||||
configLoc = FindConfig();
|
||||
|
||||
if (configLoc == -1)
|
||||
{
|
||||
WriteLog("Settings: Couldn't find configuration file. Using defaults...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char * buf = (char *)ReadFile(configPath[configLoc]);
|
||||
std::string s(buf);
|
||||
|
||||
const std::string delim = "\n\r";
|
||||
std::string::size_type start = s.find_first_not_of(delim, 0);
|
||||
std::string::size_type end = s.find_first_of(delim, start);
|
||||
|
||||
while ((start != std::string::npos) || (end != std::string::npos))
|
||||
{
|
||||
std::string sub = s.substr(start, end - start);
|
||||
|
||||
if ((sub[0] != '#') && (sub[0] != '['))
|
||||
{
|
||||
std::string::size_type kStart = sub.find_first_not_of(" ", 0);
|
||||
std::string::size_type kEnd = sub.find_first_of(" ", kStart);
|
||||
std::string::size_type vStart = sub.find_first_of(" =\t\n\r", 0);
|
||||
std::string::size_type vEnd = sub.find_first_not_of(" =\t\n\r", vStart);
|
||||
|
||||
if ((kStart != std::string::npos) && (kEnd != std::string::npos)
|
||||
&& (vStart != std::string::npos) && (vEnd != std::string::npos))
|
||||
{
|
||||
keystore[sub.substr(kStart, kEnd - kStart)] = sub.substr(vEnd);
|
||||
}
|
||||
}
|
||||
|
||||
start = s.find_first_not_of(delim, end);
|
||||
end = s.find_first_of(delim, start);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
|
||||
static bool GetValue(const char * key, bool def)
|
||||
{
|
||||
if (keystore.find(key) == keystore.end())
|
||||
return def;
|
||||
|
||||
return (atoi(keystore[key].c_str()) == 0 ? false : true);
|
||||
}
|
||||
|
||||
|
||||
static int GetValue(const char * key, int def)
|
||||
{
|
||||
if (keystore.find(key) == keystore.end())
|
||||
return def;
|
||||
|
||||
return atoi(keystore[key].c_str());
|
||||
}
|
||||
|
||||
|
||||
static const char * GetValue(const char * key, const char * def)
|
||||
{
|
||||
if (keystore.find(key) == keystore.end())
|
||||
return def;
|
||||
|
||||
return keystore[key].c_str();
|
||||
}
|
||||
|
||||
|
||||
static void SetValue(const char * key, bool value)
|
||||
{
|
||||
keystore[key] = (value ? "1" : "0");
|
||||
}
|
||||
|
||||
|
||||
static void SetValue(const char * key, int value)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
sprintf(buf, "%i", value);
|
||||
keystore[key] = buf;
|
||||
}
|
||||
|
||||
|
||||
static void SetValue(const char * key, unsigned int value)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
sprintf(buf, "%u", value);
|
||||
keystore[key] = buf;
|
||||
}
|
||||
|
||||
|
||||
static void SetValue(const char * key, const char * value)
|
||||
{
|
||||
keystore[key] = value;
|
||||
}
|
||||
|
||||
|
||||
@ -124,8 +237,85 @@ void SaveSettings(void)
|
||||
//
|
||||
static void CheckForTrailingSlash(char * path)
|
||||
{
|
||||
if (strlen(path) > 0)
|
||||
if (path[strlen(path) - 1] != '/')
|
||||
strcat(path, "/"); // NOTE: Possible buffer overflow
|
||||
uint32_t len = strlen(path);
|
||||
|
||||
// If the length is greater than zero, and the last char in the string is
|
||||
// not a forward slash, and there's room for one more character, then add a
|
||||
// trailing slash.
|
||||
if ((len > 0) && (path[len - 1] != '/') && (len < MAX_PATH))
|
||||
strcat(path, "/");
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Update the values in the config file (if one exists) with updated values in
|
||||
// the keystore.
|
||||
//
|
||||
static void UpdateConfigFile(void)
|
||||
{
|
||||
// Sanity check
|
||||
if (configLoc == -1)
|
||||
{
|
||||
WriteLog("Settings: Creating default config...\n");
|
||||
configLoc = 0;
|
||||
}
|
||||
|
||||
char * buf = (char *)ReadFile(configPath[configLoc]);
|
||||
|
||||
FILE * f = fopen(configPath[configLoc], "w");
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
WriteLog("Settings: Could not open config file for writing!\n");
|
||||
free(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string s(buf);
|
||||
|
||||
const std::string delim = "\n\r";
|
||||
std::string::size_type start = 0;
|
||||
std::string::size_type end = s.find_first_of(delim, start);
|
||||
|
||||
while (end != std::string::npos)
|
||||
{
|
||||
if (end > start)
|
||||
{
|
||||
std::string sub = s.substr(start, end - start);
|
||||
|
||||
if ((sub[0] != '#') && (sub[0] != '['))
|
||||
{
|
||||
std::string::size_type kStart = sub.find_first_not_of(" ", 0);
|
||||
std::string::size_type kEnd = sub.find_first_of(" ", kStart);
|
||||
|
||||
if ((kStart != std::string::npos)
|
||||
&& (kEnd != std::string::npos))
|
||||
{
|
||||
std::string key = sub.substr(kStart, kEnd - kStart);
|
||||
|
||||
if (keystore.find(key) != keystore.end())
|
||||
{
|
||||
sub = key + " = " + keystore[key];
|
||||
keystore.erase(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(f, "%s\n", sub.c_str());
|
||||
}
|
||||
else
|
||||
fprintf(f, "\n");
|
||||
|
||||
start = end + 1;
|
||||
end = s.find_first_of(delim, start);
|
||||
}
|
||||
|
||||
std::map<std::string, std::string>::iterator i;
|
||||
|
||||
for(i=keystore.begin(); i!=keystore.end(); i++)
|
||||
fprintf(f, "%s = %s\n", i->first.c_str(), i->second.c_str());
|
||||
|
||||
fclose(f);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <limits.h>
|
||||
#define MAX_PATH _POSIX_PATH_MAX
|
||||
#else
|
||||
#include <stdlib.h> // for MAX_PATH on MinGW/Darwin
|
||||
#include <stdlib.h> // for MAX_PATH on MinGW/Darwin
|
||||
// Win64 kludge
|
||||
#ifndef MAX_PATH
|
||||
#define MAX_PATH _MAX_PATH // Ugh.
|
||||
@ -28,26 +28,19 @@ struct Settings
|
||||
bool fullscreen;
|
||||
bool useOpenGL;
|
||||
uint32_t glFilter;
|
||||
uint32_t frameSkip;
|
||||
uint32_t renderType;
|
||||
bool autoStateSaving; // Auto-state loading/saving on entry/exit
|
||||
|
||||
// Window settings
|
||||
|
||||
int winX;
|
||||
int winY;
|
||||
|
||||
// Keybindings in order of U, D, L, R, C, B, A, Op, Pa, 0-9, #, *
|
||||
|
||||
uint16_t p1KeyBindings[21];
|
||||
uint16_t p2KeyBindings[21];
|
||||
int winX, winY;
|
||||
|
||||
// Paths
|
||||
|
||||
char BIOSPath[MAX_PATH];
|
||||
char disksPath[MAX_PATH];
|
||||
char hdPath[MAX_PATH];
|
||||
char autoStatePath[MAX_PATH];
|
||||
char BIOSPath[MAX_PATH + 1];
|
||||
char disksPath[MAX_PATH + 1];
|
||||
char autoStatePath[MAX_PATH + 1];
|
||||
char hd[7][MAX_PATH + 1];
|
||||
};
|
||||
|
||||
// Render types
|
||||
|
@ -1197,6 +1197,12 @@ bool InitVideo(void)
|
||||
SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING,
|
||||
VIRTUAL_SCREEN_WIDTH, VIRTUAL_SCREEN_HEIGHT);
|
||||
|
||||
// Start in fullscreen, if user requested it via config file
|
||||
int response = SDL_SetWindowFullscreen(sdlWindow, (settings.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));
|
||||
|
||||
if (response != 0)
|
||||
WriteLog("Video::FullScreen: SDL error = %s\n", SDL_GetError());
|
||||
|
||||
SetupBlurTable();
|
||||
|
||||
WriteLog("Video: Successfully initialized.\n");
|
||||
|
@ -29,43 +29,63 @@ h1.title
|
||||
margin-top: 45px;
|
||||
}
|
||||
|
||||
img.flt-l
|
||||
.flt-l
|
||||
{
|
||||
float: left;
|
||||
margin-right: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
img.flt-r
|
||||
.flt-r
|
||||
{
|
||||
float: right;
|
||||
margin-left: 8px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
figure.flt-r
|
||||
.clr-l
|
||||
{
|
||||
clear: left;
|
||||
}
|
||||
|
||||
.clr-r
|
||||
{
|
||||
clear: right;
|
||||
}
|
||||
|
||||
figure
|
||||
{
|
||||
float: right;
|
||||
margin-top: 0;
|
||||
margin-left: 8px;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
font-style: italic;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
figure.small33
|
||||
img
|
||||
{
|
||||
width: 33%;
|
||||
max-width: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
img.small50
|
||||
img.border
|
||||
{
|
||||
margin: 5px;
|
||||
border: 1px solid #CF5F00;
|
||||
border-radius: 4px;
|
||||
padding: 5px;
|
||||
background-color: #FFAF00;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
}
|
||||
|
||||
.small33
|
||||
{
|
||||
width: 33%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.small50
|
||||
{
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
img.small33
|
||||
{
|
||||
width: 33%;
|
||||
height: 33%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#title
|
||||
|
Before Width: | Height: | Size: 715 B After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 191 KiB |
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 195 KiB |
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 134 KiB |
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 116 KiB |
@ -21,7 +21,7 @@
|
||||
<p><img class="flt-l" src="img/a2-icon-64x64.png">This is the home of the Apple2 portable Apple //e emulator. It’s based on GCC and SDL2, and runs on Linux, Windows, and MacOS X. It’s powered by Virtual 65C02™, and sports an easy to use yet powerful interface. It also has WOZ support! The source is licensed under the GPL version 3.</p>
|
||||
|
||||
<figure class="flt-r">
|
||||
<img src="img/ss-04s.png">
|
||||
<img class="border" src="img/ss-04s.png">
|
||||
<figcaption>Apple2 running “The Bard’s Tale”</figcaption>
|
||||
</figure>
|
||||
|
||||
@ -51,11 +51,21 @@
|
||||
|
||||
<h3>How to Use It</h3>
|
||||
|
||||
<figure class="flt-r small33">
|
||||
<img src="img/ss-01s.png" width="100%">
|
||||
<figure class="clr-r flt-r small33">
|
||||
<img class="border" src="img/ss-01s.png" width="100%">
|
||||
<figcaption>Apple2’s Control Bar</figcaption>
|
||||
</figure>
|
||||
|
||||
<figure class="clr-r flt-r small33">
|
||||
<img class="border" src="img/ss-02s.png">
|
||||
<figcaption>Apple2’s Disk Selector</figcaption>
|
||||
</figure>
|
||||
|
||||
<figure class="clr-r flt-r small33">
|
||||
<img class="border" src="img/ss-03s.png">
|
||||
<figcaption>Scrolling in the Disk Selector</figcaption>
|
||||
</figure>
|
||||
|
||||
<p>By mousing over the right side of the screen, the emulator control bar will appear; moving the mouse off of the bar will cause it to disappear. On the bar are seven icons, labeled (from top to bottom): power, disk one, disk two, swap disks, save state, load state, and configure. Here’s what they do:</p>
|
||||
|
||||
<ul>
|
||||
@ -68,10 +78,6 @@
|
||||
<li>Configure: configure various behaviors of the Apple2 emulator</li>
|
||||
</ul>
|
||||
|
||||
<img class="flt-l small33" src="img/ss-02s.png">
|
||||
|
||||
<img class="flt-l small33" src="img/ss-03s.png">
|
||||
|
||||
<p>In addition to the aforementioned control bar, Apple2 also supports the following function keys:</p>
|
||||
|
||||
<ul>
|
||||
|