diff --git a/Makefile b/Makefile index ac00d10..819bcc9 100644 --- a/Makefile +++ b/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 \ diff --git a/apple2.cfg b/apple2.cfg index e1e024c..c861df7 100644 --- a/apple2.cfg +++ b/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 + diff --git a/src/apple2.cpp b/src/apple2.cpp index 6d893fa..e97363a 100644 --- a/src/apple2.cpp +++ b/src/apple2.cpp @@ -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) { diff --git a/src/fileio.cpp b/src/fileio.cpp index efa4c61..ccda0d7 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -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; } diff --git a/src/fileio.h b/src/fileio.h index db11d87..e7a15bc 100644 --- a/src/fileio.h +++ b/src/fileio.h @@ -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); diff --git a/src/gui/diskselector.cpp b/src/gui/diskselector.cpp index 211cae7..8e7820f 100644 --- a/src/gui/diskselector.cpp +++ b/src/gui/diskselector.cpp @@ -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= 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 // @@ -21,9 +21,13 @@ // #include "gui.h" +#include #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 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 -#include -#include -#include -#include -#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 vec; - -void string_tokenize_variable() -{ - 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::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::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::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::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; -} diff --git a/src/sdlemu_config.h b/src/sdlemu_config.h deleted file mode 100644 index 2c2e6d5..0000000 --- a/src/sdlemu_config.h +++ /dev/null @@ -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 diff --git a/src/settings.cpp b/src/settings.cpp index 81b4d1d..cb287d9 100644 --- a/src/settings.cpp +++ b/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 -// -// WHO WHEN WHAT -// --- ---------- ----------------------------------------------------------- -// JLH 01/04/2006 Added changelog ;-) +// (C) 2019 Underground Software // #include "settings.h" #include +#include #include #include -#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 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::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); } diff --git a/src/settings.h b/src/settings.h index e97dcdf..41be54d 100644 --- a/src/settings.h +++ b/src/settings.h @@ -10,7 +10,7 @@ #include #define MAX_PATH _POSIX_PATH_MAX #else -#include // for MAX_PATH on MinGW/Darwin +#include // 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 diff --git a/src/video.cpp b/src/video.cpp index e027dfa..7309910 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -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"); diff --git a/web/apple2.css b/web/apple2.css index 99c7362..4f6f972 100644 --- a/web/apple2.css +++ b/web/apple2.css @@ -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 diff --git a/web/img/a2-icon-64x64.png b/web/img/a2-icon-64x64.png index cc7ade3..8cf0f94 100644 Binary files a/web/img/a2-icon-64x64.png and b/web/img/a2-icon-64x64.png differ diff --git a/web/img/ss-01s.png b/web/img/ss-01s.png index b05e38e..445a27b 100644 Binary files a/web/img/ss-01s.png and b/web/img/ss-01s.png differ diff --git a/web/img/ss-02s.png b/web/img/ss-02s.png index 4078a3a..1f6337c 100644 Binary files a/web/img/ss-02s.png and b/web/img/ss-02s.png differ diff --git a/web/img/ss-03s.png b/web/img/ss-03s.png index 3703aef..b4d1a76 100644 Binary files a/web/img/ss-03s.png and b/web/img/ss-03s.png differ diff --git a/web/img/ss-04.png b/web/img/ss-04.png index 638832c..7e58b63 100644 Binary files a/web/img/ss-04.png and b/web/img/ss-04.png differ diff --git a/web/img/ss-04s.png b/web/img/ss-04s.png index 9801ac8..faecb80 100644 Binary files a/web/img/ss-04s.png and b/web/img/ss-04s.png differ diff --git a/web/index.html b/web/index.html index 9310b46..b6dae4f 100644 --- a/web/index.html +++ b/web/index.html @@ -21,7 +21,7 @@

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.

- +
Apple2 running “The Bard’s Tale”
@@ -51,11 +51,21 @@

How to Use It

-
- +
+
Apple2’s Control Bar
+
+ +
Apple2’s Disk Selector
+
+ +
+ +
Scrolling in the Disk Selector
+
+

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:

    @@ -68,10 +78,6 @@
  • Configure: configure various behaviors of the Apple2 emulator
- - - -

In addition to the aforementioned control bar, Apple2 also supports the following function keys: