333 lines
8.1 KiB
C++
333 lines
8.1 KiB
C++
//
|
|
// settings.cpp: Apple2 configuration loading/saving support
|
|
//
|
|
// by James Hammons
|
|
// (C) 2019 Underground Software
|
|
//
|
|
|
|
#include "settings.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <map>
|
|
#include <string>
|
|
#include <SDL2/SDL.h>
|
|
#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<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);
|
|
|
|
|
|
//
|
|
// 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<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);
|
|
}
|
|
|