new configuration file location algorithm
This commit is contained in:
parent
28048d7087
commit
ddf97fff28
|
@ -221,6 +221,7 @@ if(APPLE)
|
|||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
BUNDLE DESTINATION .)
|
||||
configure_file(share/FixBundle.cmake.in ${PROJECT_BINARY_DIR}/FixBundle.cmake @ONLY)
|
||||
# TODO: how to make resources read-only (on Mac)
|
||||
install(SCRIPT ${PROJECT_BINARY_DIR}/FixBundle.cmake)
|
||||
elseif(WIN32)
|
||||
install(TARGETS ${APP_NAME}
|
||||
|
|
|
@ -178,7 +178,7 @@ bool E2wxApp::OnInit() {
|
|||
|
||||
|
||||
this->emu = new Emulator();
|
||||
Config cfg(this->arg_configfile);
|
||||
Config cfg(this->arg_configfile, this->opt_config_from_prefs_only);
|
||||
this->emu->config(cfg);
|
||||
this->emu->init();
|
||||
this->emu_timer = new EmuTimer(this->emu);
|
||||
|
@ -215,8 +215,8 @@ void E2wxApp::OnFatalException() {
|
|||
|
||||
|
||||
|
||||
static const wxCmdLineEntryDesc cmdLineDesc[] =
|
||||
{
|
||||
static const wxCmdLineEntryDesc cmdLineDesc[] = {
|
||||
{ wxCMD_LINE_SWITCH, "p", "prefs", "Read config only from preferences, never an external file.", wxCMD_LINE_VAL_NONE },
|
||||
{ wxCMD_LINE_PARAM, NULL, NULL, "config-file", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
|
||||
wxCMD_LINE_DESC_END
|
||||
};
|
||||
|
@ -231,6 +231,8 @@ bool E2wxApp::OnCmdLineParsed(wxCmdLineParser& parser) {
|
|||
return false;
|
||||
}
|
||||
|
||||
this->opt_config_from_prefs_only = parser.Found("p");
|
||||
|
||||
const int n = parser.GetParamCount();
|
||||
|
||||
if (n <= 0) {
|
||||
|
|
|
@ -59,6 +59,7 @@ class E2wxApp : public wxApp {
|
|||
std::filesystem::path confdir;
|
||||
std::filesystem::path docsdir;
|
||||
std::filesystem::path arg_configfile;
|
||||
bool opt_config_from_prefs_only;
|
||||
EmuTimer *emu_timer;
|
||||
Emulator *emu;
|
||||
|
||||
|
|
|
@ -134,13 +134,7 @@ void PreferencesDialog::BuildItemTree() {
|
|||
}
|
||||
|
||||
void PreferencesDialog::OnInit() {
|
||||
wxConfigBase *appconf = wxConfigBase::Get();
|
||||
if (!appconf->Read("/ActivePreferences/name", &this->active)) {
|
||||
// TODO what to do when no config?
|
||||
this->active = "epple2";
|
||||
appconf->Write("/ActivePreferences/name", this->active);
|
||||
appconf->Flush();
|
||||
}
|
||||
wxConfigBase::Get()->Read("/ActivePreferences/name", &this->active, "");
|
||||
|
||||
wxXmlResource::Get()->LoadDialog(this, this->parent, "Preferences");
|
||||
|
||||
|
@ -163,6 +157,8 @@ void PreferencesDialog::Save(const std::filesystem::path& to) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: enable/disable the various buttons, etc. (currently all are enabled even if not functional)
|
||||
|
||||
void PreferencesDialog::OnTreeSelectionChanged(wxTreeEvent& evt) {
|
||||
// note: we don't get the first select upon dialog creation,
|
||||
// nor the final de-select upon dialog destruction
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "configep2.h"
|
||||
|
||||
#include "E2wxApp.h"
|
||||
|
||||
#include "e2filesystem.h"
|
||||
#include "apple2.h"
|
||||
#include "memory.h"
|
||||
#include "memoryrandomaccess.h"
|
||||
|
@ -36,6 +36,8 @@
|
|||
#include <wx/config.h>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <fstream>
|
||||
|
@ -47,6 +49,10 @@
|
|||
|
||||
|
||||
|
||||
static const wxString DEFAULT_CONFIG_NAME{"epple2"};
|
||||
|
||||
|
||||
|
||||
#define K 1024u
|
||||
|
||||
static std::uint16_t memory_block_size(const std::string &block_size) {
|
||||
|
@ -61,15 +67,11 @@ static std::uint16_t memory_block_size(const std::string &block_size) {
|
|||
|
||||
|
||||
|
||||
unsigned char Config::disk_mask(0);
|
||||
|
||||
Config::Config(const std::filesystem::path& f):
|
||||
file_path(f)
|
||||
{
|
||||
Config::Config(const std::filesystem::path& f, bool p): file_path {f}, prefs_only {p} {
|
||||
}
|
||||
|
||||
Config::~Config()
|
||||
{
|
||||
Config::~Config() {
|
||||
}
|
||||
|
||||
static void strip_comment(std::string& str)
|
||||
|
@ -99,140 +101,211 @@ static void trim(std::string& str)
|
|||
}
|
||||
}
|
||||
|
||||
void Config::parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2)
|
||||
{
|
||||
std::ifstream* pConfig;
|
||||
|
||||
std::filesystem::path path(this->file_path);
|
||||
|
||||
if (!path.empty())
|
||||
{
|
||||
pConfig = new std::ifstream(path);
|
||||
if (!pConfig->is_open())
|
||||
{
|
||||
// TODO use filename only and look in standard resources
|
||||
std::stringstream ss;
|
||||
ss << "Cannot open config file " << this->file_path;
|
||||
throw std::runtime_error(ss.str());
|
||||
/*
|
||||
* Searches for config file with the given name in the preferences areas.
|
||||
* The name must not be a filesystem path.
|
||||
* The actual config files themselves have ".conf" extension.
|
||||
* The preferences areas are searched in this order:
|
||||
* user area
|
||||
* built-in area
|
||||
*
|
||||
* If successful, returns an open stream, caller is responsible for deleting it
|
||||
* Otherwise, returns null
|
||||
*/
|
||||
std::ifstream *Config::openFilePref(const wxString& s_name) {
|
||||
std::ifstream *ret = nullptr;
|
||||
|
||||
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "config file name was specified as: " << s_name;
|
||||
std::filesystem::path path_name = path_from_string(s_name);
|
||||
if (path_name.has_parent_path()) {
|
||||
BOOST_LOG_TRIVIAL(error) << "invalid name for config file (paths are not allowed): " << path_name.c_str();
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (path_name.empty()) {
|
||||
BOOST_LOG_TRIVIAL(error) << "invalid: empty name for config file";
|
||||
return ret;
|
||||
}
|
||||
|
||||
// required file extension for any file to be recognized as a config file
|
||||
path_name = path_from_string(s_name+".conf");
|
||||
|
||||
|
||||
|
||||
std::filesystem::path path;
|
||||
|
||||
path = valid_input_file(path_name, wxGetApp().GetConfigDir());
|
||||
if (!path.empty()) {
|
||||
ret = new std::ifstream(path);
|
||||
if (ret->is_open()) {
|
||||
return ret; // Found specified config file in user area of preferences
|
||||
}
|
||||
delete ret;
|
||||
ret = nullptr;
|
||||
}
|
||||
|
||||
path = valid_input_file(path_name, wxGetApp().GetResDir());
|
||||
if (!path.empty()) {
|
||||
ret = new std::ifstream(path);
|
||||
if (ret->is_open()) {
|
||||
return ret; // Found specified config file in built-in area of preferences
|
||||
}
|
||||
delete ret;
|
||||
ret = nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
// TODO config file location, how to be backwardly compatible?
|
||||
wxString user_config;
|
||||
if (!wxConfigBase::Get()->Read("/ActivePreferences/name", &user_config)) {
|
||||
// TODO what to do when no config?
|
||||
user_config = "epple2";
|
||||
}
|
||||
user_config += ".conf";
|
||||
std::filesystem::path user_path{user_config.wc_str()};
|
||||
return ret;
|
||||
}
|
||||
|
||||
path = wxGetApp().GetConfigDir() / user_path;
|
||||
std::cout << "looking for config file: " << path << std::endl;
|
||||
pConfig = new std::ifstream(path);
|
||||
if (!pConfig->is_open()) {
|
||||
path = wxGetApp().GetResDir() / user_path;
|
||||
std::cout << "looking for config file: " << path << std::endl;
|
||||
pConfig = new std::ifstream(path);
|
||||
if (!pConfig->is_open()) {
|
||||
path.clear();
|
||||
std::ifstream *Config::openFileExternal(const std::filesystem::path& path) {
|
||||
std::ifstream *ret = nullptr;
|
||||
|
||||
const std::filesystem::path p = valid_input_file(path);
|
||||
if (!p.empty()) {
|
||||
ret = new std::ifstream(p);
|
||||
if (ret->is_open()) {
|
||||
return ret;
|
||||
}
|
||||
delete ret;
|
||||
ret = nullptr;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const std::array rs_path_legacy{
|
||||
"./etc/epple2/epple2.conf",
|
||||
ETCDIR "/epple2/epple2.conf",
|
||||
"/etc/epple2/epple2.conf",
|
||||
"/etc/epple2.conf",
|
||||
"./epple2.conf",
|
||||
};
|
||||
|
||||
std::ifstream *Config::openFileLegacy() {
|
||||
std::ifstream *ret = nullptr;
|
||||
|
||||
for (const auto &s_path_legacy : rs_path_legacy) {
|
||||
if ((ret = openFileExternal(std::filesystem::path{s_path_legacy})) != nullptr) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Algorithm to locate and open the configuration file, as specified by
|
||||
* the user, either on command line, or via preferences, allowing for
|
||||
* backward compatibility with legacy file locations.
|
||||
*/
|
||||
std::ifstream *Config::openFile() {
|
||||
std::ifstream *ret = nullptr;
|
||||
|
||||
if (this->file_path.empty()) {
|
||||
wxString cname{};
|
||||
const bool stored_prefs_found = wxConfigBase::Get()->Read("/ActivePreferences/name", &cname, DEFAULT_CONFIG_NAME);
|
||||
|
||||
if (stored_prefs_found) {
|
||||
ret = openFilePref(cname);
|
||||
if (ret != nullptr) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!this->prefs_only) {
|
||||
ret = openFileLegacy();
|
||||
if (ret != nullptr) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!this->prefs_only) {
|
||||
ret = openFileLegacy();
|
||||
if (ret != nullptr) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = openFilePref(cname);
|
||||
if (ret != nullptr) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!this->prefs_only) {
|
||||
ret = openFileExternal(this->file_path);
|
||||
if (ret != nullptr) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = openFilePref(this->file_path.c_str());
|
||||
if (ret != nullptr) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Config::parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2) {
|
||||
std::ifstream *p_ifstream_config = openFile();
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
std::cout << "standard config file location: " ETCDIR "/epple2/epple2.conf" << std::endl;
|
||||
|
||||
/*
|
||||
On Windows, the default directory will be
|
||||
C:\Program Files\Epple2 if they start the
|
||||
program from the Start Menu; therefore
|
||||
etc/epple2/epple2.conf would be
|
||||
C:\Program Files\epple2\etc\epple2\epple2.conf
|
||||
On Linux... the current directory could be
|
||||
anything, so this probably won't find it (unless
|
||||
the current directory is /).
|
||||
*/
|
||||
path = "etc/epple2/epple2.conf";
|
||||
pConfig = new std::ifstream(path);
|
||||
if (!pConfig->is_open())
|
||||
path.clear();
|
||||
}
|
||||
if (path.empty())
|
||||
{
|
||||
/*
|
||||
This is primarily for Linux. If configured for
|
||||
a PREFIX of "/usr/local", then this would be
|
||||
/usr/local/etc/epple2/epple2.conf
|
||||
*/
|
||||
path = ETCDIR "/epple2/epple2.conf";
|
||||
pConfig = new std::ifstream(path);
|
||||
if (!pConfig->is_open())
|
||||
path.clear();
|
||||
}
|
||||
if (path.empty())
|
||||
{
|
||||
/*
|
||||
Try a likely linux location
|
||||
*/
|
||||
path = "/etc/epple2/epple2.conf";
|
||||
pConfig = new std::ifstream(path);
|
||||
if (!pConfig->is_open())
|
||||
path.clear();
|
||||
}
|
||||
if (path.empty())
|
||||
{
|
||||
/*
|
||||
Try another likely linux location
|
||||
*/
|
||||
path = "/etc/epple2.conf";
|
||||
pConfig = new std::ifstream(path);
|
||||
if (!pConfig->is_open())
|
||||
path.clear();
|
||||
}
|
||||
if (path.empty())
|
||||
{
|
||||
/*
|
||||
Last effort to find it.
|
||||
*/
|
||||
path = "epple2.conf";
|
||||
pConfig = new std::ifstream(path);
|
||||
if (!pConfig->is_open())
|
||||
path.clear();
|
||||
}
|
||||
if (path.empty())
|
||||
{
|
||||
if (p_ifstream_config == nullptr) {
|
||||
std::cerr << "Cannot find config file. Running without any RAM, ROM, or cards." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << "Reading configuration from file: " << path << std::endl;
|
||||
|
||||
std::string line;
|
||||
std::getline(*pConfig,line);
|
||||
while (!pConfig->eof())
|
||||
{
|
||||
std::getline(*p_ifstream_config, line);
|
||||
while (!p_ifstream_config->eof()) {
|
||||
strip_comment(line);
|
||||
trim(line);
|
||||
if (!line.empty())
|
||||
{
|
||||
parseLine(line,ram,rom,slts,revision,gui,cassetteIn,cassetteOut,apple2);
|
||||
if (!line.empty()) {
|
||||
// TODO "parseLine" will become Command::execute, or similar
|
||||
parseLine(line, ram, rom, slts, revision, gui, cassetteIn, cassetteOut, apple2);
|
||||
}
|
||||
std::getline(*pConfig,line);
|
||||
std::getline(*p_ifstream_config, line);
|
||||
}
|
||||
pConfig->close();
|
||||
delete pConfig;
|
||||
|
||||
delete p_ifstream_config;
|
||||
|
||||
// TODO: make sure there is no more than ONE stdin and/or ONE stdout card
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Config::parseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2)
|
||||
{
|
||||
try
|
||||
|
@ -519,6 +592,12 @@ void Config::tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memo
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned char Config::disk_mask(0);
|
||||
|
||||
|
||||
|
||||
void Config::loadDisk(Slots& slts, int slot, int drive, const std::string& fnib)
|
||||
{
|
||||
if (drive < 1 || 2 < drive)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#ifndef CONFIGEP2_H
|
||||
#define CONFIGEP2_H
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
class Memory;
|
||||
|
@ -34,11 +35,19 @@ public:
|
|||
ConfigException(const std::string& msg) : msg(msg) {}
|
||||
};
|
||||
|
||||
// TODO split out all static things into their own class (and don't make them static)
|
||||
// Remember that, besides config, also command line entry calls parseLine
|
||||
// This will also help with adding menu items in place of commands
|
||||
class Config {
|
||||
private:
|
||||
const std::filesystem::path file_path;
|
||||
const bool prefs_only;
|
||||
static unsigned char disk_mask;
|
||||
|
||||
std::ifstream *openFile();
|
||||
std::ifstream *openFilePref(const wxString& s_name);
|
||||
std::ifstream *openFileExternal(const std::filesystem::path& path);
|
||||
std::ifstream *openFileLegacy();
|
||||
static void loadDisk(Slots& slts, int slot, int drive, const std::string& fnib);
|
||||
static void unloadDisk(Slots& slts, int slot, int drive);
|
||||
static void saveDisk(Slots& slts, int slot, int drive);
|
||||
|
@ -46,7 +55,7 @@ private:
|
|||
static void tryParseLine(const std::string& line, MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2);
|
||||
|
||||
public:
|
||||
Config(const std::filesystem::path& f);
|
||||
Config(const std::filesystem::path& f, bool p);
|
||||
~Config();
|
||||
|
||||
void parse(MemoryRandomAccess& ram, Memory& rom, Slots& slts, int& revision, ScreenImage& gui, CassetteIn& cassetteIn, CassetteOut& cassetteOut, Apple2* apple2);
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include <filesystem>
|
||||
|
||||
std::filesystem::path valid_input_file(const std::filesystem::path path, const std::filesystem::path base);
|
||||
std::filesystem::path valid_input_file(const std::filesystem::path path, const std::filesystem::path base = std::filesystem::path{});
|
||||
std::filesystem::path path_from_string(const wxString& s);
|
||||
|
||||
#endif /* E2FILESYSTEM_H */
|
||||
|
|
|
@ -116,6 +116,10 @@ bool WozFile::load(const std::filesystem::path& orig_file) {
|
|||
printf("Reading WOZ 2.0 file: %s\n", orig_file.c_str());
|
||||
|
||||
std::filesystem::path filePath = valid_input_file(orig_file, wxGetApp().GetResDir());
|
||||
if (filePath.empty()) {
|
||||
printf("Error opening WOZ file.\n");
|
||||
return false;
|
||||
}
|
||||
std::ifstream *in = new std::ifstream(filePath, std::ios::binary|std::ios::in);
|
||||
if (!in->is_open()) {
|
||||
printf("Error opening file: %d\n", errno);
|
||||
|
|
Loading…
Reference in New Issue