// // settings.cpp: Apple2 configuration loading/saving support // // by James Hammons // (C) 2019 Underground Software // #include "settings.h" #include #include #include #include #include "fileio.h" #include "log.h" #include "video.h" // 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); // // Load Apple2's settings // void LoadSettings(void) { ParseConfigFile(); 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 = GetValue("windowX", 250); settings.winY = GetValue("windowY", 100); // strcpy(settings.BIOSPath, sdlemu_getval_string("BIOSROM", "./ROMs/apple2e-enhanced.rom")); strcpy(settings.disksPath, GetValue("disks", "./disks/")); strcpy(settings.hd[0], GetValue("harddrive1", "")); 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.cardSlot[0] = GetValue("card1", 6); // Disk ][ settings.cardSlot[1] = GetValue("card2", 0); // Disk ][ settings.cardSlot[2] = GetValue("card3", 4); // Mockingboard settings.cardSlot[3] = GetValue("card4", 0); // Mockingboard settings.cardSlot[4] = GetValue("card5", 0); // AHSSCSI CheckForTrailingSlash(settings.disksPath); } // // Save Apple2's settings // 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]); SetValue("card1", settings.cardSlot[0]); SetValue("card2", settings.cardSlot[1]); SetValue("card3", settings.cardSlot[2]); SetValue("card4", settings.cardSlot[3]); SetValue("card5", settings.cardSlot[4]); 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; } // // Check path for a trailing slash, and append if not present // static void CheckForTrailingSlash(char * path) { 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); }