From ebea5610ebf593a5cca707dcef3395c365dfc6a0 Mon Sep 17 00:00:00 2001 From: ejaquay Date: Sat, 17 Jan 2026 16:35:09 -0500 Subject: [PATCH 01/10] Change initial_settings to settings --- .../util/{initial_settings.h => settings.h} | 29 ++++++++++++------- .../{initial_settings.cpp => settings.cpp} | 19 +++++------- mpi/multipak_configuration.cpp | 22 +++++++++++++- 3 files changed, 47 insertions(+), 23 deletions(-) rename libcommon/include/vcc/util/{initial_settings.h => settings.h} (66%) rename libcommon/src/util/{initial_settings.cpp => settings.cpp} (83%) diff --git a/libcommon/include/vcc/util/initial_settings.h b/libcommon/include/vcc/util/settings.h similarity index 66% rename from libcommon/include/vcc/util/initial_settings.h rename to libcommon/include/vcc/util/settings.h index 820d5d9c..a5eaf1e5 100644 --- a/libcommon/include/vcc/util/initial_settings.h +++ b/libcommon/include/vcc/util/settings.h @@ -19,29 +19,38 @@ #include // defines LIBCOMMON_EXPORT if libcommon is a DLL #include -namespace VCC::Core +namespace VCC::Util { - - class initial_settings + class settings { public: - using path_type = std::string; using string_type = std::string; public: + LIBCOMMON_EXPORT explicit settings(path_type path); - LIBCOMMON_EXPORT explicit initial_settings(path_type path); + LIBCOMMON_EXPORT void write + (const string_type& section, + const string_type& key, + int value) const; - LIBCOMMON_EXPORT void write(const string_type& section, const string_type& key, int value) const; - LIBCOMMON_EXPORT void write(const string_type& section, const string_type& key, const string_type& value) const; + LIBCOMMON_EXPORT void write + (const string_type& section, + const string_type& key, + const string_type& value) const; - LIBCOMMON_EXPORT int read(const string_type& section, const string_type& key, int default_value) const; - LIBCOMMON_EXPORT string_type read(const string_type& section, const string_type& key, const string_type& default_value = {}) const; + LIBCOMMON_EXPORT int read + (const string_type& section, + const string_type& key, + int default_value) const; + LIBCOMMON_EXPORT string_type read + (const string_type& section, + const string_type& key, + const string_type& default_value = {}) const; private: - const path_type path_; }; } diff --git a/libcommon/src/util/initial_settings.cpp b/libcommon/src/util/settings.cpp similarity index 83% rename from libcommon/src/util/initial_settings.cpp rename to libcommon/src/util/settings.cpp index 8f635c08..6cea7471 100644 --- a/libcommon/src/util/initial_settings.cpp +++ b/libcommon/src/util/settings.cpp @@ -18,16 +18,13 @@ #include #include - -namespace VCC::Core +namespace VCC::Util { - - initial_settings::initial_settings(path_type path) + settings::settings(path_type path) : path_(move(path)) - { - } + {} - void initial_settings::write( + void settings::write( const string_type& section, const string_type& key, int value) const @@ -39,7 +36,7 @@ namespace VCC::Core path_.c_str()); } - void initial_settings::write( + void settings::write( const string_type& section, const string_type& key, const string_type& value) const @@ -47,17 +44,16 @@ namespace VCC::Core WritePrivateProfileString(section.c_str(), key.c_str(), value.c_str(), path_.c_str()); } - int initial_settings::read(const string_type& section, const string_type& key, int default_value) const + int settings::read(const string_type& section, const string_type& key, int default_value) const { return GetPrivateProfileInt(section.c_str(), key.c_str(), default_value, path_.c_str()); } - initial_settings::string_type initial_settings::read( + settings::string_type settings::read( const string_type& section, const string_type& key, const string_type& default_value) const { - // FIXME-CHET: Need to determine the size of the string char loaded_string[MAX_PATH] = {}; GetPrivateProfileString( @@ -70,5 +66,4 @@ namespace VCC::Core return loaded_string; } - } diff --git a/mpi/multipak_configuration.cpp b/mpi/multipak_configuration.cpp index fe6894bd..a54531cf 100644 --- a/mpi/multipak_configuration.cpp +++ b/mpi/multipak_configuration.cpp @@ -1,7 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2015 by Joseph Forgione +// This file is part of VCC (Virtual Color Computer). +// +// This is an expansion module for the Vcc Emulator. It simulated the functions +// of the TRS-80 Multi-Pak Interface. +// +// VCC (Virtual Color Computer) is free software: you can redistribute itand/or +// modify it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// VCC (Virtual Color Computer) 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 +// VCC (Virtual Color Computer). If not, see . +//////////////////////////////////////////////////////////////////////////////// #include "multipak_configuration.h" #include -using settings = ::VCC::Core::initial_settings; +using settings = ::VCC::Util::settings; // Constructor sets the section multipak_configuration::multipak_configuration(string_type section) From 299f6aad1c648a61cb492103c8b5522af6014b86 Mon Sep 17 00:00:00 2001 From: ejaquay Date: Sun, 18 Jan 2026 00:15:51 -0500 Subject: [PATCH 02/10] Fix SDC trashing roms --- libcommon/include/vcc/util/fileutil.h | 39 +++++ libcommon/include/vcc/util/settings.h | 51 ++++--- libcommon/libcommon.vcxproj | 4 +- libcommon/libcommon.vcxproj.filters | 4 +- libcommon/src/util/fileutil.cpp | 3 + libcommon/src/util/settings.cpp | 91 +++++++---- mpi/multipak_configuration.cpp | 4 +- mpi/multipak_configuration.h | 20 +++ sdc/sdc.cpp | 210 ++++++++++++++------------ 9 files changed, 276 insertions(+), 150 deletions(-) diff --git a/libcommon/include/vcc/util/fileutil.h b/libcommon/include/vcc/util/fileutil.h index eeb5075b..7209fce8 100644 --- a/libcommon/include/vcc/util/fileutil.h +++ b/libcommon/include/vcc/util/fileutil.h @@ -57,4 +57,43 @@ namespace VCC::Util { // If path is in the application directory strip directory LIBCOMMON_EXPORT std::string strip_application_path(std::string path); + + // TODO: following stuff should go elsewhere + + // Return string with case conversion + inline std::string to_lower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c){ return std::tolower(c); }); + return s; + } + + inline std::string to_upper(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c){ return std::toupper(c); }); + return s; + } + + // Convert case of string inplace + inline void make_lower(std::string& s) { + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c){ return std::tolower(c); }); + } + + inline void make_lower(char* s) { + if (!s) return; + for (char* p = s; *p; ++p) + *p = static_cast(std::tolower(static_cast(*p))); + } + + inline void make_upper(std::string& s) { + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c){ return std::toupper(c); }); + } + + inline void make_upper(char* s) { + if (!s) return; + for (char* p = s; *p; ++p) + *p = static_cast(std::toupper(static_cast(*p))); + } + } diff --git a/libcommon/include/vcc/util/settings.h b/libcommon/include/vcc/util/settings.h index a5eaf1e5..1be24826 100644 --- a/libcommon/include/vcc/util/settings.h +++ b/libcommon/include/vcc/util/settings.h @@ -1,17 +1,17 @@ //////////////////////////////////////////////////////////////////////////////// // Copyright 2015 by Joseph Forgione // This file is part of VCC (Virtual Color Computer). -// +// // VCC (Virtual Color Computer) is free software: you can redistribute itand/or // modify it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or (at your // option) any later version. -// +// // VCC (Virtual Color Computer) 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 // VCC (Virtual Color Computer). If not, see . //////////////////////////////////////////////////////////////////////////////// @@ -25,31 +25,48 @@ namespace VCC::Util { public: using path_type = std::string; - using string_type = std::string; public: LIBCOMMON_EXPORT explicit settings(path_type path); + // write string value LIBCOMMON_EXPORT void write - (const string_type& section, - const string_type& key, - int value) const; + (const std::string& section, + const std::string& key, + const std::string& value) const; + // write int value LIBCOMMON_EXPORT void write - (const string_type& section, - const string_type& key, - const string_type& value) const; + (const std::string& section, + const std::string& key, + int value) const; + + // write bool value + LIBCOMMON_EXPORT void write_bool + (const std::string& section, + const std::string& key, + bool value) const; + // get char * value + LIBCOMMON_EXPORT void read + (const std::string& section, + const std::string& key, + const std::string& default_value, + char* buffer, + size_t buffer_size) const; + + // get string value + LIBCOMMON_EXPORT std::string read + (const std::string& section, + const std::string& key, + const std::string& default_value = {}) const; + + // get int value LIBCOMMON_EXPORT int read - (const string_type& section, - const string_type& key, + (const std::string& section, + const std::string& key, int default_value) const; - LIBCOMMON_EXPORT string_type read - (const string_type& section, - const string_type& key, - const string_type& default_value = {}) const; - private: const path_type path_; }; diff --git a/libcommon/libcommon.vcxproj b/libcommon/libcommon.vcxproj index eb95aece..70dae6ae 100644 --- a/libcommon/libcommon.vcxproj +++ b/libcommon/libcommon.vcxproj @@ -112,7 +112,7 @@ - + @@ -140,7 +140,7 @@ - + diff --git a/libcommon/libcommon.vcxproj.filters b/libcommon/libcommon.vcxproj.filters index 37e33927..eec1b6f2 100644 --- a/libcommon/libcommon.vcxproj.filters +++ b/libcommon/libcommon.vcxproj.filters @@ -20,7 +20,7 @@ core - + core @@ -70,7 +70,7 @@ core - + core diff --git a/libcommon/src/util/fileutil.cpp b/libcommon/src/util/fileutil.cpp index ba1e989b..1f344d65 100644 --- a/libcommon/src/util/fileutil.cpp +++ b/libcommon/src/util/fileutil.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -139,4 +141,5 @@ namespace VCC::Util } return path; } + } diff --git a/libcommon/src/util/settings.cpp b/libcommon/src/util/settings.cpp index 6cea7471..95c643c8 100644 --- a/libcommon/src/util/settings.cpp +++ b/libcommon/src/util/settings.cpp @@ -1,3 +1,4 @@ +//#define USE_LOGGING //////////////////////////////////////////////////////////////////////////////// // Copyright 2015 by Joseph Forgione // This file is part of VCC (Virtual Color Computer). @@ -15,7 +16,9 @@ // You should have received a copy of the GNU General Public License along with // VCC (Virtual Color Computer). If not, see . //////////////////////////////////////////////////////////////////////////////// -#include + +#include +#include #include namespace VCC::Util @@ -24,46 +27,80 @@ namespace VCC::Util : path_(move(path)) {} + // save string value void settings::write( - const string_type& section, - const string_type& key, - int value) const + const std::string& section, + const std::string& key, + const std::string& value) const + { + ::WritePrivateProfileString( + section.c_str(), + key.c_str(), + value.c_str(), + path_.c_str()); + } + + // save int value + void settings::write + (const std::string& section, + const std::string& key, + int value) const { - WritePrivateProfileString( + ::WritePrivateProfileString( section.c_str(), key.c_str(), std::to_string(value).c_str(), path_.c_str()); } - void settings::write( - const string_type& section, - const string_type& key, - const string_type& value) const - { - WritePrivateProfileString(section.c_str(), key.c_str(), value.c_str(), path_.c_str()); + // save bool value + void settings::write_bool + (const std::string& section, + const std::string& key, + bool value) const + { + int ival = value ? 1 : 0; + write(section, key, ival); } - int settings::read(const string_type& section, const string_type& key, int default_value) const + // get char * value + void settings::read + (const std::string& section, + const std::string& key, + const std::string& default_value, + char* buffer, + size_t buffer_size) const { - return GetPrivateProfileInt(section.c_str(), key.c_str(), default_value, path_.c_str()); + ::GetPrivateProfileStringA( + section.c_str(), + key.c_str(), + default_value.c_str(), + buffer, + static_cast(buffer_size), + path_.c_str()); } - settings::string_type settings::read( - const string_type& section, - const string_type& key, - const string_type& default_value) const + // get string value + std::string settings::read + (const std::string& section, + const std::string& key, + const std::string& default_value) const { - char loaded_string[MAX_PATH] = {}; - - GetPrivateProfileString( - section.c_str(), - key.c_str(), - default_value.c_str(), - loaded_string, - MAX_PATH, - path_.c_str()); + char buffer[MAX_PATH] = {}; + read(section, key, default_value, buffer, sizeof(buffer)); + return std::string(buffer); + } - return loaded_string; + // get int value + int settings::read + (const std::string& section, + const std::string& key, + int default_value) const + { + return ::GetPrivateProfileInt( + section.c_str(), + key.c_str(), + default_value, + path_.c_str()); } } diff --git a/mpi/multipak_configuration.cpp b/mpi/multipak_configuration.cpp index a54531cf..1e1415f7 100644 --- a/mpi/multipak_configuration.cpp +++ b/mpi/multipak_configuration.cpp @@ -19,7 +19,7 @@ // VCC (Virtual Color Computer). If not, see . //////////////////////////////////////////////////////////////////////////////// #include "multipak_configuration.h" -#include +#include using settings = ::VCC::Util::settings; @@ -60,7 +60,7 @@ multipak_configuration::path_type multipak_configuration::last_accessed_dll_path return settings(configuration_path()).read("DefaultPaths", "MPIPath"); } -// selected slot +// selected slot FIXME:: it is not clear that slot is an int void multipak_configuration::selected_slot(slot_id_type slot) const { settings(configuration_path()).write(section_, "SWPOSITION", slot); diff --git a/mpi/multipak_configuration.h b/mpi/multipak_configuration.h index 18d9e53a..37b3be7e 100644 --- a/mpi/multipak_configuration.h +++ b/mpi/multipak_configuration.h @@ -1,3 +1,23 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2015 by Joseph Forgione +// This file is part of VCC (Virtual Color Computer). +// +// This is an expansion module for the Vcc Emulator. It simulated the functions +// of the TRS-80 Multi-Pak Interface. +// +// VCC (Virtual Color Computer) is free software: you can redistribute itand/or +// modify it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or (at your +// option) any later version. +// +// VCC (Virtual Color Computer) 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 +// VCC (Virtual Color Computer). If not, see . +//////////////////////////////////////////////////////////////////////////////// #pragma once #include diff --git a/sdc/sdc.cpp b/sdc/sdc.cpp index 9439cc20..d96ce689 100644 --- a/sdc/sdc.cpp +++ b/sdc/sdc.cpp @@ -141,8 +141,8 @@ #include #include -//#include "fileutil.h" #include +#include #include "sdc.h" @@ -154,9 +154,8 @@ #pragma message("Util is defined as a macro!") #endif -namespace util = VCC::Util; - static ::VCC::Device::rtc::cloud9 cloud9_rtc; +namespace util = ::VCC::Util; //====================================================================== // Globals @@ -169,19 +168,19 @@ static int idle_ctr = 0; // Idle Status counter static void* gCallbackContext = nullptr; static PakAssertInteruptHostCallback AssertIntCallback = nullptr; static PakAppendCartridgeMenuHostCallback CartMenuCallback = nullptr; -static char IniFile[MAX_PATH] = {}; // Vcc ini file name +// Settings +static char IniFile[MAX_PATH] = {}; + +// Window handles static HWND gVccWindow = nullptr; static HWND hConfigureDlg = nullptr; static HWND hSDCardBox = nullptr; -static HWND hStartupBank = nullptr; +static HWND hStartBankBox = nullptr; static int ClockEnable = 0; static char SDC_Status[16] = {}; static char PakRom[0x4000] = {}; -static unsigned char CurrentBank = 0xff; -static unsigned char EnableBankWrite = 0; -static unsigned char BankWriteState = 0; static std::string gSDRoot {}; // SD card root directory static std::string gCurDir {}; // Current directory relative to root @@ -194,11 +193,15 @@ static Interface IFace {}; // Flash banks static char FlashFile[8][MAX_PATH]; -static FILE *h_RomFile = nullptr; -static unsigned char StartupBank = 0; static unsigned char BankWriteNum = 0; -static unsigned char BankDirty = 0; static unsigned char BankData = 0; +static unsigned char EnableBankWrite = 0; +static unsigned char BankWriteState = 0; + +static unsigned char gStartupBank = 0; +static unsigned char gLoadedBank = 0xff; // Currently loaded bank +static unsigned char gRomDirty = 0; // RomDirty flag + // Streaming control static int streaming; @@ -221,7 +224,7 @@ static int EDBOXES[8] = {ID_TEXT0,ID_TEXT1,ID_TEXT2,ID_TEXT3, static int UPDBTNS[8] = {ID_UPDATE0,ID_UPDATE1,ID_UPDATE2,ID_UPDATE3, ID_UPDATE4,ID_UPDATE5,ID_UPDATE6,ID_UPDATE7}; -static char ROMPath[MAX_PATH]; +static std::string gRomPath {}; //====================================================================== // Functions @@ -291,7 +294,6 @@ bool SDCReadDrive(unsigned char,unsigned int); bool SDCLoadNextDirPage(); std::string SDCGetFullPath(const std::string&); - //====================================================================== // DLL interface //====================================================================== @@ -310,7 +312,6 @@ extern "C" CartMenuCallback = callbacks->add_menu_item; AssertIntCallback = callbacks->assert_interrupt; strcpy(IniFile, configuration_path); - LoadConfig(); BuildCartridgeMenu(); } @@ -397,7 +398,7 @@ extern "C" idle_ctr++; } else { idle_ctr = 0; - snprintf(SDC_Status,16,"SDC:%d idle",CurrentBank); + snprintf(SDC_Status,16,"SDC:%d idle",gLoadedBank); } strncpy(text_buffer,SDC_Status,buffer_size); } @@ -472,10 +473,10 @@ SDC_Configure(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lParam*/) InitFlashBoxes(); InitCardBox(); SendDlgItemMessage(hDlg,IDC_CLOCK,BM_SETCHECK,ClockEnable,0); - hStartupBank = GetDlgItem(hDlg,ID_STARTUP_BANK); + hStartBankBox = GetDlgItem(hDlg,ID_STARTUP_BANK); char tmp[4]; - snprintf(tmp,4,"%d",(StartupBank & 7)); - SetWindowText(hStartupBank,tmp); + snprintf(tmp,4,"%d",(gStartupBank & 7)); + SetWindowText(hStartBankBox,tmp); break; case WM_COMMAND: switch (LOWORD(wParam)) { @@ -520,13 +521,13 @@ SDC_Configure(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lParam*/) case ID_STARTUP_BANK: if (HIWORD(wParam) == EN_CHANGE) { char tmp[4]; - GetWindowText(hStartupBank,tmp,4); - StartupBank = atoi(tmp);// & 7; - if (StartupBank > 7) { - StartupBank &= 7; + GetWindowText(hStartBankBox,tmp,4); + gStartupBank = atoi(tmp);// & 7; + if (gStartupBank > 7) { + gStartupBank &= 7; char tmp[4]; - snprintf(tmp,4,"%d",StartupBank); - SetWindowText(hStartupBank,tmp); + snprintf(tmp,4,"%d",gStartupBank); + SetWindowText(hStartBankBox,tmp); } } break; @@ -545,34 +546,26 @@ SDC_Configure(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lParam*/) //------------------------------------------------------------ void LoadConfig() { - char tmp[MAX_PATH]; + util::settings settings(IniFile); - // FIXME should be "SDCRomPath" and saved when changed - GetPrivateProfileString - ("DefaultPaths", "MPIPath", "", ROMPath, MAX_PATH, IniFile); + gRomPath = settings.read("DefaultPaths", "RomPath", ""); + gSDRoot = settings.read("SDC", "SDCardPath", ""); - GetPrivateProfileString - ("SDC", "SDCardPath", "", tmp, MAX_PATH, IniFile); + DLOG_C("LoadConfig gRomPath %s\n",gRomPath.c_str()); + DLOG_C("LoadConfig gSDRoot %s\n",gSDRoot.c_str()); - gSDRoot = tmp; util::FixDirSlashes(gSDRoot); - - DLOG_C("LoadConfig ROMPath %s\n",ROMPath); - DLOG_C("LoadConfig gSDRoot %s\n",gSDRoot); - if (!util::IsDirectory(gSDRoot)) { MessageBox (gVccWindow,"Invalid SDCard Path in VCC init","Error",0); } for (int i=0;i<8;i++) { - char txt[32]; - snprintf(txt,MAX_PATH,"FlashFile_%d",i); - GetPrivateProfileString - ("SDC", txt, "", FlashFile[i], MAX_PATH, IniFile); + std::string tmp = "FlashFile_" + std::to_string(i); + settings.read("SDC", tmp, "", FlashFile[i], MAX_PATH);//, IniFile); } - - ClockEnable = GetPrivateProfileInt("SDC","ClockEnable",1,IniFile); - StartupBank = GetPrivateProfileInt("SDC","StarupBank",0,IniFile); + + ClockEnable = settings.read("SDC","ClockEnable",1); + gStartupBank = settings.read("SDC","StartupBank",0); } //------------------------------------------------------------ @@ -580,26 +573,29 @@ void LoadConfig() //------------------------------------------------------------ bool SaveConfig(HWND hDlg) { + util::settings settings(IniFile); + if (!util::IsDirectory(gSDRoot)) { MessageBox(gVccWindow,"Invalid SDCard Path\n","Error",0); return false; } - WritePrivateProfileString("SDC","SDCardPath",gSDRoot.c_str(),IniFile); + + settings.write("SDC","SDCardPath",gSDRoot); + settings.write("DefaultPaths","RomPath",gRomPath); for (int i=0;i<8;i++) { - char txt[32]; - sprintf(txt,"FlashFile_%d",i); - WritePrivateProfileString("SDC",txt,FlashFile[i],IniFile); + std::string tmp = "FlashFile_" + std::to_string(i); + settings.write("SDC", tmp, FlashFile[i]); } if (SendDlgItemMessage(hDlg,IDC_CLOCK,BM_GETCHECK,0,0)) { - WritePrivateProfileString("SDC","ClockEnable","1",IniFile); + settings.write("SDC","ClockEnable","1"); } else { - WritePrivateProfileString("SDC","ClockEnable","0",IniFile); + settings.write("SDC","ClockEnable","0"); } - char tmp[4]; - snprintf(tmp,4,"%d",(StartupBank & 7)); - WritePrivateProfileString("SDC","StarupBank",tmp,IniFile); + + gStartupBank &= 7; + settings.write("SDC","StartupBank",std::to_string(gStartupBank)); return true; } @@ -653,6 +649,7 @@ void InitCardBox() void UpdateFlashItem(int index) { + util::settings settings(IniFile); char filename[MAX_PATH]={}; if ((index < 0) | (index > 7)) return; @@ -666,10 +663,11 @@ void UpdateFlashItem(int index) dlg.setDefExt("rom"); dlg.setFilter("Rom File\0*.rom\0All Files\0*.*\0\0"); dlg.setTitle(title); - dlg.setInitialDir(ROMPath); + dlg.setInitialDir(gRomPath.c_str()); if (dlg.show(0,hConfigureDlg)) { dlg.getupath(filename,MAX_PATH); // cvt to unix style strncpy(FlashFile[index],filename,MAX_PATH); + gRomPath = util::GetDirectoryPart(filename); } } InitFlashBoxes(); @@ -677,7 +675,8 @@ void UpdateFlashItem(int index) // Save path to ini file char txt[32]; sprintf(txt,"FlashFile_%d",index); - WritePrivateProfileString("SDC",txt,FlashFile[index],IniFile); + settings.write("SDC",txt,FlashFile[index]); + } //------------------------------------------------------------ @@ -742,7 +741,7 @@ void InitSDC() // Load SDC settings LoadConfig(); - LoadRom(StartupBank); + LoadRom(gStartupBank); SDCSetCurDir(""); // May be changed by ParseStartup() @@ -819,54 +818,63 @@ void LoadRom(unsigned char bank) DLOG_C("LoadRom load flash bank %d\n",bank); - // Skip if bank is already active - if (bank == CurrentBank) return; +// // Skip if bank is already loaded +// if (bank == gLoadedBank) return; - // Make sure flash file is closed - if (h_RomFile) { - fclose(h_RomFile); - h_RomFile = nullptr; - } + // Sanity check before trying to save to empty file + if (*FlashFile[gLoadedBank] == '\0') gRomDirty = 0; // If bank contents have been changed write the flash file - if (BankDirty) { - RomFile = FlashFile[CurrentBank]; - DLOG_C("LoadRom switching out dirty bank %d %s\n",CurrentBank,RomFile); - h_RomFile = fopen(RomFile,"wb"); - if (!h_RomFile) { - MessageBox (gVccWindow,"Can not write Rom file","SDC Rom Save Failed",0); - } else { - ctr = 0; - p_rom = PakRom; - while (ctr++ < 0x4000) fputc(*p_rom++, h_RomFile); - fclose(h_RomFile); - h_RomFile = nullptr; + if (gRomDirty) { + RomFile = FlashFile[gLoadedBank]; + int rc = MessageBox(gVccWindow, + "Save Rom Contents", + "Write Rom File?", + MB_YESNOCANCEL); + switch (rc) { + case IDYES: + { + FILE *hf = fopen(RomFile,"wb"); + if (!hf) { + MessageBox (gVccWindow, + "Can not write Rom file", + "Write Rom Failed",0); + } else { + ctr = 0; + p_rom = PakRom; + while (ctr++ < 0x4000) fputc(*p_rom++, hf); + fclose(hf); + gRomDirty = 0; + } + break; + } + case IDNO: + gRomDirty = 0; + break; } - BankDirty = 0; } - RomFile = FlashFile[bank]; - CurrentBank = bank; + // Abort if user canceled or save failed + if (gRomDirty) return; + + RomFile = FlashFile[bank]; // RomFile is a pointer // If bank is empty and is the StartupBank load SDC-DOS - if (*FlashFile[CurrentBank] == '\0') { - DLOG_C("LoadRom bank %d is empty\n",CurrentBank); - if (CurrentBank == StartupBank) { + if (*FlashFile[bank] == '\0') { + DLOG_C("LoadRom bank %d is empty\n",bank); + if (bank == gStartupBank) { DLOG_C("LoadRom loading default SDC-DOS\n"); strncpy(RomFile,"SDC-DOS.ROM",MAX_PATH); + } else { + return; } } - // Open romfile for write if not startup bank - if (CurrentBank != StartupBank) - h_RomFile = fopen(RomFile,"wb"); - - if (CurrentBank == StartupBank || h_RomFile == nullptr) - h_RomFile = fopen(RomFile,"rb"); - - if (h_RomFile == nullptr) { + // Load the rom + FILE *hf = fopen(RomFile,"rb"); + if (hf == nullptr) { std::string msg="Check Rom Path and SDC Config\n"+std::string(RomFile); - MessageBox (gVccWindow,msg.c_str(),"SDC Startup Rom Load Failed",0); + MessageBox (gVccWindow,msg.c_str(),"SDC Rom Load Failed",0); return; } @@ -875,11 +883,12 @@ void LoadRom(unsigned char bank) ctr = 0; p_rom = PakRom; while (ctr++ < 0x4000) { - if ((ch = fgetc(h_RomFile)) < 0) break; + if ((ch = fgetc(hf)) < 0) break; *p_rom++ = (char) ch; } - fclose(h_RomFile); + gLoadedBank = bank; + fclose(hf); return; } @@ -1042,7 +1051,7 @@ unsigned char SDCRead(unsigned char port) break; // Flash control read is used by SDCDOS to detect the SDC case 0x43: - rpy = CurrentBank | (BankData & 0xF8); + rpy = gLoadedBank | (BankData & 0xF8); break; // Floppy read status case 0x48: @@ -1141,6 +1150,7 @@ void SDCFloppyCommand(unsigned char data) break; case 13: //FORCEINTERUPT //Nitros9 driver does this + //SDC-ROM does this after bank switch DLOG_C("SDCFloppyCommand force interrupt?\n"); break; //case 14: //READTRACK @@ -1302,7 +1312,7 @@ void SDCFloppyReadDisk() auto lsn = SDCFloppyLSN(FlopDrive,FlopTrack,FlopSector); //DLOG_C("FloppyReadDisk %d %d\n",FlopDrive,lsn); - snprintf(SDC_Status,16,"SDC:%d Rd %d,%d",CurrentBank,FlopDrive,lsn); + snprintf(SDC_Status,16,"SDC:%d Rd %d,%d",gLoadedBank,FlopDrive,lsn); if (SDCSeekSector(FlopDrive,lsn)) { if (ReadFile(gCocoDisk[FlopDrive].hFile,FlopRdBuf,256,&FlopRdCnt,nullptr)) { DLOG_C("FloppyReadDisk %d %d\n",FlopDrive,lsn); @@ -1492,7 +1502,7 @@ void SDCFlashControl(unsigned char data) EnableBankWrite = data & 0x80; if (EnableBankWrite) { BankWriteNum = bank; - } else if (CurrentBank != bank) { + } else if (gLoadedBank != bank) { LoadRom(bank); } } @@ -1542,9 +1552,9 @@ unsigned char SDCWriteFlashBank(unsigned short adr) break; // State three writes data case 3: - if (BankWriteNum != CurrentBank) LoadRom(BankWriteNum); + if (BankWriteNum != gLoadedBank) LoadRom(BankWriteNum); PakRom[adr] = BankData; - BankDirty = 1; + gRomDirty = 1; break; // State four continues kill sequence case 4: @@ -1558,9 +1568,9 @@ unsigned char SDCWriteFlashBank(unsigned short adr) // State six kills (fills with 0xFF) bank sector case 6: if (BankData == 0x30) { - if (BankWriteNum != CurrentBank) LoadRom(BankWriteNum); + if (BankWriteNum != gLoadedBank) LoadRom(BankWriteNum); memset(PakRom + (adr & 0x3000), 0xFF, 0x1000); - BankDirty = 1; + gRomDirty = 1; } break; } @@ -1632,7 +1642,7 @@ void SDCWriteSector() DWORD cnt = 0; int drive = IFace.cmdcode & 1; unsigned int lsn = (IFace.param1 << 16) + (IFace.param2 << 8) + IFace.param3; - snprintf(SDC_Status,16,"SDC:%d Wr %d,%d",CurrentBank,drive,lsn); + snprintf(SDC_Status,16,"SDC:%d Wr %d,%d",gLoadedBank,drive,lsn); if (gCocoDisk[drive].hFile == nullptr) { IFace.status = STA_FAIL; @@ -1777,7 +1787,7 @@ bool SDCReadDrive(unsigned char cmdcode, unsigned int lsn) return false; } - snprintf(SDC_Status,16,"SDC:%d Rd %d,%d",CurrentBank,drive,lsn); + snprintf(SDC_Status,16,"SDC:%d Rd %d,%d",gLoadedBank,drive,lsn); SDCLoadReply(buf,cnt); return true; } @@ -1926,7 +1936,7 @@ bool SDCMountNext (int drive) if (gFileList.cursor >= gFileList.files.size()) { gFileList.cursor = 0; - DLOG_C("SDCMountNext reset cursor\n"); + DLOG_C("SDCMountNext reset filelist cursor\n"); } DLOG_C("SDCMountNext %d %d\n", From c80673e827cf806d9da812e59ee2b036a9c91b78 Mon Sep 17 00:00:00 2001 From: ejaquay Date: Sun, 18 Jan 2026 11:37:42 -0500 Subject: [PATCH 03/10] Clean up Vcc and CommandLine headers --- CommandLine.cpp | 38 ++++++++++++++------------- CommandLine.h | 50 ++++++++++++++++++++---------------- Vcc.cpp | 2 +- Vcc.h | 65 +++++++++++++++++++++-------------------------- tcc1014graphics.h | 1 + 5 files changed, 79 insertions(+), 77 deletions(-) diff --git a/CommandLine.cpp b/CommandLine.cpp index e99ded68..db6667d6 100644 --- a/CommandLine.cpp +++ b/CommandLine.cpp @@ -1,23 +1,25 @@ -/* -Copyright E J Jaquay 2020 -This file is part of VCC (Virtual Color Computer). - VCC (Virtual Color Computer) 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 3 of the License, - or (at your option) any later version. - - VCC (Virtual Color Computer) 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 VCC (Virtual Color Computer). If not, see . -*/ - -//------------------------------------------------------------------- +//#define USE_LOGGING +//====================================================================== +// This file is part of VCC (Virtual Color Computer). +// Vcc is Copyright 2015 by Joseph Forgione +// +// VCC (Virtual Color Computer) is free software, you can redistribute +// and/or modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// VCC (Virtual Color Computer) 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 VCC (Virtual Color Computer). If not, see +// . +//====================================================================== // // Command line arguments and options to VCC +// E J Jaquay 2020 // // Since VCC is a windows application traditional Unix command arguments (argc,argV) // are not availiable. However the third argument to WinMain() does provide the diff --git a/CommandLine.h b/CommandLine.h index f9a62bb6..9f796236 100644 --- a/CommandLine.h +++ b/CommandLine.h @@ -1,27 +1,34 @@ -#ifndef __COMMANDLINE_H__ -#define __COMMANDLINE_H__ -/* +//#define USE_LOGGING +//====================================================================== +// This file is part of VCC (Virtual Color Computer). +// Vcc is Copyright 2015 by Joseph Forgione +// +// VCC (Virtual Color Computer) is free software, you can redistribute +// and/or modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// VCC (Virtual Color Computer) 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 VCC (Virtual Color Computer). If not, see +// . +//====================================================================== + +//---------------------------------------------------------------------- +// Command line arguments and options to VCC +// E J Jaquay 2020 +//---------------------------------------------------------------------- + +#pragma once -Copyright 2015 by E J Jaquay -This file is part of VCC (Virtual Color Computer). - - VCC (Virtual Color Computer) 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 3 of the License, or - (at your option) any later version. - - VCC (Virtual Color Computer) 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 VCC (Virtual Color Computer). If not, see . -*/ - -// Declare global variables defined by GetCmdLineArgs constexpr auto CL_MAX_PATH = 256u; constexpr auto CL_MAX_PASTE = 256u; + +// Variables defined by GetCmdLineArgs struct CmdLineArguments { char QLoadFile[CL_MAX_PATH]; char IniFile[CL_MAX_PATH]; @@ -42,4 +49,3 @@ int GetCmdLineArgs(const char * lpCmdLine); #define CL_ERR_UNKOPT 1 // Unknown option found #define CL_ERR_XTRARG 2 // Too many arguments -#endif diff --git a/Vcc.cpp b/Vcc.cpp index 0adea136..07fb7678 100644 --- a/Vcc.cpp +++ b/Vcc.cpp @@ -81,7 +81,7 @@ #include "IDisplayDebug.h" #endif -using namespace VCC; +using namespace ::VCC; // namespace is everything VCC static HANDLE hout=nullptr; diff --git a/Vcc.h b/Vcc.h index 50c775d5..95c61a89 100644 --- a/Vcc.h +++ b/Vcc.h @@ -1,40 +1,33 @@ -#ifndef __VCC_H__ -#define __VCC_H__ +//====================================================================== +// This file is part of VCC (Virtual Color Computer). +// Vcc is Copyright 2015 by Joseph Forgione +// +// VCC (Virtual Color Computer) is free software, you can redistribute +// and/or modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// VCC (Virtual Color Computer) 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 VCC (Virtual Color Computer). If not, see +// . +//====================================================================== -/* -Copyright 2015 by Joseph Forgione -This file is part of VCC (Virtual Color Computer). +// Vcc.h callable functions exposed to the rest of vcc core - VCC (Virtual Color Computer) 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 3 of the License, or - (at your option) any later version. +#pragma once - VCC (Virtual Color Computer) 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. +void SetCPUMultiplyerFlag (unsigned char); //tcc1014registers.cpp +void SetTurboMode(unsigned char); //tcc1014registers.cpp +unsigned char SetCPUMultiplyer(unsigned char); //tcc1014registers.cpp, config.cpp +unsigned char SetRamSize(unsigned char); //config.cpp +unsigned char SetSpeedThrottle(unsigned char); //config.cpp, keyboard.cpp +unsigned char SetFrameSkip(unsigned char); //config.cpp +unsigned char SetCpuType( unsigned char); //config.cpp +unsigned char SetAutoStart(unsigned char); //config.cpp +void DoReboot(); //config.cpp - You should have received a copy of the GNU General Public License - along with VCC (Virtual Color Computer). If not, see . -*/ - -// define this to make the main config dialog modal -// vs. modeless where you can interact with the main window -// while the config dialog is open -//#define CONFIG_DIALOG_MODAL - -void SetCPUMultiplyerFlag (unsigned char); -void SetTurboMode(unsigned char); -unsigned char SetCPUMultiplyer(unsigned char ); -unsigned char SetRamSize(unsigned char); -unsigned char SetSpeedThrottle(unsigned char); -unsigned char SetFrameSkip(unsigned char); -unsigned char SetCpuType( unsigned char); -unsigned char SetAutoStart(unsigned char); -void SetPaletteType(); -void DoReboot(); -void DoHardReset(SystemState *); -void LoadPack (int); - -#endif diff --git a/tcc1014graphics.h b/tcc1014graphics.h index 7c942fc7..da841ddf 100644 --- a/tcc1014graphics.h +++ b/tcc1014graphics.h @@ -48,6 +48,7 @@ void SetGimeHorzOffset(unsigned char); void SetGimeBoarderColor(unsigned char); void SetVidMask(unsigned int); void InvalidateBoarder(); +void SetPaletteType(); void GimeInit(); void GimeReset(); From 29560e47cbf1f6e946450e14271fc3cc3f11f490 Mon Sep 17 00:00:00 2001 From: ejaquay Date: Sun, 18 Jan 2026 14:21:35 -0500 Subject: [PATCH 04/10] Vcc.cpp no longer writes settings. Settings should only be written by config dialogs in config.cpp or by cartridge DLL config dialogs. --- Vcc.cpp | 20 +++++------ config.cpp | 102 +++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 81 insertions(+), 41 deletions(-) diff --git a/Vcc.cpp b/Vcc.cpp index 07fb7678..d0111d45 100644 --- a/Vcc.cpp +++ b/Vcc.cpp @@ -210,8 +210,10 @@ int APIENTRY WinMain(_In_ HINSTANCE hInstance, while (BinaryRunning) { - if (FlagEmuStop==TH_WAITING) //Need to stop the EMU thread for screen mode change - { //As it holds the Secondary screen buffer open while running + //Need to stop the EMU thread for screen mode change + //As it holds the Secondary screen buffer open while running + if (FlagEmuStop==TH_WAITING) + { FullScreenToggle(); FlagEmuStop=TH_RUNNING; } @@ -255,7 +257,6 @@ int APIENTRY WinMain(_In_ HINSTANCE hInstance, CloseScreen(); timeEndPeriod(1); UnloadDll(); - WriteIniFile(); //Save Any changes to ini File return Msg.wParam; } @@ -927,10 +928,9 @@ void LoadIniFile() dlg.setpath(curini); if ( dlg.show() ) { - WriteIniFile(); // Flush current profile SetIniFilePath(dlg.path()); // Set new ini file path - ReadIniFile(); // Load it - UpdateConfig(); + ReadIniFile(); // FIXME ReadIniFile should apply changes + UpdateConfig(); // Applies only video and Cpu changes EmuState.ResetPending = 2; } return; @@ -953,7 +953,6 @@ void SaveConfig() { if ( dlg.show(1) ) { SetIniFilePath(dlg.path()); // Set new ini file path - WriteIniFile(); // Flush current profile // If ini file has changed if (_stricmp(curini,dlg.path()) != 0) { // Copy current ini to new ini @@ -1010,8 +1009,9 @@ unsigned __stdcall EmuLoop(HANDLE hEvent) break; case 2: //Hard Reset - UpdateConfig(); - DoCls(&EmuState); + ReadIniFile(); // FIXME ReadIniFile should apply changes + UpdateConfig(); // Applies only video and Cpu changes + DoCls(&EmuState); // Clear the screen DoHardReset(&EmuState); break; @@ -1020,7 +1020,7 @@ unsigned __stdcall EmuLoop(HANDLE hEvent) break; case 4: - UpdateConfig(); + UpdateConfig(); // Apply video and cpy changes DoCls(&EmuState); break; diff --git a/config.cpp b/config.cpp index 5943633d..fe51e5a4 100644 --- a/config.cpp +++ b/config.cpp @@ -193,14 +193,21 @@ void LoadConfig(SystemState *LCState) LCState->ScanLines=0; NumberOfSoundCards=GetSoundCardList(SoundCards); + ReadIniFile(); + // The rest of this belongs in ReadIniFile(); maybe it needs a name change + + // FIXME ReadIniFile() should cause hard reset. CurrentConfig.RebootNow=0; + + // FIXME ReadIniFile should apply changes then UpdateConfig() here not needed UpdateConfig(); RefreshJoystickStatus(); if (EmuState.WindowHandle != nullptr) InitSound(); -// Try to open the config file. Create it if necessary. Abort if failure. + // FIXME: This belongs in ReadIniFile(), already called above + // Try to open the config file. Create it if necessary. Abort if failure. hr = CreateFile(IniFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); lasterror = GetLastError(); @@ -220,47 +227,53 @@ void InitSound() } /***********************************************************/ -/* Save Configuration Settings to ini file */ +/* Save All Configuration Settings to ini file */ /***********************************************************/ + +// FIXME WriteIniFile() is a sledgehammer. +// Each config dialog should save the stuff it sets unsigned char WriteIniFile() { - Rect winRect = GetCurWindowSize(); + WritePrivateProfileString("Version","Release",AppName,IniFilePath); + GetCurrentModule(CurrentConfig.ModulePath); ValidatePath(CurrentConfig.ModulePath); + WritePrivateProfileString("Module", "OnBoot", CurrentConfig.ModulePath, IniFilePath); - WritePrivateProfileString("Version","Release",AppName,IniFilePath); + // CPU WritePrivateProfileInt("CPU","DoubleSpeedClock",CurrentConfig.CPUMultiplyer,IniFilePath); WritePrivateProfileInt("CPU","FrameSkip",CurrentConfig.FrameSkip,IniFilePath); WritePrivateProfileInt("CPU","Throttle",CurrentConfig.SpeedThrottle,IniFilePath); WritePrivateProfileInt("CPU","CpuType",CurrentConfig.CpuType,IniFilePath); - WritePrivateProfileInt("CPU", "MaxOverClock", CurrentConfig.MaxOverclock, IniFilePath); + WritePrivateProfileInt("CPU","MaxOverClock", CurrentConfig.MaxOverclock, IniFilePath); WritePrivateProfileInt("CPU","BreakEnabled",CurrentConfig.BreakOpcEnabled,IniFilePath); + WritePrivateProfileInt("Memory","RamSize",CurrentConfig.RamSize,IniFilePath); + WritePrivateProfileInt("Misc","AutoStart",CurrentConfig.AutoStart,IniFilePath); + WritePrivateProfileInt("Misc","CartAutoStart",CurrentConfig.CartAutoStart,IniFilePath); + WritePrivateProfileInt("Misc","UseExtCocoRom", CurrentConfig.UseExtCocoRom, IniFilePath); + WritePrivateProfileInt("Misc","Overclock", CurrentConfig.EnableOverclock, IniFilePath); + WritePrivateProfileString("Misc","ExternalBasicImage", CurrentConfig.ExtRomFile,IniFilePath); + // Audio WritePrivateProfileString("Audio","SndCard",CurrentConfig.SoundCardName,IniFilePath); WritePrivateProfileInt("Audio","Rate",CurrentConfig.AudioRate,IniFilePath); + // Video + Rect winRect = GetCurWindowSize(); WritePrivateProfileInt("Video","MonitorType",CurrentConfig.MonitorType,IniFilePath); WritePrivateProfileInt("Video","PaletteType",CurrentConfig.PaletteType, IniFilePath); WritePrivateProfileInt("Video","ScanLines",CurrentConfig.ScanLines,IniFilePath); WritePrivateProfileInt("Video","ForceAspect",CurrentConfig.Aspect,IniFilePath); WritePrivateProfileInt("Video","RememberSize", CurrentConfig.RememberSize, IniFilePath); - WritePrivateProfileInt("Video", "WindowSizeX", winRect.w, IniFilePath); - WritePrivateProfileInt("Video", "WindowSizeY", winRect.h, IniFilePath); - WritePrivateProfileInt("Video", "WindowPosX", winRect.x, IniFilePath); - WritePrivateProfileInt("Video", "WindowPosY", winRect.y, IniFilePath); + WritePrivateProfileInt("Video","WindowSizeX", winRect.w, IniFilePath); + WritePrivateProfileInt("Video","WindowSizeY", winRect.h, IniFilePath); + WritePrivateProfileInt("Video","WindowPosX", winRect.x, IniFilePath); + WritePrivateProfileInt("Video","WindowPosY", winRect.y, IniFilePath); - WritePrivateProfileInt("Memory","RamSize",CurrentConfig.RamSize,IniFilePath); - - WritePrivateProfileInt("Misc","AutoStart",CurrentConfig.AutoStart,IniFilePath); - WritePrivateProfileInt("Misc","CartAutoStart",CurrentConfig.CartAutoStart,IniFilePath); - WritePrivateProfileInt("Misc","ShowMousePointer",CurrentConfig.ShowMousePointer,IniFilePath); + // Keyboard WritePrivateProfileInt("Misc","KeyMapIndex",CurrentConfig.KeyMap,IniFilePath); - WritePrivateProfileInt("Misc", "UseExtCocoRom", CurrentConfig.UseExtCocoRom, IniFilePath); - WritePrivateProfileInt("Misc", "Overclock", CurrentConfig.EnableOverclock, IniFilePath); - WritePrivateProfileString("Misc", "ExternalBasicImage", CurrentConfig.ExtRomFile,IniFilePath); - - WritePrivateProfileString("Module", "OnBoot", CurrentConfig.ModulePath, IniFilePath); + // Joystick WritePrivateProfileInt("LeftJoyStick","UseMouse",LeftJS.UseMouse,IniFilePath); WritePrivateProfileInt("LeftJoyStick","Left",LeftJS.Left,IniFilePath); WritePrivateProfileInt("LeftJoyStick","Right",LeftJS.Right,IniFilePath); @@ -279,8 +292,8 @@ unsigned char WriteIniFile() WritePrivateProfileInt("RightJoyStick","Fire2",RightJS.Fire2,IniFilePath); WritePrivateProfileInt("RightJoyStick","DiDevice",RightJS.DiDevice,IniFilePath); WritePrivateProfileInt("RightJoyStick", "HiResDevice", RightJS.HiRes, IniFilePath); - - // Force flush inifile + WritePrivateProfileInt("Misc","ShowMousePointer",CurrentConfig.ShowMousePointer,IniFilePath); + // Force flush inifile Is this required? WritePrivateProfileString(nullptr,nullptr,nullptr,IniFilePath); return 0; } @@ -288,6 +301,8 @@ unsigned char WriteIniFile() /***********************************************************/ /* Load Configuration Settings from ini file */ /***********************************************************/ + +// FIXME ReadIniFile should apply changes, should hard reset if RAM or CPU changes unsigned char ReadIniFile() { unsigned char Index=0; @@ -437,34 +452,35 @@ void SetKeyMapFilePath(const char *Path) WritePrivateProfileString("Misc","CustomKeyMapFile",KeyMapFilePath,IniFilePath); } -/*****************************************************/ -/* Apply Current VCC settings. Also Called by Vcc.c */ -/*****************************************************/ +/*******************************************************/ +/* Apply video and CPU settings. Also Called by Vcc.c */ +/*******************************************************/ void UpdateConfig () { + // Video SetPaletteType(); + SetMonitorType(CurrentConfig.MonitorType); SetAspect(CurrentConfig.Aspect); SetScanLines(CurrentConfig.ScanLines); SetFrameSkip(CurrentConfig.FrameSkip); + EmuState.MousePointer = CurrentConfig.ShowMousePointer; + // Cpu SetAutoStart(CurrentConfig.AutoStart); SetSpeedThrottle(CurrentConfig.SpeedThrottle); SetCPUMultiplyer(CurrentConfig.CPUMultiplyer); SetRamSize(CurrentConfig.RamSize); SetCpuType(CurrentConfig.CpuType); SetOverclock(CurrentConfig.EnableOverclock); - if (CurrentConfig.BreakOpcEnabled) { EmuState.Debugger.Enable_Break(true); } else { EmuState.Debugger.Enable_Break(false); } - - SetMonitorType(CurrentConfig.MonitorType); SetCartAutoStart(CurrentConfig.CartAutoStart); + if (CurrentConfig.RebootNow) DoReboot(); CurrentConfig.RebootNow=0; - EmuState.MousePointer = CurrentConfig.ShowMousePointer; } /********************************************/ @@ -536,7 +552,10 @@ LRESULT CALLBACK CpuConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lPar GetDlgItemText(hDlg,IDC_ROMPATH,tmpcfg.ExtRomFile,MAX_PATH); } - // ResetPending causes Vcc.c to call UpdateConfig(). + // It is important that Cpu settings are not changed while the + // emulation is actually running. ResetPending causes Vcc.c to + // call UpdateConfig() which causes the settings to take effect. + // TODO: Use lock to avoid race condition instead? if ( (CurrentConfig.RamSize != tmpcfg.RamSize) | (CurrentConfig.CpuType != tmpcfg.CpuType) | (CurrentConfig.UseExtCocoRom != tmpcfg.UseExtCocoRom) | @@ -559,6 +578,10 @@ LRESULT CALLBACK CpuConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lPar CurrentConfig.EnableOverclock = tmpcfg.EnableOverclock; strncpy(CurrentConfig.ExtRomFile,tmpcfg.ExtRomFile,MAX_PATH); SetOverclock(CurrentConfig.EnableOverclock); + + // FIXME should only save CPU stuff here + WriteIniFile(); + // Exit dialog if IDOK if (LOWORD(wParam)==IDOK) { hCpuDlg = nullptr; @@ -651,6 +674,7 @@ void IncreaseOverclockSpeed() 0, (LPARAM)(LPCSTR)OutBuffer); } EmuState.ResetPending = 4; // Without this, changing the config does nothing. + //SetCPUMultiplyer(CurrentConfig.CPUMultiplyer) may work here instead } /* Decrease the overclock speed */ @@ -668,7 +692,8 @@ void DecreaseOverclockSpeed() SendDlgItemMessage(hCpuDlg, IDC_CLOCKDISPLAY, WM_SETTEXT, 0, (LPARAM)(LPCSTR)OutBuffer); } - EmuState.ResetPending = 4; + EmuState.ResetPending = 4; // So emulation knows the speed changed + //SetCPUMultiplyer(CurrentConfig.CPUMultiplyer) may work here instead } /********************************************/ @@ -721,6 +746,7 @@ LRESULT CALLBACK TapeConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lPa case IDOK: case IDAPPLY: UpdateConfig(); + // FIXME Save changes (TapeFastLoad) to IniFile if (LOWORD(wParam)==IDOK) { hTapeDlg = nullptr; DestroyWindow(hDlg); @@ -867,7 +893,12 @@ LRESULT CALLBACK AudioConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lP CurrentConfig.AudioRate = tmpcfg.AudioRate; CurrentConfig.SndOutDev = tmpcfg.SndOutDev; strcpy(CurrentConfig.SoundCardName, SoundCards[CurrentConfig.SndOutDev].CardName); - UpdateConfig(); +// FIXME Should only save Audio stuff here +//WritePrivateProfileString("Audio","SndCard",CurrentConfig.SoundCardName,IniFilePath); +//WritePrivateProfileInt("Audio","Rate",CurrentConfig.AudioRate,IniFilePath); + // FIXME Save mute button state (AudioRate) + WriteIniFile(); + if (LOWORD(wParam)==IDOK) { hAudioDlg = nullptr; DestroyWindow(hDlg); @@ -1000,6 +1031,9 @@ LRESULT CALLBACK DisplayConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /* CurrentConfig.MonitorType = tmpcfg.MonitorType; CurrentConfig.PaletteType = tmpcfg.PaletteType; UpdateConfig(); + + // FIXME should only save display stuff here + WriteIniFile(); if (LOWORD(wParam)==IDOK) { hDisplayDlg = nullptr; DestroyWindow(hDlg); @@ -1081,6 +1115,8 @@ LRESULT CALLBACK InputConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lP case IDAPPLY: vccKeyboardBuildRuntimeTable((keyboardlayout_e)CurrentConfig.KeyMap); UpdateConfig(); + // FIXME Should only save keyboard stuff here + WriteIniFile(); if (LOWORD(wParam)==IDOK) { hInputDlg = nullptr; DestroyWindow(hDlg); @@ -1317,6 +1353,8 @@ LRESULT CALLBACK JoyStickConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM / SetStickNumbers(LeftJS.DiDevice,RightJS.DiDevice); CurrentConfig.ShowMousePointer = tmpcfg.ShowMousePointer; UpdateConfig(); + // FIXME Should only save joystick stuff here + WriteIniFile(); if (LOWORD(wParam)==IDOK) { hJoyStickDlg = nullptr; DestroyWindow(hDlg); @@ -1521,6 +1559,8 @@ LRESULT CALLBACK BitBanger(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lPar case IDOK: case IDAPPLY: UpdateConfig(); + // FIXME Should save bitbanger here + WriteIniFile(); if (LOWORD(wParam)==IDOK) { hBitBangerDlg = nullptr; DestroyWindow(hDlg); From f409519c5001bdbbaa61c85f97c883bfc2f676bd Mon Sep 17 00:00:00 2001 From: ejaquay Date: Sun, 18 Jan 2026 17:46:45 -0500 Subject: [PATCH 05/10] Config.cpp use new settings object --- Vcc.cpp | 17 +- config.cpp | 265 ++++++++++++++------------ config.h | 1 + libcommon/include/vcc/util/fileutil.h | 8 + libcommon/include/vcc/util/settings.h | 19 ++ 5 files changed, 182 insertions(+), 128 deletions(-) diff --git a/Vcc.cpp b/Vcc.cpp index d0111d45..6fc500b4 100644 --- a/Vcc.cpp +++ b/Vcc.cpp @@ -68,6 +68,7 @@ #include "CommandLine.h" #include #include +#include #include "memdump.h" #include "MemoryMap.h" @@ -915,16 +916,17 @@ unsigned char SetAutoStart(unsigned char Tmp) // LoadIniFile allows user to browse for an ini file and reloads the config from it. void LoadIniFile() { + FlushSettings(); + + char curini[MAX_PATH]=""; + GetIniFilePath(curini); + FileDialog dlg; dlg.setFilter("INI\0*.ini\0\0"); dlg.setDefExt("ini"); dlg.setInitialDir(AppDirectory() ); dlg.setTitle(TEXT("Load Vcc Config File") ); dlg.setFlags(OFN_FILEMUSTEXIST); - - // Send current ini file path to dialog - char curini[MAX_PATH]=""; - GetIniFilePath(curini); dlg.setpath(curini); if ( dlg.show() ) { @@ -939,6 +941,11 @@ void LoadIniFile() // SaveConfig copies the current ini file to a choosen ini file. void SaveConfig() { + FlushSettings(); + + char curini[MAX_PATH]=""; + GetIniFilePath(curini); + FileDialog dlg; dlg.setFilter("INI\0*.ini\0\0"); dlg.setDefExt("ini"); @@ -947,8 +954,6 @@ void SaveConfig() { dlg.setFlags(OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT); // Send current ini file path to dialog - char curini[MAX_PATH]=""; - GetIniFilePath(curini); dlg.setpath(curini); if ( dlg.show(1) ) { diff --git a/config.cpp b/config.cpp index fe51e5a4..421e22e0 100644 --- a/config.cpp +++ b/config.cpp @@ -1,20 +1,22 @@ -/* -Copyright 2015 by Joseph Forgione -This file is part of VCC (Virtual Color Computer). - - VCC (Virtual Color Computer) 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 3 of the License, or - (at your option) any later version. - - VCC (Virtual Color Computer) 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 VCC (Virtual Color Computer). If not, see . -*/ +//#define USE_LOGGING +//====================================================================== +// This file is part of VCC (Virtual Color Computer). +// Vcc is Copyright 2015 by Joseph Forgione +// +// VCC (Virtual Color Computer) is free software, you can redistribute +// and/or modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// VCC (Virtual Color Computer) 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 VCC (Virtual Color Computer). If not, see +// . +//====================================================================== // FIXME: This should be defined on the command line #define DIRECTINPUT_VERSION 0x0800 @@ -51,6 +53,8 @@ This file is part of VCC (Virtual Color Computer). #include "Cassette.h" #include "CommandLine.h" #include +#include +#include using namespace std; using namespace VCC; @@ -163,6 +167,15 @@ unsigned char _TranslateScan2Disp[SCAN_TRANS_COUNT] = { /***********************************************************/ /* Establish ini file and Load Settings */ /***********************************************************/ + +// Flush the settings store +void FlushSettings() +{ + if (Util::is_null_or_empty(IniFilePath)) return; + Util::settings settings(IniFilePath); + settings.flush(); +} + void LoadConfig(SystemState *LCState) { HANDLE hr=nullptr; @@ -234,67 +247,71 @@ void InitSound() // Each config dialog should save the stuff it sets unsigned char WriteIniFile() { - WritePrivateProfileString("Version","Release",AppName,IniFilePath); + // TODO: Deal with missing ini path + if (Util::is_null_or_empty(IniFilePath)) return 1; + Util::settings setting(IniFilePath); + + setting.write("Version","Release",AppName); GetCurrentModule(CurrentConfig.ModulePath); ValidatePath(CurrentConfig.ModulePath); - WritePrivateProfileString("Module", "OnBoot", CurrentConfig.ModulePath, IniFilePath); + setting.write("Module", "OnBoot", CurrentConfig.ModulePath); // CPU - WritePrivateProfileInt("CPU","DoubleSpeedClock",CurrentConfig.CPUMultiplyer,IniFilePath); - WritePrivateProfileInt("CPU","FrameSkip",CurrentConfig.FrameSkip,IniFilePath); - WritePrivateProfileInt("CPU","Throttle",CurrentConfig.SpeedThrottle,IniFilePath); - WritePrivateProfileInt("CPU","CpuType",CurrentConfig.CpuType,IniFilePath); - WritePrivateProfileInt("CPU","MaxOverClock", CurrentConfig.MaxOverclock, IniFilePath); - WritePrivateProfileInt("CPU","BreakEnabled",CurrentConfig.BreakOpcEnabled,IniFilePath); - WritePrivateProfileInt("Memory","RamSize",CurrentConfig.RamSize,IniFilePath); - WritePrivateProfileInt("Misc","AutoStart",CurrentConfig.AutoStart,IniFilePath); - WritePrivateProfileInt("Misc","CartAutoStart",CurrentConfig.CartAutoStart,IniFilePath); - WritePrivateProfileInt("Misc","UseExtCocoRom", CurrentConfig.UseExtCocoRom, IniFilePath); - WritePrivateProfileInt("Misc","Overclock", CurrentConfig.EnableOverclock, IniFilePath); - WritePrivateProfileString("Misc","ExternalBasicImage", CurrentConfig.ExtRomFile,IniFilePath); + setting.write("CPU","DoubleSpeedClock",CurrentConfig.CPUMultiplyer); + setting.write("CPU","FrameSkip",CurrentConfig.FrameSkip); + setting.write("CPU","Throttle",CurrentConfig.SpeedThrottle); + setting.write("CPU","CpuType",CurrentConfig.CpuType); + setting.write("CPU","MaxOverClock", CurrentConfig.MaxOverclock); + setting.write("CPU","BreakEnabled",CurrentConfig.BreakOpcEnabled); + setting.write("Memory","RamSize",CurrentConfig.RamSize); + setting.write("Misc","AutoStart",CurrentConfig.AutoStart); + setting.write("Misc","CartAutoStart",CurrentConfig.CartAutoStart); + setting.write("Misc","UseExtCocoRom", CurrentConfig.UseExtCocoRom); + setting.write("Misc","Overclock", CurrentConfig.EnableOverclock); + setting.write("Misc","ExternalBasicImage", CurrentConfig.ExtRomFile); // Audio - WritePrivateProfileString("Audio","SndCard",CurrentConfig.SoundCardName,IniFilePath); - WritePrivateProfileInt("Audio","Rate",CurrentConfig.AudioRate,IniFilePath); + setting.write("Audio","SndCard",CurrentConfig.SoundCardName); + setting.write("Audio","Rate",CurrentConfig.AudioRate); // Video Rect winRect = GetCurWindowSize(); - WritePrivateProfileInt("Video","MonitorType",CurrentConfig.MonitorType,IniFilePath); - WritePrivateProfileInt("Video","PaletteType",CurrentConfig.PaletteType, IniFilePath); - WritePrivateProfileInt("Video","ScanLines",CurrentConfig.ScanLines,IniFilePath); - WritePrivateProfileInt("Video","ForceAspect",CurrentConfig.Aspect,IniFilePath); - WritePrivateProfileInt("Video","RememberSize", CurrentConfig.RememberSize, IniFilePath); - WritePrivateProfileInt("Video","WindowSizeX", winRect.w, IniFilePath); - WritePrivateProfileInt("Video","WindowSizeY", winRect.h, IniFilePath); - WritePrivateProfileInt("Video","WindowPosX", winRect.x, IniFilePath); - WritePrivateProfileInt("Video","WindowPosY", winRect.y, IniFilePath); + setting.write("Video","MonitorType",CurrentConfig.MonitorType); + setting.write("Video","PaletteType",CurrentConfig.PaletteType); + setting.write("Video","ScanLines",CurrentConfig.ScanLines); + setting.write("Video","ForceAspect",CurrentConfig.Aspect); + setting.write("Video","RememberSize", CurrentConfig.RememberSize); + setting.write("Video","WindowSizeX", winRect.w); + setting.write("Video","WindowSizeY", winRect.h); + setting.write("Video","WindowPosX", winRect.x); + setting.write("Video","WindowPosY", winRect.y); // Keyboard - WritePrivateProfileInt("Misc","KeyMapIndex",CurrentConfig.KeyMap,IniFilePath); + setting.write("Misc","KeyMapIndex",CurrentConfig.KeyMap); // Joystick - WritePrivateProfileInt("LeftJoyStick","UseMouse",LeftJS.UseMouse,IniFilePath); - WritePrivateProfileInt("LeftJoyStick","Left",LeftJS.Left,IniFilePath); - WritePrivateProfileInt("LeftJoyStick","Right",LeftJS.Right,IniFilePath); - WritePrivateProfileInt("LeftJoyStick","Up",LeftJS.Up,IniFilePath); - WritePrivateProfileInt("LeftJoyStick","Down",LeftJS.Down,IniFilePath); - WritePrivateProfileInt("LeftJoyStick","Fire1",LeftJS.Fire1,IniFilePath); - WritePrivateProfileInt("LeftJoyStick","Fire2",LeftJS.Fire2,IniFilePath); - WritePrivateProfileInt("LeftJoyStick","DiDevice",LeftJS.DiDevice,IniFilePath); - WritePrivateProfileInt("LeftJoyStick", "HiResDevice", LeftJS.HiRes, IniFilePath); - WritePrivateProfileInt("RightJoyStick","UseMouse",RightJS.UseMouse,IniFilePath); - WritePrivateProfileInt("RightJoyStick","Left",RightJS.Left,IniFilePath); - WritePrivateProfileInt("RightJoyStick","Right",RightJS.Right,IniFilePath); - WritePrivateProfileInt("RightJoyStick","Up",RightJS.Up,IniFilePath); - WritePrivateProfileInt("RightJoyStick","Down",RightJS.Down,IniFilePath); - WritePrivateProfileInt("RightJoyStick","Fire1",RightJS.Fire1,IniFilePath); - WritePrivateProfileInt("RightJoyStick","Fire2",RightJS.Fire2,IniFilePath); - WritePrivateProfileInt("RightJoyStick","DiDevice",RightJS.DiDevice,IniFilePath); - WritePrivateProfileInt("RightJoyStick", "HiResDevice", RightJS.HiRes, IniFilePath); - WritePrivateProfileInt("Misc","ShowMousePointer",CurrentConfig.ShowMousePointer,IniFilePath); + setting.write("LeftJoyStick","UseMouse",LeftJS.UseMouse); + setting.write("LeftJoyStick","Left",LeftJS.Left); + setting.write("LeftJoyStick","Right",LeftJS.Right); + setting.write("LeftJoyStick","Up",LeftJS.Up); + setting.write("LeftJoyStick","Down",LeftJS.Down); + setting.write("LeftJoyStick","Fire1",LeftJS.Fire1); + setting.write("LeftJoyStick","Fire2",LeftJS.Fire2); + setting.write("LeftJoyStick","DiDevice",LeftJS.DiDevice); + setting.write("LeftJoyStick", "HiResDevice", LeftJS.HiRes); + setting.write("RightJoyStick","UseMouse",RightJS.UseMouse); + setting.write("RightJoyStick","Left",RightJS.Left); + setting.write("RightJoyStick","Right",RightJS.Right); + setting.write("RightJoyStick","Up",RightJS.Up); + setting.write("RightJoyStick","Down",RightJS.Down); + setting.write("RightJoyStick","Fire1",RightJS.Fire1); + setting.write("RightJoyStick","Fire2",RightJS.Fire2); + setting.write("RightJoyStick","DiDevice",RightJS.DiDevice); + setting.write("RightJoyStick", "HiResDevice", RightJS.HiRes); + setting.write("Misc","ShowMousePointer",CurrentConfig.ShowMousePointer); // Force flush inifile Is this required? - WritePrivateProfileString(nullptr,nullptr,nullptr,IniFilePath); + setting.flush(); return 0; } @@ -305,45 +322,49 @@ unsigned char WriteIniFile() // FIXME ReadIniFile should apply changes, should hard reset if RAM or CPU changes unsigned char ReadIniFile() { - unsigned char Index=0; + // TODO: Deal with missing ini path + if (Util::is_null_or_empty(IniFilePath)) return 1; + Util::settings setting(IniFilePath); + + unsigned char Index=0; //Loads the config structure from the hard disk - CurrentConfig.CPUMultiplyer = GetPrivateProfileInt("CPU","DoubleSpeedClock",2,IniFilePath); - CurrentConfig.FrameSkip = GetPrivateProfileInt("CPU","FrameSkip",1,IniFilePath); - CurrentConfig.SpeedThrottle = GetPrivateProfileInt("CPU","Throttle",1,IniFilePath); - CurrentConfig.CpuType = GetPrivateProfileInt("CPU","CpuType",0,IniFilePath); - CurrentConfig.MaxOverclock = GetPrivateProfileInt("CPU","MaxOverClock",100,IniFilePath); - CurrentConfig.BreakOpcEnabled = GetPrivateProfileInt("CPU","BreakEnabled",0,IniFilePath); - - CurrentConfig.AudioRate = GetPrivateProfileInt("Audio","Rate",3,IniFilePath); - GetPrivateProfileString("Audio","SndCard","",CurrentConfig.SoundCardName,63,IniFilePath); - - CurrentConfig.MonitorType = GetPrivateProfileInt("Video","MonitorType",1,IniFilePath); - CurrentConfig.PaletteType = GetPrivateProfileInt("Video","PaletteType",PALETTE_NTSC,IniFilePath); - CurrentConfig.ScanLines = GetPrivateProfileInt("Video","ScanLines",0,IniFilePath); - - CurrentConfig.Aspect = GetPrivateProfileInt("Video","ForceAspect",1,IniFilePath); - CurrentConfig.RememberSize = GetPrivateProfileInt("Video","RememberSize",1,IniFilePath); - CurrentConfig.WindowRect.w = GetPrivateProfileInt("Video", "WindowSizeX", 640, IniFilePath); - CurrentConfig.WindowRect.h = GetPrivateProfileInt("Video", "WindowSizeY", 480, IniFilePath); - CurrentConfig.WindowRect.x = GetPrivateProfileInt("Video", "WindowPosX", CW_USEDEFAULT, IniFilePath); - CurrentConfig.WindowRect.y = GetPrivateProfileInt("Video", "WindowPosY", CW_USEDEFAULT, IniFilePath); - CurrentConfig.AutoStart = GetPrivateProfileInt("Misc","AutoStart",1,IniFilePath); - CurrentConfig.CartAutoStart = GetPrivateProfileInt("Misc","CartAutoStart",1,IniFilePath); - CurrentConfig.ShowMousePointer = GetPrivateProfileInt("Misc","ShowMousePointer",1,IniFilePath); - CurrentConfig.UseExtCocoRom=GetPrivateProfileInt("Misc","UseExtCocoRom",0,IniFilePath); - CurrentConfig.EnableOverclock=GetPrivateProfileInt("Misc","Overclock",1,IniFilePath); - GetPrivateProfileString("Misc","ExternalBasicImage","",CurrentConfig.ExtRomFile,MAX_PATH,IniFilePath); - - CurrentConfig.RamSize = GetPrivateProfileInt("Memory","RamSize",1,IniFilePath); - - GetPrivateProfileString("Module","OnBoot","",CurrentConfig.ModulePath,MAX_PATH,IniFilePath); - - CurrentConfig.KeyMap = GetPrivateProfileInt("Misc","KeyMapIndex",0,IniFilePath); + CurrentConfig.CPUMultiplyer = setting.read("CPU","DoubleSpeedClock",2); + CurrentConfig.FrameSkip = setting.read("CPU","FrameSkip",1); + CurrentConfig.SpeedThrottle = setting.read("CPU","Throttle",1); + CurrentConfig.CpuType = setting.read("CPU","CpuType",0); + CurrentConfig.MaxOverclock = setting.read("CPU","MaxOverClock",100); + CurrentConfig.BreakOpcEnabled = setting.read("CPU","BreakEnabled",0); + + CurrentConfig.AudioRate = setting.read("Audio","Rate",3); + setting.read("Audio","SndCard","",CurrentConfig.SoundCardName,63); + + CurrentConfig.MonitorType = setting.read("Video","MonitorType",1); + CurrentConfig.PaletteType = setting.read("Video","PaletteType",PALETTE_NTSC); + CurrentConfig.ScanLines = setting.read("Video","ScanLines",0); + + CurrentConfig.Aspect = setting.read("Video","ForceAspect",1); + CurrentConfig.RememberSize = setting.read("Video","RememberSize",1); + CurrentConfig.WindowRect.w = setting.read("Video","WindowSizeX", 640); + CurrentConfig.WindowRect.h = setting.read("Video","WindowSizeY", 480); + CurrentConfig.WindowRect.x = setting.read("Video","WindowPosX",CW_USEDEFAULT); + CurrentConfig.WindowRect.y = setting.read("Video","WindowPosY",CW_USEDEFAULT); + CurrentConfig.AutoStart = setting.read("Misc","AutoStart",1); + CurrentConfig.CartAutoStart = setting.read("Misc","CartAutoStart",1); + CurrentConfig.ShowMousePointer = setting.read("Misc","ShowMousePointer",1); + CurrentConfig.UseExtCocoRom = setting.read("Misc","UseExtCocoRom",0); + CurrentConfig.EnableOverclock = setting.read("Misc","Overclock",1); + setting.read("Misc","ExternalBasicImage","",CurrentConfig.ExtRomFile,MAX_PATH); + + CurrentConfig.RamSize = setting.read("Memory","RamSize",1); + + setting.read("Module","OnBoot","",CurrentConfig.ModulePath,MAX_PATH); + + CurrentConfig.KeyMap = setting.read("Misc","KeyMapIndex",0); if (CurrentConfig.KeyMap>3) CurrentConfig.KeyMap=0; //Default to DECB Mapping - GetPrivateProfileString("Misc","CustomKeyMapFile","",KeyMapFilePath,MAX_PATH,IniFilePath); + setting.read("Misc","CustomKeyMapFile","",KeyMapFilePath,MAX_PATH); if (*KeyMapFilePath == '\0') { strcpy(KeyMapFilePath, AppDataPath); strcat(KeyMapFilePath, "\\custom.keymap"); @@ -353,27 +374,27 @@ unsigned char ReadIniFile() CheckPath(CurrentConfig.ModulePath); - LeftJS.UseMouse=GetPrivateProfileInt("LeftJoyStick","UseMouse",1,IniFilePath); - LeftJS.Left=GetPrivateProfileInt("LeftJoyStick","Left",75,IniFilePath); - LeftJS.Right=GetPrivateProfileInt("LeftJoyStick","Right",77,IniFilePath); - LeftJS.Up=GetPrivateProfileInt("LeftJoyStick","Up",72,IniFilePath); - LeftJS.Down=GetPrivateProfileInt("LeftJoyStick","Down",80,IniFilePath); - LeftJS.Fire1=GetPrivateProfileInt("LeftJoyStick","Fire1",59,IniFilePath); - LeftJS.Fire2=GetPrivateProfileInt("LeftJoyStick","Fire2",60,IniFilePath); - LeftJS.DiDevice=GetPrivateProfileInt("LeftJoyStick","DiDevice",0,IniFilePath); - LeftJS.HiRes= GetPrivateProfileInt("LeftJoyStick", "HiResDevice", 0, IniFilePath); - RightJS.UseMouse=GetPrivateProfileInt("RightJoyStick","UseMouse",1,IniFilePath); - RightJS.Left=GetPrivateProfileInt("RightJoyStick","Left",75,IniFilePath); - RightJS.Right=GetPrivateProfileInt("RightJoyStick","Right",77,IniFilePath); - RightJS.Up=GetPrivateProfileInt("RightJoyStick","Up",72,IniFilePath); - RightJS.Down=GetPrivateProfileInt("RightJoyStick","Down",80,IniFilePath); - RightJS.Fire1=GetPrivateProfileInt("RightJoyStick","Fire1",59,IniFilePath); - RightJS.Fire2=GetPrivateProfileInt("RightJoyStick","Fire2",60,IniFilePath); - RightJS.DiDevice=GetPrivateProfileInt("RightJoyStick","DiDevice",0,IniFilePath); - RightJS.HiRes = GetPrivateProfileInt("RightJoyStick", "HiResDevice", 0, IniFilePath); - - GetPrivateProfileString("DefaultPaths", "CassPath", "", CurrentConfig.CassPath, MAX_PATH, IniFilePath); - GetPrivateProfileString("DefaultPaths", "FloppyPath", "", CurrentConfig.FloppyPath, MAX_PATH, IniFilePath); + LeftJS.UseMouse = setting.read("LeftJoyStick" ,"UseMouse",1); + LeftJS.Left = setting.read("LeftJoyStick" ,"Left",75); + LeftJS.Right = setting.read("LeftJoyStick" ,"Right",77); + LeftJS.Up = setting.read("LeftJoyStick" ,"Up",72); + LeftJS.Down = setting.read("LeftJoyStick" ,"Down",80); + LeftJS.Fire1 = setting.read("LeftJoyStick" ,"Fire1",59); + LeftJS.Fire2 = setting.read("LeftJoyStick" ,"Fire2",60); + LeftJS.DiDevice = setting.read("LeftJoyStick" ,"DiDevice",0); + LeftJS.HiRes = setting.read("LeftJoyStick" ,"HiResDevice",0); + RightJS.UseMouse = setting.read("RightJoyStick","UseMouse",1); + RightJS.Left = setting.read("RightJoyStick","Left",75); + RightJS.Right = setting.read("RightJoyStick","Right",77); + RightJS.Up = setting.read("RightJoyStick","Up",72); + RightJS.Down = setting.read("RightJoyStick","Down",80); + RightJS.Fire1 = setting.read("RightJoyStick","Fire1",59); + RightJS.Fire2 = setting.read("RightJoyStick","Fire2",60); + RightJS.DiDevice = setting.read("RightJoyStick","DiDevice",0); + RightJS.HiRes = setting.read("RightJoyStick", "HiResDevice",0); + + setting.read("DefaultPaths", "CassPath", "", CurrentConfig.CassPath, MAX_PATH); + setting.read("DefaultPaths", "FloppyPath", "", CurrentConfig.FloppyPath, MAX_PATH); for (Index = 0; Index < NumberOfSoundCards; Index++) { @@ -386,10 +407,10 @@ unsigned char ReadIniFile() // Make sure Window geometry is reasonable int sw = GetSystemMetrics(SM_CXVIRTUALSCREEN); int sh = GetSystemMetrics(SM_CYVIRTUALSCREEN); - if ( (CurrentConfig.WindowRect.w < 215) | - (CurrentConfig.WindowRect.h < 160) | - (CurrentConfig.WindowRect.x < -100) | - (CurrentConfig.WindowRect.y < -80) | + if ( (CurrentConfig.WindowRect.w < 215) | + (CurrentConfig.WindowRect.h < 160) | + (CurrentConfig.WindowRect.x < -100) | + (CurrentConfig.WindowRect.y < -80) | (CurrentConfig.WindowRect.x > sw-100) | (CurrentConfig.WindowRect.y > sh-80) ) CurrentConfig.WindowRect = {0,0,640,480}; diff --git a/config.h b/config.h index 1044a6e0..69bd519b 100644 --- a/config.h +++ b/config.h @@ -26,6 +26,7 @@ void LoadModule(); unsigned char WriteIniFile(); unsigned char ReadIniFile(); void GetIniFilePath( char *); +void FlushSettings(); // Flush the settings store (vcc init file) void UpdateConfig (); void UpdateSoundBar(const unsigned int *,unsigned int); void UpdateTapeCounter(unsigned int,unsigned char,bool force = false); diff --git a/libcommon/include/vcc/util/fileutil.h b/libcommon/include/vcc/util/fileutil.h index 7209fce8..6b5410f5 100644 --- a/libcommon/include/vcc/util/fileutil.h +++ b/libcommon/include/vcc/util/fileutil.h @@ -96,4 +96,12 @@ namespace VCC::Util { *p = static_cast(std::toupper(static_cast(*p))); } + inline bool is_null_or_empty(const char* s) { + return s == nullptr || *s == '\0'; + } + + inline bool is_null_or_empty(const std::string& s) { + return s.empty(); + } + } diff --git a/libcommon/include/vcc/util/settings.h b/libcommon/include/vcc/util/settings.h index 1be24826..70b977e7 100644 --- a/libcommon/include/vcc/util/settings.h +++ b/libcommon/include/vcc/util/settings.h @@ -18,6 +18,7 @@ #pragma once #include // defines LIBCOMMON_EXPORT if libcommon is a DLL #include +#include namespace VCC::Util { @@ -67,6 +68,24 @@ namespace VCC::Util const std::string& key, int default_value) const; + // delete key + LIBCOMMON_EXPORT void delete_key + (const std::string& section, + const std::string& key) const + { ::WritePrivateProfileString + (section.c_str(), key.c_str(), NULL, path_.c_str());} + + // delete section + LIBCOMMON_EXPORT void delete_section + (const std::string& section) const + { ::WritePrivateProfileString + (section.c_str(), NULL, NULL, path_.c_str());} + + // flush cache + LIBCOMMON_EXPORT void flush () const + { ::WritePrivateProfileString + (NULL, NULL, NULL, path_.c_str());} + private: const path_type path_; }; From b0dac32c3825f72da6c9e9bc2899700b7e901971 Mon Sep 17 00:00:00 2001 From: ejaquay Date: Tue, 20 Jan 2026 00:03:15 -0500 Subject: [PATCH 06/10] All of main uses Setting() to manage config store Switching ini files via File -> Load Config is now much more reliable. Also fixed is quickload causing settings to be permanatly changed. TODO: MPI is not handling ini slot cart entries properly TODO: Have each Config dialog manage it's own settings --- Cassette.cpp | 6 +- config.cpp | 456 ++++++++++++++----------- config.h | 9 +- libcommon/include/vcc/util/fileutil.h | 10 +- libcommon/src/bus/cartridge_loader.cpp | 31 +- libcommon/src/util/fileutil.cpp | 50 +++ pakinterface.cpp | 22 +- 7 files changed, 362 insertions(+), 222 deletions(-) diff --git a/Cassette.cpp b/Cassette.cpp index bdaed93a..8ee6bde9 100644 --- a/Cassette.cpp +++ b/Cassette.cpp @@ -450,9 +450,7 @@ void CloseTapeFile() unsigned int LoadTape() { FileDialog dlg; - char IniFilePath[MAX_PATH]; - GetIniFilePath(IniFilePath); - GetPrivateProfileString("DefaultPaths","CassPath","",CassPath,MAX_PATH,IniFilePath); + Setting().read("DefaultPaths","CassPath","",CassPath,MAX_PATH); dlg.setInitialDir(CassPath); dlg.setFilter("Cassette Files (.cas,.wav)\0*.cas;*.wav\0\0"); dlg.setTitle("Insert Tape Image"); @@ -466,7 +464,7 @@ unsigned int LoadTape() } dlg.getdir(CassPath); if (strcmp(CassPath, "") != 0) - WritePrivateProfileString("DefaultPaths","CassPath",CassPath,IniFilePath); + Setting().write("DefaultPaths","CassPath",CassPath); // turn off fast load for wav files if (FileType == WAV) TapeFastLoad = false; TapeWritten = false; diff --git a/config.cpp b/config.cpp index 421e22e0..39277e4a 100644 --- a/config.cpp +++ b/config.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #pragma warning(push) #pragma warning(disable:4091) @@ -37,7 +38,6 @@ #include "defines.h" #include "resource.h" -#include "config.h" #include "tcc1014graphics.h" #include "mc6821.h" #include "Vcc.h" @@ -56,6 +56,8 @@ #include #include +#include "config.h" + using namespace std; using namespace VCC; @@ -63,11 +65,13 @@ using namespace VCC; /* Local Function Templates */ /********************************************/ +void EstablishIniFile(); int SelectFile(char *); void RefreshJoystickStatus(); unsigned char TranslateDisp2Scan(int); unsigned char TranslateScan2Disp(int); void buildTransDisp2ScanTable(); +void SetBootModulePath(const std::string); LRESULT CALLBACK CpuConfig(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK AudioConfig(HWND, UINT, WPARAM, LPARAM); @@ -164,207 +168,201 @@ unsigned char _TranslateScan2Disp[SCAN_TRANS_COUNT] = { 37,17,29,28,47,48,49,51,0,53,54,50,66,67,0,0,0,0,0,0,0,0,0,0,58, 64,60,0,62,0,63,0,59,65,61,56,57 }; +// Pointer for settings +static Util::settings* gpSettings = nullptr; + /***********************************************************/ /* Establish ini file and Load Settings */ /***********************************************************/ +//--------------------------------------------------------------- +// Establish the settings storage path (IniFilePath) +//--------------------------------------------------------------- +void EstablishIniFile() +{ + // Establish full path to inifile + if (SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_APPDATA, nullptr, 0, AppDataPath))) + OutputDebugString(AppDataPath); + + strcat(AppDataPath, "\\VCC"); + + if (*CmdArg.IniFile) { + GetFullPathNameA(CmdArg.IniFile,MAX_PATH,IniFilePath,nullptr); + } else { + strcpy(IniFilePath, AppDataPath); + strcat(IniFilePath, "\\"); + strcat(IniFilePath, IniFileName); + } +} + +//--------------------------------------------------------------- +// Fatal if this is called before valid IniFilePath is established +//--------------------------------------------------------------- +VCC::Util::settings& Setting() +{ + if (!gpSettings) { + // Fatal if ini file can not be opened + if (!Util::ValidateRWFile(IniFilePath)) { + std::string s = "Can't open settings " + + std::string(IniFilePath); + MessageBox(EmuState.WindowHandle,s.c_str(),"Fatal",0); + exit(0); + } + gpSettings = new Util::settings(IniFilePath); + } + return *gpSettings; +} + +//--------------------------------------------------------------- +// Change the ini file path. (from Vcc File menu) +//--------------------------------------------------------------- +void SetIniFilePath(const char * path) +{ + DLOG_C("SetIniFilePath %s\n",path); + if (Util::ValidateRWFile(path)) { + strcpy(IniFilePath,path); + // Destroy settings object + delete gpSettings; + gpSettings = nullptr; + // Load new settings + ReadIniFile(); + // Reload boot module + UnloadPack(); + LoadModule(); + // Reset + EmuState.ResetPending=2; + UpdateConfig(); + } +} + +//--------------------------------------------------------------- +// Return settings file path +//--------------------------------------------------------------- +void GetIniFilePath( char *Path) +{ + // FIXME convert to returning a string + strcpy(Path,IniFilePath); + return; +} + +//--------------------------------------------------------------- // Flush the settings store +//--------------------------------------------------------------- void FlushSettings() { - if (Util::is_null_or_empty(IniFilePath)) return; - Util::settings settings(IniFilePath); - settings.flush(); + Setting().flush(); } +//--------------------------------------------------------------- +// Initial system config +//--------------------------------------------------------------- void LoadConfig(SystemState *LCState) { - HANDLE hr=nullptr; - int lasterror; - buildTransDisp2ScanTable(); + // Establish application path LoadString(nullptr, IDS_APP_TITLE,AppName, MAX_LOADSTRING); GetModuleFileName(nullptr,ExecDirectory,MAX_PATH); PathRemoveFileSpec(ExecDirectory); - if (SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_APPDATA, nullptr, 0, AppDataPath))) - OutputDebugString(AppDataPath); strcpy(CurrentConfig.PathtoExe,ExecDirectory); - strcat(AppDataPath, "\\VCC"); - - if (_mkdir(AppDataPath) != 0) { - OutputDebugString("Unable to create VCC config folder."); - } - - if (*CmdArg.IniFile) { - GetFullPathNameA(CmdArg.IniFile,MAX_PATH,IniFilePath,nullptr); - } else { - strcpy(IniFilePath, AppDataPath); - strcat(IniFilePath, "\\"); - strcat(IniFilePath, IniFileName); - } - + // Make sure scanlines is off LCState->ScanLines=0; + + // Get sound cards from audio.cpp NumberOfSoundCards=GetSoundCardList(SoundCards); + // Establish settings store + EstablishIniFile(); + + // Load settings from store ReadIniFile(); - // The rest of this belongs in ReadIniFile(); maybe it needs a name change - // FIXME ReadIniFile() should cause hard reset. CurrentConfig.RebootNow=0; - - // FIXME ReadIniFile should apply changes then UpdateConfig() here not needed UpdateConfig(); + RefreshJoystickStatus(); - if (EmuState.WindowHandle != nullptr) - InitSound(); - - // FIXME: This belongs in ReadIniFile(), already called above - // Try to open the config file. Create it if necessary. Abort if failure. - hr = CreateFile(IniFilePath, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - lasterror = GetLastError(); - if (hr==INVALID_HANDLE_VALUE) { // Fatal could not open ini file - MessageBox(nullptr,"Could not open ini file","Error",0); - exit(0); - } else { - CloseHandle(hr); - if (lasterror != ERROR_ALREADY_EXISTS) WriteIniFile(); //!=183 - } + if (EmuState.WindowHandle != nullptr) InitSound(); + } +//--------------------------------------------------------------- +// Set up sound cards +//--------------------------------------------------------------- void InitSound() { SoundInit(EmuState.WindowHandle, SoundCards[CurrentConfig.SndOutDev].Guid, CurrentConfig.AudioRate); } -/***********************************************************/ -/* Save All Configuration Settings to ini file */ -/***********************************************************/ - -// FIXME WriteIniFile() is a sledgehammer. -// Each config dialog should save the stuff it sets -unsigned char WriteIniFile() +//-------------------------------------------------------------------------- +// Make sure boot module path is reasonable for cartridge_loader +// pakinterface loads boot module and saves the ModulePath setting +// Still need to verify the boot module after switching the ini file +//-------------------------------------------------------------------------- +void SetBootModulePath(const std::string bootpath) { - // TODO: Deal with missing ini path - if (Util::is_null_or_empty(IniFilePath)) return 1; - Util::settings setting(IniFilePath); - - setting.write("Version","Release",AppName); - - GetCurrentModule(CurrentConfig.ModulePath); - ValidatePath(CurrentConfig.ModulePath); - setting.write("Module", "OnBoot", CurrentConfig.ModulePath); - - // CPU - setting.write("CPU","DoubleSpeedClock",CurrentConfig.CPUMultiplyer); - setting.write("CPU","FrameSkip",CurrentConfig.FrameSkip); - setting.write("CPU","Throttle",CurrentConfig.SpeedThrottle); - setting.write("CPU","CpuType",CurrentConfig.CpuType); - setting.write("CPU","MaxOverClock", CurrentConfig.MaxOverclock); - setting.write("CPU","BreakEnabled",CurrentConfig.BreakOpcEnabled); - setting.write("Memory","RamSize",CurrentConfig.RamSize); - setting.write("Misc","AutoStart",CurrentConfig.AutoStart); - setting.write("Misc","CartAutoStart",CurrentConfig.CartAutoStart); - setting.write("Misc","UseExtCocoRom", CurrentConfig.UseExtCocoRom); - setting.write("Misc","Overclock", CurrentConfig.EnableOverclock); - setting.write("Misc","ExternalBasicImage", CurrentConfig.ExtRomFile); - - // Audio - setting.write("Audio","SndCard",CurrentConfig.SoundCardName); - setting.write("Audio","Rate",CurrentConfig.AudioRate); - - // Video - Rect winRect = GetCurWindowSize(); - setting.write("Video","MonitorType",CurrentConfig.MonitorType); - setting.write("Video","PaletteType",CurrentConfig.PaletteType); - setting.write("Video","ScanLines",CurrentConfig.ScanLines); - setting.write("Video","ForceAspect",CurrentConfig.Aspect); - setting.write("Video","RememberSize", CurrentConfig.RememberSize); - setting.write("Video","WindowSizeX", winRect.w); - setting.write("Video","WindowSizeY", winRect.h); - setting.write("Video","WindowPosX", winRect.x); - setting.write("Video","WindowPosY", winRect.y); - - // Keyboard - setting.write("Misc","KeyMapIndex",CurrentConfig.KeyMap); - - // Joystick - setting.write("LeftJoyStick","UseMouse",LeftJS.UseMouse); - setting.write("LeftJoyStick","Left",LeftJS.Left); - setting.write("LeftJoyStick","Right",LeftJS.Right); - setting.write("LeftJoyStick","Up",LeftJS.Up); - setting.write("LeftJoyStick","Down",LeftJS.Down); - setting.write("LeftJoyStick","Fire1",LeftJS.Fire1); - setting.write("LeftJoyStick","Fire2",LeftJS.Fire2); - setting.write("LeftJoyStick","DiDevice",LeftJS.DiDevice); - setting.write("LeftJoyStick", "HiResDevice", LeftJS.HiRes); - setting.write("RightJoyStick","UseMouse",RightJS.UseMouse); - setting.write("RightJoyStick","Left",RightJS.Left); - setting.write("RightJoyStick","Right",RightJS.Right); - setting.write("RightJoyStick","Up",RightJS.Up); - setting.write("RightJoyStick","Down",RightJS.Down); - setting.write("RightJoyStick","Fire1",RightJS.Fire1); - setting.write("RightJoyStick","Fire2",RightJS.Fire2); - setting.write("RightJoyStick","DiDevice",RightJS.DiDevice); - setting.write("RightJoyStick", "HiResDevice", RightJS.HiRes); - setting.write("Misc","ShowMousePointer",CurrentConfig.ShowMousePointer); - // Force flush inifile Is this required? - setting.flush(); - return 0; + memset(CurrentConfig.ModulePath,0,sizeof(CurrentConfig.ModulePath)); + if (bootpath.empty()) return; + + std::string path = Util::QualifyPath(bootpath); + namespace fs = std::filesystem; + fs::path p = path; + if (fs::exists(p) && (fs::file_size(p) > 2)) { + Setting().write("Module","OnBoot",path); + strncpy(CurrentConfig.ModulePath, + path.c_str(), + sizeof(CurrentConfig.ModulePath)); + } else { + // Delete the key if it does not + Setting().delete_key("Module","OnBoot"); + std::string msg = "Invalid boot module '"+bootpath+"' removed"; + MessageBox(EmuState.WindowHandle,msg.c_str(),"Warn",MB_OK); + } } -/***********************************************************/ -/* Load Configuration Settings from ini file */ -/***********************************************************/ +//--------------------------------------------------------------- +// Load Configuration Settings from ini file +//--------------------------------------------------------------- -// FIXME ReadIniFile should apply changes, should hard reset if RAM or CPU changes unsigned char ReadIniFile() { - - // TODO: Deal with missing ini path - if (Util::is_null_or_empty(IniFilePath)) return 1; - Util::settings setting(IniFilePath); - unsigned char Index=0; //Loads the config structure from the hard disk - CurrentConfig.CPUMultiplyer = setting.read("CPU","DoubleSpeedClock",2); - CurrentConfig.FrameSkip = setting.read("CPU","FrameSkip",1); - CurrentConfig.SpeedThrottle = setting.read("CPU","Throttle",1); - CurrentConfig.CpuType = setting.read("CPU","CpuType",0); - CurrentConfig.MaxOverclock = setting.read("CPU","MaxOverClock",100); - CurrentConfig.BreakOpcEnabled = setting.read("CPU","BreakEnabled",0); - - CurrentConfig.AudioRate = setting.read("Audio","Rate",3); - setting.read("Audio","SndCard","",CurrentConfig.SoundCardName,63); - - CurrentConfig.MonitorType = setting.read("Video","MonitorType",1); - CurrentConfig.PaletteType = setting.read("Video","PaletteType",PALETTE_NTSC); - CurrentConfig.ScanLines = setting.read("Video","ScanLines",0); - - CurrentConfig.Aspect = setting.read("Video","ForceAspect",1); - CurrentConfig.RememberSize = setting.read("Video","RememberSize",1); - CurrentConfig.WindowRect.w = setting.read("Video","WindowSizeX", 640); - CurrentConfig.WindowRect.h = setting.read("Video","WindowSizeY", 480); - CurrentConfig.WindowRect.x = setting.read("Video","WindowPosX",CW_USEDEFAULT); - CurrentConfig.WindowRect.y = setting.read("Video","WindowPosY",CW_USEDEFAULT); - CurrentConfig.AutoStart = setting.read("Misc","AutoStart",1); - CurrentConfig.CartAutoStart = setting.read("Misc","CartAutoStart",1); - CurrentConfig.ShowMousePointer = setting.read("Misc","ShowMousePointer",1); - CurrentConfig.UseExtCocoRom = setting.read("Misc","UseExtCocoRom",0); - CurrentConfig.EnableOverclock = setting.read("Misc","Overclock",1); - setting.read("Misc","ExternalBasicImage","",CurrentConfig.ExtRomFile,MAX_PATH); - - CurrentConfig.RamSize = setting.read("Memory","RamSize",1); - - setting.read("Module","OnBoot","",CurrentConfig.ModulePath,MAX_PATH); - - CurrentConfig.KeyMap = setting.read("Misc","KeyMapIndex",0); + CurrentConfig.CPUMultiplyer = Setting().read("CPU","DoubleSpeedClock",2); + CurrentConfig.FrameSkip = Setting().read("CPU","FrameSkip",1); + CurrentConfig.SpeedThrottle = Setting().read("CPU","Throttle",1); + CurrentConfig.CpuType = Setting().read("CPU","CpuType",0); + CurrentConfig.MaxOverclock = Setting().read("CPU","MaxOverClock",100); + CurrentConfig.BreakOpcEnabled = Setting().read("CPU","BreakEnabled",0); + + CurrentConfig.AudioRate = Setting().read("Audio","Rate",3); + Setting().read("Audio","SndCard","",CurrentConfig.SoundCardName,63); + + CurrentConfig.MonitorType = Setting().read("Video","MonitorType",1); + CurrentConfig.PaletteType = Setting().read("Video","PaletteType",PALETTE_NTSC); + CurrentConfig.ScanLines = Setting().read("Video","ScanLines",0); + + CurrentConfig.Aspect = Setting().read("Video","ForceAspect",1); + CurrentConfig.RememberSize = Setting().read("Video","RememberSize",1); + CurrentConfig.WindowRect.w = Setting().read("Video","WindowSizeX", 640); + CurrentConfig.WindowRect.h = Setting().read("Video","WindowSizeY", 480); + CurrentConfig.WindowRect.x = Setting().read("Video","WindowPosX",CW_USEDEFAULT); + CurrentConfig.WindowRect.y = Setting().read("Video","WindowPosY",CW_USEDEFAULT); + CurrentConfig.AutoStart = Setting().read("Misc","AutoStart",1); + CurrentConfig.CartAutoStart = Setting().read("Misc","CartAutoStart",1); + CurrentConfig.ShowMousePointer = Setting().read("Misc","ShowMousePointer",1); + CurrentConfig.UseExtCocoRom = Setting().read("Misc","UseExtCocoRom",0); + CurrentConfig.EnableOverclock = Setting().read("Misc","Overclock",1); + Setting().read("Misc","ExternalBasicImage","",CurrentConfig.ExtRomFile,MAX_PATH); + + CurrentConfig.RamSize = Setting().read("Memory","RamSize",1); + + CurrentConfig.KeyMap = Setting().read("Misc","KeyMapIndex",0); if (CurrentConfig.KeyMap>3) CurrentConfig.KeyMap=0; //Default to DECB Mapping - setting.read("Misc","CustomKeyMapFile","",KeyMapFilePath,MAX_PATH); + Setting().read("Misc","CustomKeyMapFile","",KeyMapFilePath,MAX_PATH); if (*KeyMapFilePath == '\0') { strcpy(KeyMapFilePath, AppDataPath); strcat(KeyMapFilePath, "\\custom.keymap"); @@ -372,29 +370,31 @@ unsigned char ReadIniFile() if (CurrentConfig.KeyMap == kKBLayoutCustom) LoadCustomKeyMap(KeyMapFilePath); vccKeyboardBuildRuntimeTable((keyboardlayout_e)CurrentConfig.KeyMap); - CheckPath(CurrentConfig.ModulePath); - - LeftJS.UseMouse = setting.read("LeftJoyStick" ,"UseMouse",1); - LeftJS.Left = setting.read("LeftJoyStick" ,"Left",75); - LeftJS.Right = setting.read("LeftJoyStick" ,"Right",77); - LeftJS.Up = setting.read("LeftJoyStick" ,"Up",72); - LeftJS.Down = setting.read("LeftJoyStick" ,"Down",80); - LeftJS.Fire1 = setting.read("LeftJoyStick" ,"Fire1",59); - LeftJS.Fire2 = setting.read("LeftJoyStick" ,"Fire2",60); - LeftJS.DiDevice = setting.read("LeftJoyStick" ,"DiDevice",0); - LeftJS.HiRes = setting.read("LeftJoyStick" ,"HiResDevice",0); - RightJS.UseMouse = setting.read("RightJoyStick","UseMouse",1); - RightJS.Left = setting.read("RightJoyStick","Left",75); - RightJS.Right = setting.read("RightJoyStick","Right",77); - RightJS.Up = setting.read("RightJoyStick","Up",72); - RightJS.Down = setting.read("RightJoyStick","Down",80); - RightJS.Fire1 = setting.read("RightJoyStick","Fire1",59); - RightJS.Fire2 = setting.read("RightJoyStick","Fire2",60); - RightJS.DiDevice = setting.read("RightJoyStick","DiDevice",0); - RightJS.HiRes = setting.read("RightJoyStick", "HiResDevice",0); - - setting.read("DefaultPaths", "CassPath", "", CurrentConfig.CassPath, MAX_PATH); - setting.read("DefaultPaths", "FloppyPath", "", CurrentConfig.FloppyPath, MAX_PATH); + // Set up boot module path + std::string bootpath = Util::QualifyPath(Setting().read("Module","OnBoot","")); + SetBootModulePath(bootpath); + + LeftJS.UseMouse = Setting().read("LeftJoyStick" ,"UseMouse",1); + LeftJS.Left = Setting().read("LeftJoyStick" ,"Left",75); + LeftJS.Right = Setting().read("LeftJoyStick" ,"Right",77); + LeftJS.Up = Setting().read("LeftJoyStick" ,"Up",72); + LeftJS.Down = Setting().read("LeftJoyStick" ,"Down",80); + LeftJS.Fire1 = Setting().read("LeftJoyStick" ,"Fire1",59); + LeftJS.Fire2 = Setting().read("LeftJoyStick" ,"Fire2",60); + LeftJS.DiDevice = Setting().read("LeftJoyStick" ,"DiDevice",0); + LeftJS.HiRes = Setting().read("LeftJoyStick" ,"HiResDevice",0); + RightJS.UseMouse = Setting().read("RightJoyStick","UseMouse",1); + RightJS.Left = Setting().read("RightJoyStick","Left",75); + RightJS.Right = Setting().read("RightJoyStick","Right",77); + RightJS.Up = Setting().read("RightJoyStick","Up",72); + RightJS.Down = Setting().read("RightJoyStick","Down",80); + RightJS.Fire1 = Setting().read("RightJoyStick","Fire1",59); + RightJS.Fire2 = Setting().read("RightJoyStick","Fire2",60); + RightJS.DiDevice = Setting().read("RightJoyStick","DiDevice",0); + RightJS.HiRes = Setting().read("RightJoyStick", "HiResDevice",0); + + Setting().read("DefaultPaths", "CassPath", "", CurrentConfig.CassPath, MAX_PATH); + Setting().read("DefaultPaths", "FloppyPath", "", CurrentConfig.FloppyPath, MAX_PATH); for (Index = 0; Index < NumberOfSoundCards; Index++) { @@ -418,6 +418,78 @@ unsigned char ReadIniFile() return 0; } +//--------------------------------------------------------------- +// Save All Configuration Settings to ini file +//--------------------------------------------------------------- + +// FIXME WriteIniFile() is a sledgehammer. +// Each config dialog should save the stuff it changes +unsigned char WriteIniFile() +{ + + // ModulePath is managed by pakinterface.cpp + // AppName is set in LoadConfig() + Setting().write("Version","Release",AppName); + + // CPU + Setting().write("CPU","DoubleSpeedClock",CurrentConfig.CPUMultiplyer); + Setting().write("CPU","FrameSkip",CurrentConfig.FrameSkip); + Setting().write("CPU","Throttle",CurrentConfig.SpeedThrottle); + Setting().write("CPU","CpuType",CurrentConfig.CpuType); + Setting().write("CPU","MaxOverClock", CurrentConfig.MaxOverclock); + Setting().write("CPU","BreakEnabled",CurrentConfig.BreakOpcEnabled); + Setting().write("Memory","RamSize",CurrentConfig.RamSize); + Setting().write("Misc","AutoStart",CurrentConfig.AutoStart); + Setting().write("Misc","CartAutoStart",CurrentConfig.CartAutoStart); + Setting().write("Misc","UseExtCocoRom", CurrentConfig.UseExtCocoRom); + Setting().write("Misc","Overclock", CurrentConfig.EnableOverclock); + Setting().write("Misc","ExternalBasicImage", CurrentConfig.ExtRomFile); + + // Audio + Setting().write("Audio","SndCard",CurrentConfig.SoundCardName); + Setting().write("Audio","Rate",CurrentConfig.AudioRate); + + // Video + Rect winRect = GetCurWindowSize(); + Setting().write("Video","MonitorType",CurrentConfig.MonitorType); + Setting().write("Video","PaletteType",CurrentConfig.PaletteType); + Setting().write("Video","ScanLines",CurrentConfig.ScanLines); + Setting().write("Video","ForceAspect",CurrentConfig.Aspect); + Setting().write("Video","RememberSize", CurrentConfig.RememberSize); + Setting().write("Video","WindowSizeX", winRect.w); + Setting().write("Video","WindowSizeY", winRect.h); + Setting().write("Video","WindowPosX", winRect.x); + Setting().write("Video","WindowPosY", winRect.y); + + // Keyboard + Setting().write("Misc","KeyMapIndex",CurrentConfig.KeyMap); + + // Joystick + Setting().write("LeftJoyStick","UseMouse",LeftJS.UseMouse); + Setting().write("LeftJoyStick","Left",LeftJS.Left); + Setting().write("LeftJoyStick","Right",LeftJS.Right); + Setting().write("LeftJoyStick","Up",LeftJS.Up); + Setting().write("LeftJoyStick","Down",LeftJS.Down); + Setting().write("LeftJoyStick","Fire1",LeftJS.Fire1); + Setting().write("LeftJoyStick","Fire2",LeftJS.Fire2); + Setting().write("LeftJoyStick","DiDevice",LeftJS.DiDevice); + Setting().write("LeftJoyStick", "HiResDevice", LeftJS.HiRes); + Setting().write("RightJoyStick","UseMouse",RightJS.UseMouse); + Setting().write("RightJoyStick","Left",RightJS.Left); + Setting().write("RightJoyStick","Right",RightJS.Right); + Setting().write("RightJoyStick","Up",RightJS.Up); + Setting().write("RightJoyStick","Down",RightJS.Down); + Setting().write("RightJoyStick","Fire1",RightJS.Fire1); + Setting().write("RightJoyStick","Fire2",RightJS.Fire2); + Setting().write("RightJoyStick","DiDevice",RightJS.DiDevice); + Setting().write("RightJoyStick", "HiResDevice", RightJS.HiRes); + Setting().write("Misc","ShowMousePointer",CurrentConfig.ShowMousePointer); + + // Force flush inifile Is this required? + Setting().flush(); + return 0; +} + void LoadModule() { if (CurrentConfig.ModulePath[0]) @@ -445,19 +517,6 @@ void SetWindowRect(const Rect& rect) } } -/********************************************/ -/* Config File paths */ -/********************************************/ -void GetIniFilePath( char *Path) -{ - strcpy(Path,IniFilePath); - return; -} -void SetIniFilePath( const char *Path) -{ - // Path must be to an existing ini file - strcpy(IniFilePath,Path); -} // The following two functions only work after LoadConfig has been called char * AppDirectory() { @@ -470,7 +529,7 @@ char * GetKeyMapFilePath() void SetKeyMapFilePath(const char *Path) { strcpy(KeyMapFilePath,Path); - WritePrivateProfileString("Misc","CustomKeyMapFile",KeyMapFilePath,IniFilePath); + Setting().write("Misc","CustomKeyMapFile",KeyMapFilePath); } /*******************************************************/ @@ -914,9 +973,6 @@ LRESULT CALLBACK AudioConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lP CurrentConfig.AudioRate = tmpcfg.AudioRate; CurrentConfig.SndOutDev = tmpcfg.SndOutDev; strcpy(CurrentConfig.SoundCardName, SoundCards[CurrentConfig.SndOutDev].CardName); -// FIXME Should only save Audio stuff here -//WritePrivateProfileString("Audio","SndCard",CurrentConfig.SoundCardName,IniFilePath); -//WritePrivateProfileInt("Audio","Rate",CurrentConfig.AudioRate,IniFilePath); // FIXME Save mute button state (AudioRate) WriteIniFile(); @@ -1633,8 +1689,7 @@ LRESULT CALLBACK Paths(HWND /*hDlg*/, UINT /*message*/, WPARAM /*wParam*/, LPARA int SelectFile(char *FileName) { char CapFilePath[MAX_PATH]; - GetPrivateProfileString - ("DefaultPaths", "CapFilePath", "", CapFilePath, MAX_PATH, IniFilePath); + Setting().read("DefaultPaths", "CapFilePath", "", CapFilePath, MAX_PATH); FileDialog dlg; dlg.setTitle("Open print capture file"); @@ -1648,8 +1703,7 @@ int SelectFile(char *FileName) if (OpenPrintFile(dlg.path())) { dlg.getdir(CapFilePath); if (strcmp(CapFilePath,"") != 0) { - WritePrivateProfileString - ("DefaultPaths", "CapFilePath", CapFilePath, IniFilePath); + Setting().write("DefaultPaths", "CapFilePath", CapFilePath); } } else { MessageBox(EmuState.WindowHandle,"Can't open file.","Error",0); diff --git a/config.h b/config.h index 69bd519b..6ea99ae5 100644 --- a/config.h +++ b/config.h @@ -17,15 +17,22 @@ This file is part of VCC (Virtual Color Computer). You should have received a copy of the GNU General Public License along with VCC (Virtual Color Computer). If not, see . */ + #include "defines.h" #include +#include +//FIXME only shared functions belong here the rest should be in config.cpp +void FlushSettings(); void LoadConfig(SystemState *); void InitSound(); void LoadModule(); unsigned char WriteIniFile(); unsigned char ReadIniFile(); -void GetIniFilePath( char *); + +VCC::Util::settings& Setting(); // New way to manage settings +void GetIniFilePath( char *); // for passing to cart dll's + void FlushSettings(); // Flush the settings store (vcc init file) void UpdateConfig (); void UpdateSoundBar(const unsigned int *,unsigned int); diff --git a/libcommon/include/vcc/util/fileutil.h b/libcommon/include/vcc/util/fileutil.h index 6b5410f5..2618fb11 100644 --- a/libcommon/include/vcc/util/fileutil.h +++ b/libcommon/include/vcc/util/fileutil.h @@ -58,6 +58,15 @@ namespace VCC::Util { // If path is in the application directory strip directory LIBCOMMON_EXPORT std::string strip_application_path(std::string path); + // Verify that a file can be opened for read/write + LIBCOMMON_EXPORT bool ValidateRWFile(const std::string& path); + + // Verify that a file can be opened for read + LIBCOMMON_EXPORT bool ValidateRDFile(const std::string& path); + + // Fully qualify a file based on execution directory + LIBCOMMON_EXPORT std::string QualifyPath(const std::string& path); + // TODO: following stuff should go elsewhere // Return string with case conversion @@ -103,5 +112,4 @@ namespace VCC::Util { inline bool is_null_or_empty(const std::string& s) { return s.empty(); } - } diff --git a/libcommon/src/bus/cartridge_loader.cpp b/libcommon/src/bus/cartridge_loader.cpp index ffe27b09..6370dedc 100644 --- a/libcommon/src/bus/cartridge_loader.cpp +++ b/libcommon/src/bus/cartridge_loader.cpp @@ -1,3 +1,4 @@ +//#define USE_LOGGING //////////////////////////////////////////////////////////////////////////////// // Copyright 2015 by Joseph Forgione // This file is part of VCC (Virtual Color Computer). @@ -18,11 +19,11 @@ #include #include #include +#include #include #include #include - namespace VCC::Core { @@ -40,20 +41,31 @@ namespace VCC::Core } } - // Determine cart type by looking for magic 'MZ' cartridge_file_type determine_cartridge_type(const std::string& filename) { std::ifstream input(filename, std::ios::binary); - if (!input.is_open()) - { + + if (!input.is_open()) { return cartridge_file_type::not_opened; } - if (input.get() == 'M' && input.get() == 'Z') - { + // Read two bytes from the start of the file + unsigned char header[2] = {}; + input.clear(); + input.seekg(0, std::ios::beg); + input.read(reinterpret_cast(header), 2); + if (!input) { + DLOG_C("core::cartridge is too small\n\n"); + return cartridge_file_type::rom_image; + } + + // Check for magic 'MZ' DLL indicator + if (header[0] == 'M' && header[1] == 'Z') { + DLOG_C("core::cartridge type library\n\n"); return cartridge_file_type::library; } + DLOG_C("core::cartridge assume rom\n\n"); return cartridge_file_type::rom_image; } @@ -70,8 +82,7 @@ namespace VCC::Core // Open the ROM file, fail if unable to std::ifstream input(filename, std::ios::binary); - if (!input.is_open()) - { + if (!input.is_open()) { return { nullptr, nullptr, cartridge_loader_status::cannot_open }; } @@ -82,8 +93,7 @@ namespace VCC::Core std::istream_iterator(), back_inserter(romImage)); - if (romImage.empty()) - { + if (romImage.empty()) { return { nullptr, nullptr, cartridge_loader_status::not_rom }; } @@ -160,6 +170,7 @@ namespace VCC::Core HWND hVccWnd, const cpak_callbacks& cpak_callbacks) { + DLOG_C("\ncore::load_cartridge %s\n",filename.c_str()); switch (VCC::Core::determine_cartridge_type(filename)) { default: diff --git a/libcommon/src/util/fileutil.cpp b/libcommon/src/util/fileutil.cpp index 1f344d65..c3d53f7a 100644 --- a/libcommon/src/util/fileutil.cpp +++ b/libcommon/src/util/fileutil.cpp @@ -142,4 +142,54 @@ namespace VCC::Util return path; } + //--------------------------------------------------------------- + // Verify that a file can be opened for read/write + //--------------------------------------------------------------- + bool ValidateRWFile(const std::string& path) + { + HANDLE h = CreateFile + (path.c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, nullptr, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, nullptr); + + if (h==INVALID_HANDLE_VALUE) + return false; + + CloseHandle(h); + return true; + } + + //--------------------------------------------------------------- + // Verify that a file can be opened for read + //--------------------------------------------------------------- + bool ValidateRDFile(const std::string& path) + { + HANDLE h = CreateFile + (path.c_str(), GENERIC_READ, + FILE_SHARE_READ, nullptr, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, nullptr); + + if (h==INVALID_HANDLE_VALUE) + return false; + + CloseHandle(h); + return true; + } + + //--------------------------------------------------------------- + // Fully qualify a file based on current execution directory + //--------------------------------------------------------------- + std::string QualifyPath(const std::string& path) + { + if (path.empty()) return {}; + + std::string mod = path; + FixDirSlashes(mod); + if (mod.find('/') != std::string::npos) return mod; + + std::string exe = Util::get_module_path(NULL); + std::string dir = Util::GetDirectoryPart(exe); + return dir + '/' + mod; + } + } diff --git a/pakinterface.cpp b/pakinterface.cpp index 557a5f3b..2c0500a2 100644 --- a/pakinterface.cpp +++ b/pakinterface.cpp @@ -225,16 +225,17 @@ void PakLoadCartridgeUI(int type) char inifile[MAX_PATH]; GetIniFilePath(inifile); - static char cartDir[MAX_PATH] = ""; + +static char cartDir[MAX_PATH] = ""; FileDialog dlg; if (type == 0) { dlg.setTitle(TEXT("Load Program Pack")); dlg.setFilter("Hardware Packs\0*.dll\0All Supported Formats (*.dll;*.ccc;*.rom)\0*.dll;*.ccc;*.rom\0\0"); - GetPrivateProfileString("DefaultPaths", "MPIPath", "", cartDir, MAX_PATH, inifile); + Setting().read("DefaultPaths", "MPIPath", "", cartDir, MAX_PATH); } else { dlg.setTitle(TEXT("Load ROM")); dlg.setFilter("Rom Packs(*.ccc;*.rom)\0*.ccc;*.rom\0All Supported Formats (*.dll;*.ccc;*.rom)\0*.dll;*.ccc;*.rom\0\0"); - GetPrivateProfileString("DefaultPaths", "PakPath", "", cartDir, MAX_PATH, inifile); + Setting().read("DefaultPaths", "PakPath", "", cartDir, MAX_PATH); } dlg.setInitialDir(cartDir); dlg.setFlags(OFN_FILEMUSTEXIST); @@ -244,9 +245,9 @@ void PakLoadCartridgeUI(int type) dlg.getdir(cartDir); dlg.gettype(filetype); if ((strcmp(filetype,"dll") == 0) | (strcmp(filetype,"DLL") == 0 )) { // DLL? - WritePrivateProfileString("DefaultPaths", "MPIPath", cartDir, inifile); + Setting().write("DefaultPaths", "MPIPath", cartDir); } else { - WritePrivateProfileString("DefaultPaths", "PAKPath", cartDir, inifile); + Setting().write("DefaultPaths", "PAKPath", cartDir); } } } @@ -269,6 +270,8 @@ cartridge_loader_status PakLoadCartridge(const char* filename) const auto result(load_any_cartridge(filename, TempIni)); if (result == cartridge_loader_status::success) { + DLOG_C("pakinterface load cart %s\n",filename); + Setting().write("Module", "OnBoot", filename); return result; } @@ -287,6 +290,11 @@ static cartridge_loader_status load_any_cartridge(const char *filename, const ch // callback_context is a host-owned opaque per-cartridge state. Passed to the cartridge // at initialization and returned on every callback so the host can route and manage a // specific cartridge instance. Currently for pakinterface it is empty, but MPI uses it. + + // TODO:: Consider using a slot number instead. slot0 for this, slot1-slot4 for MMI + // That way all loaded cart context can reside in a simple vector with no reliance + // on cart trampolines to maintain context. With slot_num in the callbacks MPI will + // not have intercept to adjust menu_id's and forwarding the callback to main. auto vccContext=std::make_unique(); cpak_callbacks callbacks { @@ -357,6 +365,10 @@ void UnloadPack() strcpy(DllPath,""); SetCart(0); EmuState.ResetPending=2; + + char inifile[MAX_PATH]; + GetIniFilePath(inifile); + Setting().delete_key("Module", "OnBoot"); } void LoadPack(int type) { From 97cc84cc3250592f03801126bf06dc94106dcb17 Mon Sep 17 00:00:00 2001 From: ejaquay Date: Tue, 20 Jan 2026 16:56:55 -0500 Subject: [PATCH 07/10] Use forward slashes as dir seperator where possible --- Vcc.cpp | 2 +- config.cpp | 151 +++++++++++++------------- libcommon/include/vcc/util/fileutil.h | 141 ++++++++++++++++++++---- libcommon/src/util/DialogOps.cpp | 46 ++++---- libcommon/src/util/fileutil.cpp | 106 ++---------------- 5 files changed, 230 insertions(+), 216 deletions(-) diff --git a/Vcc.cpp b/Vcc.cpp index 6fc500b4..fe2429f5 100644 --- a/Vcc.cpp +++ b/Vcc.cpp @@ -1025,7 +1025,7 @@ unsigned __stdcall EmuLoop(HANDLE hEvent) break; case 4: - UpdateConfig(); // Apply video and cpy changes + UpdateConfig(); // Apply video and cpy changes DoCls(&EmuState); break; diff --git a/config.cpp b/config.cpp index 39277e4a..fc429ef0 100644 --- a/config.cpp +++ b/config.cpp @@ -26,6 +26,7 @@ #include #include #include +//#include #include #include #include @@ -65,7 +66,6 @@ using namespace VCC; /* Local Function Templates */ /********************************************/ -void EstablishIniFile(); int SelectFile(char *); void RefreshJoystickStatus(); unsigned char TranslateDisp2Scan(int); @@ -124,14 +124,13 @@ static STRConfig CurrentConfig; static HICON CpuIcons[2],MonIcons[2],JoystickIcons[4]; static unsigned char temp=0,temp2=0; -static char IniFileName[]="Vcc.ini"; -static char IniFilePath[MAX_PATH]=""; +static char gcIniFilePath[MAX_PATH]=""; static char KeyMapFilePath[MAX_PATH]=""; static char TapeFileName[MAX_PATH]=""; static char ExecDirectory[MAX_PATH]=""; static char SerialCaptureFile[MAX_PATH]=""; static unsigned char NumberofJoysticks=0; -TCHAR AppDataPath[MAX_PATH]; +TCHAR gcAppDataPath[MAX_PATH]; char OutBuffer[MAX_PATH]=""; char AppName[MAX_LOADSTRING]=""; @@ -176,51 +175,83 @@ static Util::settings* gpSettings = nullptr; /***********************************************************/ //--------------------------------------------------------------- -// Establish the settings storage path (IniFilePath) +// Initial config. This should be called only once when VCC starts up //--------------------------------------------------------------- -void EstablishIniFile() -{ - // Establish full path to inifile - if (SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_APPDATA, nullptr, 0, AppDataPath))) - OutputDebugString(AppDataPath); +void LoadConfig(SystemState *LCState) +{ + buildTransDisp2ScanTable(); - strcat(AppDataPath, "\\VCC"); + // Establish application path + LoadString(nullptr, IDS_APP_TITLE,AppName, MAX_LOADSTRING); + GetModuleFileName(nullptr,ExecDirectory,MAX_PATH); + PathRemoveFileSpec(ExecDirectory); + strcpy(CurrentConfig.PathtoExe,ExecDirectory); + + // Make sure scanlines is off + LCState->ScanLines=0; + + // Get sound cards from audio.cpp + NumberOfSoundCards=GetSoundCardList(SoundCards); + + // Establish application data path + TCHAR szPath[MAX_PATH] {}; + std::string appData {}; + if (SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_APPDATA, nullptr, 0, szPath))) { + Util::FixDirSlashes(szPath); + appData = std::string(szPath) + "/VCC"; + } + Util::copy_to_char(appData,gcAppDataPath,MAX_PATH); + // Establish settings storage (ini file) path + std::string ini; if (*CmdArg.IniFile) { - GetFullPathNameA(CmdArg.IniFile,MAX_PATH,IniFilePath,nullptr); + char buf[MAX_PATH]; + if (GetFullPathNameA(CmdArg.IniFile, MAX_PATH, buf, nullptr)) + ini = buf; + else + ini = CmdArg.IniFile; // fallback + Util::FixDirSlashes(ini); } else { - strcpy(IniFilePath, AppDataPath); - strcat(IniFilePath, "\\"); - strcat(IniFilePath, IniFileName); + ini = appData + "/Vcc.ini"; } + Util::copy_to_char(ini, gcIniFilePath, MAX_PATH); + + // Load settings + ReadIniFile(); + + CurrentConfig.RebootNow=0; + UpdateConfig(); + + RefreshJoystickStatus(); + if (EmuState.WindowHandle != nullptr) InitSound(); } //--------------------------------------------------------------- -// Fatal if this is called before valid IniFilePath is established +// Fatal if this is called before valid gcIniFilePath is established //--------------------------------------------------------------- VCC::Util::settings& Setting() { if (!gpSettings) { // Fatal if ini file can not be opened - if (!Util::ValidateRWFile(IniFilePath)) { - std::string s = "Can't open settings " - + std::string(IniFilePath); + if (!Util::ValidateRWFile(gcIniFilePath)) { + std::string s = "Can't open settings " + + std::string(gcIniFilePath); MessageBox(EmuState.WindowHandle,s.c_str(),"Fatal",0); exit(0); } - gpSettings = new Util::settings(IniFilePath); + gpSettings = new Util::settings(gcIniFilePath); } return *gpSettings; } //--------------------------------------------------------------- -// Change the ini file path. (from Vcc File menu) +// Change the ini file path. (from Vcc File menu) //--------------------------------------------------------------- void SetIniFilePath(const char * path) { - DLOG_C("SetIniFilePath %s\n",path); + DLOG_C("SetgcIniFilePath %s\n",path); if (Util::ValidateRWFile(path)) { - strcpy(IniFilePath,path); + strcpy(gcIniFilePath,path); // Destroy settings object delete gpSettings; gpSettings = nullptr; @@ -241,7 +272,8 @@ void SetIniFilePath(const char * path) void GetIniFilePath( char *Path) { // FIXME convert to returning a string - strcpy(Path,IniFilePath); + strncpy(Path,gcIniFilePath,MAX_PATH); + DLOG_C("GetIniFilePath %s\n",Path); return; } @@ -254,40 +286,7 @@ void FlushSettings() } //--------------------------------------------------------------- -// Initial system config -//--------------------------------------------------------------- -void LoadConfig(SystemState *LCState) -{ - buildTransDisp2ScanTable(); - - // Establish application path - LoadString(nullptr, IDS_APP_TITLE,AppName, MAX_LOADSTRING); - GetModuleFileName(nullptr,ExecDirectory,MAX_PATH); - PathRemoveFileSpec(ExecDirectory); - strcpy(CurrentConfig.PathtoExe,ExecDirectory); - - // Make sure scanlines is off - LCState->ScanLines=0; - - // Get sound cards from audio.cpp - NumberOfSoundCards=GetSoundCardList(SoundCards); - - // Establish settings store - EstablishIniFile(); - - // Load settings from store - ReadIniFile(); - - CurrentConfig.RebootNow=0; - UpdateConfig(); - - RefreshJoystickStatus(); - if (EmuState.WindowHandle != nullptr) InitSound(); - -} - -//--------------------------------------------------------------- -// Set up sound cards +// Set up sound cards //--------------------------------------------------------------- void InitSound() { @@ -364,7 +363,7 @@ unsigned char ReadIniFile() Setting().read("Misc","CustomKeyMapFile","",KeyMapFilePath,MAX_PATH); if (*KeyMapFilePath == '\0') { - strcpy(KeyMapFilePath, AppDataPath); + strcpy(KeyMapFilePath, gcAppDataPath); strcat(KeyMapFilePath, "\\custom.keymap"); } if (CurrentConfig.KeyMap == kKBLayoutCustom) LoadCustomKeyMap(KeyMapFilePath); @@ -431,7 +430,7 @@ unsigned char WriteIniFile() // AppName is set in LoadConfig() Setting().write("Version","Release",AppName); - // CPU + // CPU Setting().write("CPU","DoubleSpeedClock",CurrentConfig.CPUMultiplyer); Setting().write("CPU","FrameSkip",CurrentConfig.FrameSkip); Setting().write("CPU","Throttle",CurrentConfig.SpeedThrottle); @@ -498,7 +497,7 @@ void LoadModule() } } -void SetWindowRect(const Rect& rect) +void SetWindowRect(const Rect& rect) { if (EmuState.WindowHandle != nullptr) { @@ -517,18 +516,20 @@ void SetWindowRect(const Rect& rect) } } -// The following two functions only work after LoadConfig has been called +// The following functions only work after LoadConfig has been called char * AppDirectory() { - return AppDataPath; + return gcAppDataPath; } + char * GetKeyMapFilePath() { return KeyMapFilePath; } + void SetKeyMapFilePath(const char *Path) { - strcpy(KeyMapFilePath,Path); + strcpy(KeyMapFilePath,Path); Setting().write("Misc","CustomKeyMapFile",KeyMapFilePath); } @@ -734,7 +735,7 @@ LRESULT CALLBACK CpuConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lPar void SetOverclock(unsigned char flag) { EmuState.OverclockFlag = flag; - if (hCpuDlg != nullptr) + if (hCpuDlg != nullptr) SendDlgItemMessage(hCpuDlg,IDC_OVERCLOCK,BM_SETCHECK,flag,0); } @@ -746,7 +747,7 @@ void IncreaseOverclockSpeed() CurrentConfig.CPUMultiplyer = (unsigned char)(CurrentConfig.CPUMultiplyer + 1); // Send updates to the dialog if it's open. - if (hCpuDlg != nullptr) { + if (hCpuDlg != nullptr) { SendDlgItemMessage(hCpuDlg, IDC_CLOCKSPEED, TBM_SETPOS, TRUE, CurrentConfig.CPUMultiplyer); sprintf(OutBuffer, "%2.3f Mhz", (float)CurrentConfig.CPUMultiplyer * 0.894); @@ -765,7 +766,7 @@ void DecreaseOverclockSpeed() CurrentConfig.CPUMultiplyer = (unsigned char)(CurrentConfig.CPUMultiplyer - 1); // Send updates to the dialog if it's open. - if (hCpuDlg != nullptr) { + if (hCpuDlg != nullptr) { SendDlgItemMessage(hCpuDlg, IDC_CLOCKSPEED, TBM_SETPOS, TRUE, CurrentConfig.CPUMultiplyer); sprintf(OutBuffer, "%2.3f Mhz", (float)CurrentConfig.CPUMultiplyer * 0.894); @@ -973,7 +974,7 @@ LRESULT CALLBACK AudioConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lP CurrentConfig.AudioRate = tmpcfg.AudioRate; CurrentConfig.SndOutDev = tmpcfg.SndOutDev; strcpy(CurrentConfig.SoundCardName, SoundCards[CurrentConfig.SndOutDev].CardName); - // FIXME Save mute button state (AudioRate) + // FIXME Save mute button state (AudioRate) WriteIniFile(); if (LOWORD(wParam)==IDOK) { @@ -1177,15 +1178,15 @@ void OpenInputConfig() { } LRESULT CALLBACK InputConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lParam*/) { - switch (message) { - case WM_INITDIALOG: - ShowKeymapStatus(hDlg); - break; - case WM_COMMAND: - switch(LOWORD(wParam)) { + switch (message) { + case WM_INITDIALOG: + ShowKeymapStatus(hDlg); + break; + case WM_COMMAND: + switch(LOWORD(wParam)) { case IDCANCEL: case IDCLOSE: - hInputDlg = nullptr; + hInputDlg = nullptr; DestroyWindow(hDlg); break; case IDOK: diff --git a/libcommon/include/vcc/util/fileutil.h b/libcommon/include/vcc/util/fileutil.h index 2618fb11..e8e8d538 100644 --- a/libcommon/include/vcc/util/fileutil.h +++ b/libcommon/include/vcc/util/fileutil.h @@ -35,22 +35,6 @@ namespace VCC::Util { // Get most recent windows error text LIBCOMMON_EXPORT std::string LastErrorString(); - const char * LastErrorTxt(); - - // Convert backslashes to slashes in directory string - LIBCOMMON_EXPORT void FixDirSlashes(std::string &dir); - - // Return copy of string with spaces trimmed from end of a string - LIBCOMMON_EXPORT std::string trim_right_spaces(const std::string &s); - - // Return slash normalized directory part of a path - LIBCOMMON_EXPORT std::string GetDirectoryPart(const std::string& input); - - // Return filename part of a path - LIBCOMMON_EXPORT std::string GetFileNamePart(const std::string& input); - - // Determine if path is a direcory - LIBCOMMON_EXPORT bool IsDirectory(const std::string& path); // Get path of loaded module or current application LIBCOMMON_EXPORT std::string get_module_path(HMODULE module_handle); @@ -58,16 +42,123 @@ namespace VCC::Util { // If path is in the application directory strip directory LIBCOMMON_EXPORT std::string strip_application_path(std::string path); + // Fully qualify a file based on execution directory + LIBCOMMON_EXPORT std::string QualifyPath(const std::string& path); + + //------------------------------------------------------------------------ + // In line functions + //------------------------------------------------------------------------ + + // Return copy of string with spaces trimmed from end of a string + inline std::string trim_right_spaces(const std::string& s) + { + size_t end = s.find_last_not_of(' '); + if (end == std::string::npos) return {}; + return s.substr(0, end + 1); + } + + // Return filename part of a path + inline std::string GetFileNamePart(const std::string& input) + { + std::filesystem::path p(input); + return p.filename().string(); + } + + // Determine if path is a direcory + inline bool IsDirectory(const std::string& path) + { + std::error_code ec; + return std::filesystem::is_directory(path, ec) && !ec; + } + // Verify that a file can be opened for read/write - LIBCOMMON_EXPORT bool ValidateRWFile(const std::string& path); + inline bool ValidateRWFile(const std::string& path) + { + HANDLE h = CreateFile + (path.c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, nullptr, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, nullptr); + if (h==INVALID_HANDLE_VALUE) return false; + CloseHandle(h); + return true; + } // Verify that a file can be opened for read - LIBCOMMON_EXPORT bool ValidateRDFile(const std::string& path); + inline bool ValidateRDFile(const std::string& path) + { + HANDLE h = CreateFile + (path.c_str(), GENERIC_READ, + FILE_SHARE_READ, nullptr, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, nullptr); + if (h==INVALID_HANDLE_VALUE) return false; + CloseHandle(h); + return true; + } - // Fully qualify a file based on execution directory - LIBCOMMON_EXPORT std::string QualifyPath(const std::string& path); + // Convert backslashes to slashes within string + inline void FixDirSlashes(std::string& dir) + { + if (dir.empty()) return; + std::replace(dir.begin(), dir.end(), '\\', '/'); + if (dir.back() == '/') dir.pop_back(); + } - // TODO: following stuff should go elsewhere + // Convert backslashes to slashes within char * + inline void FixDirSlashes(char* dir) + { + if (!dir || !*dir) return; + for (char* p = dir; *p; ++p) + if (*p == '\\') *p = '/'; + } + + // Convert slashes to backslashes within char * (for legacy win calls) + inline void RevDirSlashes(char* dir) + { + if (!dir || !*dir) return; + for (char* p = dir; *p; ++p) + if (*p == '/') *p = '\\'; + } + + // Return copy of path with backslashes converterd + inline std::string FixDirSlashes(const std::string& dir) + { + std::string s = dir; + FixDirSlashes(s); + return s; + } + + // Strip trailing backslash from directory or path + inline void StripTrailingSlash(std::string& dir) + { + if (dir.back() == '/') dir.pop_back(); + } + + // Return copy with trailing backslash stripped + inline std::string StripTrailingSlash(const std::string& dir) + { + std::string s = dir; + StripTrailingSlash(s); + return s; + } + + // Conditionally append trailing backslash to directory + inline void AppendTrailingSlash(std::string dir) + { + if (dir.back() != '/') dir += '/'; + } + + // Return slash normalized directory part of a path + inline std::string GetDirectoryPart(const std::string& input) + { + std::filesystem::path p(input); + std::string out = p.parent_path().string(); + FixDirSlashes(out); + return out; + } + + //------------------------------------------------------------------------ + // TODO: In line functions that should go elsewhere + //------------------------------------------------------------------------ // Return string with case conversion inline std::string to_lower(std::string s) { @@ -112,4 +203,12 @@ namespace VCC::Util { inline bool is_null_or_empty(const std::string& s) { return s.empty(); } + + inline void copy_to_char(const std::string& src, char* dst, size_t dst_size) + { + if (dst_size == 0) return; + const size_t n = std::min(src.size(), dst_size - 1); + memcpy(dst, src.data(), n); + dst[n] = '\0'; + } } diff --git a/libcommon/src/util/DialogOps.cpp b/libcommon/src/util/DialogOps.cpp index 46bcb3c1..7f3a9e2c 100644 --- a/libcommon/src/util/DialogOps.cpp +++ b/libcommon/src/util/DialogOps.cpp @@ -16,6 +16,8 @@ // VCC (Virtual Color Computer). If not, see . //////////////////////////////////////////////////////////////////////////////// #include +#include +#include #include #include //#include @@ -33,7 +35,7 @@ FileDialog::FileDialog() { // FileDialog::show calls GetOpenFileName() or GetSaveFileName() bool FileDialog::show(BOOL Save, HWND Owner) { - +PrintLogC("FileDialog::show %d %p\n",Save,Owner); // instance is that of the current module ofn_.hInstance = GetModuleHandle(nullptr); @@ -54,6 +56,9 @@ bool FileDialog::show(BOOL Save, HWND Owner) { } else { rc = GetOpenFileName(&ofn_) ; } + +PrintLogC("FileDialog::rc %d\n",rc); + return ((rc == 1) && (*path_ != '\0')); } @@ -81,27 +86,21 @@ void FileDialog::setTitle(const char * Title) { void FileDialog::setpath(const char * NewPath) { if (NewPath == nullptr) return; strncpy(path_,NewPath,MAX_PATH); + // GetSaveFileName can't deal with forward slashes here + VCC::Util::RevDirSlashes(path_); } // Get a copy of the selected file path void FileDialog::getpath(char * PathCopy, int maxsize) const { - if (PathCopy == nullptr || path_ == nullptr || maxsize < 1) return; + if (PathCopy == nullptr) return; strncpy(PathCopy,path_,maxsize); } -// Get a copy of the selected file path with unix dir delimiters +// Get a copy of the selected file path with slash delimiters void FileDialog::getupath(char * PathCopy, int maxsize) const { - if (PathCopy == nullptr || path_ == nullptr || maxsize < 1) return; - int i = 0; - while (path_[i] != '\0' && i < maxsize - 1) { - if (path_[i] == '\\') { - PathCopy[i] = '/'; - } else { - PathCopy[i] = path_[i]; - } - i++; - } - PathCopy[i] = '\0'; + if (PathCopy == nullptr) return; + strncpy(PathCopy,path_,maxsize); + VCC::Util::FixDirSlashes(PathCopy); } // Get a pointer to the selected file path @@ -112,19 +111,20 @@ const char *FileDialog::path() const // FileDialog::getdir() returns the directory portion of the file path void FileDialog::getdir(char * Dir, int maxsize) const { - if (Dir == nullptr || path_ == nullptr || maxsize < 1) return; - strncpy(Dir,path_,maxsize); - if (char * p = strrchr(Dir,'\\')) *p = '\0'; + if (Dir == nullptr) return; + std::string s = VCC::Util::GetDirectoryPart(path_); + VCC::Util::copy_to_char(s,Dir,maxsize); } // FileDialog::gettype() returns the file type void FileDialog::gettype(char * Type, int maxsize) const { - if (Type == nullptr || path_ == nullptr || maxsize < 1) return; - const char* dot = strrchr(path_, '.'); - if (!dot || strchr(dot, '/') || strchr(dot, '\\')) { - *Type = '\0'; - } - strncpy(Type,dot+1,maxsize); + if (Type == nullptr) return; + if (maxsize < 1) return; + *Type = '\0'; + std::string s = VCC::Util::GetFileNamePart(path_); + size_t pos = s.rfind('.'); + if (pos == std::string::npos || pos == s.size()-1) return; + VCC::Util::copy_to_char(s.substr(pos+1),Type,maxsize); } // String overloads for path_ and path_ components diff --git a/libcommon/src/util/fileutil.cpp b/libcommon/src/util/fileutil.cpp index c3d53f7a..f8b35143 100644 --- a/libcommon/src/util/fileutil.cpp +++ b/libcommon/src/util/fileutil.cpp @@ -56,76 +56,25 @@ namespace VCC::Util return buffer.c_str(); } - //TODO: This is generic, move to something for generic utils - //---------------------------------------------------------------------- - // Return copy of string with spaces trimmed from end of a string - //---------------------------------------------------------------------- - std::string trim_right_spaces(const std::string& s) - { - size_t end = s.find_last_not_of(' '); - if (end == std::string::npos) - return {}; - return s.substr(0, end + 1); - } - - //---------------------------------------------------------------------- - // Return slash normalized directory part of a path - //---------------------------------------------------------------------- - std::string GetDirectoryPart(const std::string& input) - { - std::filesystem::path p(input); - std::string out = p.parent_path().string(); - FixDirSlashes(out); - return out; - } - - //---------------------------------------------------------------------- - // Return filename part of a path - //---------------------------------------------------------------------- - std::string GetFileNamePart(const std::string& input) - { - std::filesystem::path p(input); - return p.filename().string(); - } - - //---------------------------------------------------------------------- - // Determine if path is a direcory - //---------------------------------------------------------------------- - bool IsDirectory(const std::string& path) - { - std::error_code ec; - return std::filesystem::is_directory(path, ec) && !ec; - } - - //------------------------------------------------------------------- - // Convert path directory backslashes to forward slashes - //------------------------------------------------------------------- - void FixDirSlashes(std::string &dir) - { - if (dir.empty()) return; - std::replace(dir.begin(), dir.end(), '\\', '/'); - if (dir.back() == '/') dir.pop_back(); - } - //------------------------------------------------------------------- // Get the file path of a loaded module // Current exe path is returned if module_handle is null //------------------------------------------------------------------- std::string get_module_path(HMODULE module_handle) { - std::string text(MAX_PATH, '\0'); - for (;;) { + std::string text(MAX_PATH, '\0'); + for (;;) { DWORD ret = GetModuleFileName(module_handle, &text[0], text.size()); - if (ret == 0) + if (ret == 0) return {}; // error - if (ret < text.size() - 1) { - text.resize(ret); - return text; - } - // truncated grow and retry - text.resize(text.size() * 2); - } + if (ret < text.size() - 1) { + text.resize(ret); + return text; + } + // truncated grow and retry + text.resize(text.size() * 2); + } } //------------------------------------------------------------------- @@ -142,40 +91,6 @@ namespace VCC::Util return path; } - //--------------------------------------------------------------- - // Verify that a file can be opened for read/write - //--------------------------------------------------------------- - bool ValidateRWFile(const std::string& path) - { - HANDLE h = CreateFile - (path.c_str(), GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ, nullptr, OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, nullptr); - - if (h==INVALID_HANDLE_VALUE) - return false; - - CloseHandle(h); - return true; - } - - //--------------------------------------------------------------- - // Verify that a file can be opened for read - //--------------------------------------------------------------- - bool ValidateRDFile(const std::string& path) - { - HANDLE h = CreateFile - (path.c_str(), GENERIC_READ, - FILE_SHARE_READ, nullptr, OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, nullptr); - - if (h==INVALID_HANDLE_VALUE) - return false; - - CloseHandle(h); - return true; - } - //--------------------------------------------------------------- // Fully qualify a file based on current execution directory //--------------------------------------------------------------- @@ -191,5 +106,4 @@ namespace VCC::Util std::string dir = Util::GetDirectoryPart(exe); return dir + '/' + mod; } - } From f041c158e3f4631417fd4a7a7a3fb3f2007d7de7 Mon Sep 17 00:00:00 2001 From: ejaquay Date: Tue, 20 Jan 2026 17:45:21 -0500 Subject: [PATCH 08/10] Show mouse pointer when VCC looses focus Mousepointer still shown when hide focus is set but VCC does not have keyboard focus. Dialog specific settings are stored only by the dialog that sets them Protect DialogOps from paths with forward slashes --- Cassette.cpp | 5 +- Vcc.cpp | 19 ++++-- config.cpp | 93 +++++++++++++------------- config.h | 1 - libcommon/include/vcc/util/DialogOps.h | 9 +++ libcommon/src/util/DialogOps.cpp | 30 +++++++-- 6 files changed, 95 insertions(+), 62 deletions(-) diff --git a/Cassette.cpp b/Cassette.cpp index 8ee6bde9..829e2868 100644 --- a/Cassette.cpp +++ b/Cassette.cpp @@ -23,6 +23,7 @@ This file is part of VCC (Virtual Color Computer). #include "coco3.h" #include "config.h" #include +#include #include "Cassette.h" #include "stdio.h" #include @@ -463,8 +464,10 @@ unsigned int LoadTape() } } dlg.getdir(CassPath); - if (strcmp(CassPath, "") != 0) + if (strcmp(CassPath, "") != 0) { + VCC::Util::FixDirSlashes(CassPath); Setting().write("DefaultPaths","CassPath",CassPath); + } // turn off fast load for wav files if (FileType == WAV) TapeFastLoad = false; TapeWritten = false; diff --git a/Vcc.cpp b/Vcc.cpp index fe2429f5..1585e005 100644 --- a/Vcc.cpp +++ b/Vcc.cpp @@ -135,6 +135,8 @@ bool IsShiftKeyDown(); CartridgeMenu CartMenu; +static bool gHasFocus {}; + //static CRITICAL_SECTION FrameRender; /*--------------------------------------------------------------------------*/ int APIENTRY WinMain(_In_ HINSTANCE hInstance, @@ -441,15 +443,20 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_SETCURSOR: // Hide mouse cursor if ((EmuState.MousePointer != 1) && (LOWORD(lParam) == HTCLIENT)) { - SetCursor(nullptr); + if (gHasFocus) SetCursor(nullptr); return TRUE; } break; + case WM_SETFOCUS: + gHasFocus = true; + break; + case WM_KILLFOCUS: // Force keys up if main widow keyboard focus is lost. Otherwise // down keys will cause issues with OS-9 on return raise_saved_keys(); + gHasFocus = false; break; case WM_CLOSE: @@ -916,7 +923,7 @@ unsigned char SetAutoStart(unsigned char Tmp) // LoadIniFile allows user to browse for an ini file and reloads the config from it. void LoadIniFile() { - FlushSettings(); + FlushSettings(); // Make sure all current settings are flused to store char curini[MAX_PATH]=""; GetIniFilePath(curini); @@ -930,10 +937,10 @@ void LoadIniFile() dlg.setpath(curini); if ( dlg.show() ) { - SetIniFilePath(dlg.path()); // Set new ini file path - ReadIniFile(); // FIXME ReadIniFile should apply changes - UpdateConfig(); // Applies only video and Cpu changes - EmuState.ResetPending = 2; + SetIniFilePath(dlg.path()); // Set new settings path + ReadIniFile(); // Load settings + UpdateConfig(); // Apply critical CPU and Video settings + EmuState.ResetPending = 2; // Force reset } return; } diff --git a/config.cpp b/config.cpp index fc429ef0..fadb1c0b 100644 --- a/config.cpp +++ b/config.cpp @@ -73,6 +73,12 @@ unsigned char TranslateScan2Disp(int); void buildTransDisp2ScanTable(); void SetBootModulePath(const std::string); +void WriteCPUSettings(); +void WriteAudioSettings(); +void WriteVideoSettings(); +void WriteKeyboardSettings(); +void WriteJoystickSettings(); + LRESULT CALLBACK CpuConfig(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK AudioConfig(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK DisplayConfig(HWND, UINT, WPARAM, LPARAM); @@ -133,7 +139,6 @@ static unsigned char NumberofJoysticks=0; TCHAR gcAppDataPath[MAX_PATH]; char OutBuffer[MAX_PATH]=""; -char AppName[MAX_LOADSTRING]=""; extern SystemState EmuState; extern char StickName[MAXSTICKS][STRLEN]; @@ -181,8 +186,7 @@ void LoadConfig(SystemState *LCState) { buildTransDisp2ScanTable(); - // Establish application path - LoadString(nullptr, IDS_APP_TITLE,AppName, MAX_LOADSTRING); + GetModuleFileName(nullptr,ExecDirectory,MAX_PATH); PathRemoveFileSpec(ExecDirectory); strcpy(CurrentConfig.PathtoExe,ExecDirectory); @@ -216,6 +220,11 @@ void LoadConfig(SystemState *LCState) } Util::copy_to_char(ini, gcIniFilePath, MAX_PATH); + // Establish application path + char AppName[MAX_LOADSTRING]=""; + LoadString(nullptr, IDS_APP_TITLE,AppName, MAX_LOADSTRING); + Setting().write("Version","Release",AppName); + // Load settings ReadIniFile(); @@ -341,6 +350,7 @@ unsigned char ReadIniFile() CurrentConfig.MonitorType = Setting().read("Video","MonitorType",1); CurrentConfig.PaletteType = Setting().read("Video","PaletteType",PALETTE_NTSC); CurrentConfig.ScanLines = Setting().read("Video","ScanLines",0); + CurrentConfig.FrameSkip = Setting().read("Video","FrameSkip",1); CurrentConfig.Aspect = Setting().read("Video","ForceAspect",1); CurrentConfig.RememberSize = Setting().read("Video","RememberSize",1); @@ -418,19 +428,9 @@ unsigned char ReadIniFile() } //--------------------------------------------------------------- -// Save All Configuration Settings to ini file +// Save Configuration Settings to ini file //--------------------------------------------------------------- - -// FIXME WriteIniFile() is a sledgehammer. -// Each config dialog should save the stuff it changes -unsigned char WriteIniFile() -{ - - // ModulePath is managed by pakinterface.cpp - // AppName is set in LoadConfig() - Setting().write("Version","Release",AppName); - - // CPU +void WriteCPUSettings() { Setting().write("CPU","DoubleSpeedClock",CurrentConfig.CPUMultiplyer); Setting().write("CPU","FrameSkip",CurrentConfig.FrameSkip); Setting().write("CPU","Throttle",CurrentConfig.SpeedThrottle); @@ -440,30 +440,31 @@ unsigned char WriteIniFile() Setting().write("Memory","RamSize",CurrentConfig.RamSize); Setting().write("Misc","AutoStart",CurrentConfig.AutoStart); Setting().write("Misc","CartAutoStart",CurrentConfig.CartAutoStart); - Setting().write("Misc","UseExtCocoRom", CurrentConfig.UseExtCocoRom); + Setting().write("Misc","UseExtCocoRom",CurrentConfig.UseExtCocoRom); Setting().write("Misc","Overclock", CurrentConfig.EnableOverclock); Setting().write("Misc","ExternalBasicImage", CurrentConfig.ExtRomFile); - - // Audio +} +void WriteAudioSettings() { Setting().write("Audio","SndCard",CurrentConfig.SoundCardName); Setting().write("Audio","Rate",CurrentConfig.AudioRate); - - // Video +} +void WriteVideoSettings() { Rect winRect = GetCurWindowSize(); Setting().write("Video","MonitorType",CurrentConfig.MonitorType); Setting().write("Video","PaletteType",CurrentConfig.PaletteType); Setting().write("Video","ScanLines",CurrentConfig.ScanLines); Setting().write("Video","ForceAspect",CurrentConfig.Aspect); - Setting().write("Video","RememberSize", CurrentConfig.RememberSize); - Setting().write("Video","WindowSizeX", winRect.w); - Setting().write("Video","WindowSizeY", winRect.h); - Setting().write("Video","WindowPosX", winRect.x); - Setting().write("Video","WindowPosY", winRect.y); - - // Keyboard + Setting().write("Video","RememberSize",CurrentConfig.RememberSize); + Setting().write("Video","FrameSkip",CurrentConfig.FrameSkip); + Setting().write("Video","WindowSizeX",winRect.w); + Setting().write("Video","WindowSizeY",winRect.h); + Setting().write("Video","WindowPosX",winRect.x); + Setting().write("Video","WindowPosY",winRect.y); +} +void WriteKeyboardSettings() { Setting().write("Misc","KeyMapIndex",CurrentConfig.KeyMap); - - // Joystick +} +void WriteJoystickSettings() { Setting().write("LeftJoyStick","UseMouse",LeftJS.UseMouse); Setting().write("LeftJoyStick","Left",LeftJS.Left); Setting().write("LeftJoyStick","Right",LeftJS.Right); @@ -483,10 +484,6 @@ unsigned char WriteIniFile() Setting().write("RightJoyStick","DiDevice",RightJS.DiDevice); Setting().write("RightJoyStick", "HiResDevice", RightJS.HiRes); Setting().write("Misc","ShowMousePointer",CurrentConfig.ShowMousePointer); - - // Force flush inifile Is this required? - Setting().flush(); - return 0; } void LoadModule() @@ -661,7 +658,7 @@ LRESULT CALLBACK CpuConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lPar SetOverclock(CurrentConfig.EnableOverclock); // FIXME should only save CPU stuff here - WriteIniFile(); + WriteCPUSettings(); // Exit dialog if IDOK if (LOWORD(wParam)==IDOK) { @@ -720,7 +717,7 @@ LRESULT CALLBACK CpuConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lPar dlg.setTitle("Coco3 Rom Image"); dlg.setFlags(OFN_FILEMUSTEXIST); if (dlg.show()) { - dlg.getpath(tmpcfg.ExtRomFile,MAX_PATH); + dlg.getupath(tmpcfg.ExtRomFile,MAX_PATH); SetDlgItemText(hDlg,IDC_ROMPATH,tmpcfg.ExtRomFile); } } @@ -974,8 +971,7 @@ LRESULT CALLBACK AudioConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lP CurrentConfig.AudioRate = tmpcfg.AudioRate; CurrentConfig.SndOutDev = tmpcfg.SndOutDev; strcpy(CurrentConfig.SoundCardName, SoundCards[CurrentConfig.SndOutDev].CardName); - // FIXME Save mute button state (AudioRate) - WriteIniFile(); + WriteAudioSettings(); if (LOWORD(wParam)==IDOK) { hAudioDlg = nullptr; @@ -1111,7 +1107,8 @@ LRESULT CALLBACK DisplayConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /* UpdateConfig(); // FIXME should only save display stuff here - WriteIniFile(); + WriteVideoSettings(); + if (LOWORD(wParam)==IDOK) { hDisplayDlg = nullptr; DestroyWindow(hDlg); @@ -1193,8 +1190,8 @@ LRESULT CALLBACK InputConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lP case IDAPPLY: vccKeyboardBuildRuntimeTable((keyboardlayout_e)CurrentConfig.KeyMap); UpdateConfig(); - // FIXME Should only save keyboard stuff here - WriteIniFile(); + WriteKeyboardSettings(); + if (LOWORD(wParam)==IDOK) { hInputDlg = nullptr; DestroyWindow(hDlg); @@ -1260,27 +1257,28 @@ BOOL SelectKeymapFile(HWND hDlg) FileDialog dlg; dlg.setFilter("Keymap Files\0*.keymap\0\0"); dlg.setInitialDir(AppDirectory()); + dlg.setInitialDir(gcAppDataPath); dlg.setTitle(TEXT("Select Keymap file")); dlg.setFlags(OFN_PATHMUSTEXIST); dlg.setDefExt(".keymap"); if ( dlg.show() ) { // Load keymap if file exists - DWORD attr = GetFileAttributesA(dlg.path()); + DWORD attr = GetFileAttributesA(dlg.upath()); if ( (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_DIRECTORY) ) { - LoadCustomKeyMap(dlg.path()); + LoadCustomKeyMap(dlg.upath()); // Else create new file from current selection } else { char txt[MAX_PATH+32]; strcpy (txt,"Create "); - strcat (txt,dlg.path()); + strcat (txt,dlg.upath()); strcat (txt,"?"); if (MessageBox(hDlg,txt,"Warning",MB_YESNO)==IDYES) { CloneStandardKeymap(CurrentConfig.KeyMap); - SaveCustomKeyMap(dlg.path()); + SaveCustomKeyMap(dlg.upath()); } } - dlg.getpath(KeyMapFilePath,MAX_PATH); + dlg.getupath(KeyMapFilePath,MAX_PATH); SetKeyMapFilePath(KeyMapFilePath); // Save filename in Vcc.config } return TRUE; @@ -1431,8 +1429,8 @@ LRESULT CALLBACK JoyStickConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM / SetStickNumbers(LeftJS.DiDevice,RightJS.DiDevice); CurrentConfig.ShowMousePointer = tmpcfg.ShowMousePointer; UpdateConfig(); - // FIXME Should only save joystick stuff here - WriteIniFile(); + WriteJoystickSettings(); + if (LOWORD(wParam)==IDOK) { hJoyStickDlg = nullptr; DestroyWindow(hDlg); @@ -1638,7 +1636,6 @@ LRESULT CALLBACK BitBanger(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lPar case IDAPPLY: UpdateConfig(); // FIXME Should save bitbanger here - WriteIniFile(); if (LOWORD(wParam)==IDOK) { hBitBangerDlg = nullptr; DestroyWindow(hDlg); diff --git a/config.h b/config.h index 6ea99ae5..3cd02a98 100644 --- a/config.h +++ b/config.h @@ -27,7 +27,6 @@ void FlushSettings(); void LoadConfig(SystemState *); void InitSound(); void LoadModule(); -unsigned char WriteIniFile(); unsigned char ReadIniFile(); VCC::Util::settings& Setting(); // New way to manage settings diff --git a/libcommon/include/vcc/util/DialogOps.h b/libcommon/include/vcc/util/DialogOps.h index d58c5940..fe8ca942 100644 --- a/libcommon/include/vcc/util/DialogOps.h +++ b/libcommon/include/vcc/util/DialogOps.h @@ -43,6 +43,12 @@ LIBCOMMON_EXPORT void CenterDialog(HWND hDlg); // getdir() returns a copy of the directory portion of "Path" // getupath() gets a copy of "Path" with all '\' chars replaced by '/' // +// upath_ is a copy of path_ with backslashes converted to slashes +// +// There is an effort to standardize VCC using slashes as directory seperators +// but GetSaveFileName(&ofn_) and GetOpenFileName are older windows functions +// that do not play well with slashes so there is a bit of converting in and out; +// //------------------------------------------------------------------------------------------- #include #include @@ -69,10 +75,13 @@ class LIBCOMMON_EXPORT FileDialog std::string getpath(); const char *path() const; + const char *upath() const; private: OPENFILENAME ofn_; char path_[MAX_PATH] = {}; + char upath_[MAX_PATH] = {}; + char initialdir_[MAX_PATH] = {}; }; diff --git a/libcommon/src/util/DialogOps.cpp b/libcommon/src/util/DialogOps.cpp index 7f3a9e2c..9649591a 100644 --- a/libcommon/src/util/DialogOps.cpp +++ b/libcommon/src/util/DialogOps.cpp @@ -24,6 +24,14 @@ //------------------------------------------------------------------------------------------- // FileDialog class shows a dialog for user to select a file for open or save. +// +// There is an effort to standardize VCC using slashes as directory seperators +// but GetSaveFileName() and GetOpenFileName() are old windows functions that +// do not play well with slashes so there is a bit of converting in and out; +// +// "path" refers to reverse slash path +// "upath" refers to forward slash path +// //------------------------------------------------------------------------------------------- // FileDialog constructor initializes open file name structure. @@ -35,7 +43,6 @@ FileDialog::FileDialog() { // FileDialog::show calls GetOpenFileName() or GetSaveFileName() bool FileDialog::show(BOOL Save, HWND Owner) { -PrintLogC("FileDialog::show %d %p\n",Save,Owner); // instance is that of the current module ofn_.hInstance = GetModuleHandle(nullptr); @@ -57,7 +64,11 @@ PrintLogC("FileDialog::show %d %p\n",Save,Owner); rc = GetOpenFileName(&ofn_) ; } -PrintLogC("FileDialog::rc %d\n",rc); + // upath_ is the forward slash version + if (rc == 1) { + strncpy (upath_,path_,MAX_PATH); + VCC::Util::FixDirSlashes(upath_); + } return ((rc == 1) && (*path_ != '\0')); } @@ -67,7 +78,10 @@ void FileDialog::setDefExt(const char * DefExt) { } void FileDialog::setInitialDir(const char * InitialDir) { - ofn_.lpstrInitialDir = InitialDir; + if (InitialDir == nullptr) return; + strncpy(initialdir_,InitialDir,MAX_PATH); + VCC::Util::RevDirSlashes(initialdir_); + ofn_.lpstrInitialDir = initialdir_; } void FileDialog::setFilter(const char * Filter) { @@ -86,8 +100,9 @@ void FileDialog::setTitle(const char * Title) { void FileDialog::setpath(const char * NewPath) { if (NewPath == nullptr) return; strncpy(path_,NewPath,MAX_PATH); - // GetSaveFileName can't deal with forward slashes here VCC::Util::RevDirSlashes(path_); + strncpy(upath_,NewPath,MAX_PATH); + VCC::Util::FixDirSlashes(upath_); } // Get a copy of the selected file path @@ -99,8 +114,7 @@ void FileDialog::getpath(char * PathCopy, int maxsize) const { // Get a copy of the selected file path with slash delimiters void FileDialog::getupath(char * PathCopy, int maxsize) const { if (PathCopy == nullptr) return; - strncpy(PathCopy,path_,maxsize); - VCC::Util::FixDirSlashes(PathCopy); + strncpy(PathCopy,upath_,maxsize); } // Get a pointer to the selected file path @@ -108,6 +122,10 @@ const char *FileDialog::path() const { return path_; } +const char *FileDialog::upath() const +{ + return upath_; +} // FileDialog::getdir() returns the directory portion of the file path void FileDialog::getdir(char * Dir, int maxsize) const { From 18d71dcdbae81698420611cbb2882a9bec8b684f Mon Sep 17 00:00:00 2001 From: ejaquay Date: Tue, 20 Jan 2026 22:48:34 -0500 Subject: [PATCH 09/10] Fix missing def in fileutil.h --- libcommon/include/vcc/util/fileutil.h | 1 + sdc/sdc.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libcommon/include/vcc/util/fileutil.h b/libcommon/include/vcc/util/fileutil.h index e8e8d538..5ae1c1fc 100644 --- a/libcommon/include/vcc/util/fileutil.h +++ b/libcommon/include/vcc/util/fileutil.h @@ -35,6 +35,7 @@ namespace VCC::Util { // Get most recent windows error text LIBCOMMON_EXPORT std::string LastErrorString(); + LIBCOMMON_EXPORT const char* LastErrorTxt(); // Get path of loaded module or current application LIBCOMMON_EXPORT std::string get_module_path(HMODULE module_handle); diff --git a/sdc/sdc.cpp b/sdc/sdc.cpp index d96ce689..2454090b 100644 --- a/sdc/sdc.cpp +++ b/sdc/sdc.cpp @@ -490,7 +490,6 @@ SDC_Configure(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lParam*/) if (*tmp != '\0') { gCurDir = tmp; util::FixDirSlashes(gCurDir); - //strncpy(SDCard,tmp,MAX_PATH); } } return TRUE; @@ -561,7 +560,7 @@ void LoadConfig() for (int i=0;i<8;i++) { std::string tmp = "FlashFile_" + std::to_string(i); - settings.read("SDC", tmp, "", FlashFile[i], MAX_PATH);//, IniFile); + settings.read("SDC", tmp, "", FlashFile[i], MAX_PATH); } ClockEnable = settings.read("SDC","ClockEnable",1); From 4b62f44a3f956475ae7a17831343f3caf6957a2a Mon Sep 17 00:00:00 2001 From: ejaquay Date: Wed, 21 Jan 2026 11:30:09 -0500 Subject: [PATCH 10/10] refactor dialogops --- libcommon/include/vcc/util/DialogOps.h | 150 +++++++++++++++++++------ libcommon/include/vcc/util/fileutil.h | 30 +++-- libcommon/src/util/DialogOps.cpp | 128 ++++----------------- 3 files changed, 156 insertions(+), 152 deletions(-) diff --git a/libcommon/include/vcc/util/DialogOps.h b/libcommon/include/vcc/util/DialogOps.h index fe8ca942..1b2b4eee 100644 --- a/libcommon/include/vcc/util/DialogOps.h +++ b/libcommon/include/vcc/util/DialogOps.h @@ -17,6 +17,8 @@ //////////////////////////////////////////////////////////////////////////////// #pragma once #include // defines LIBCOMMON_EXPORT if libcommon is a DLL +#include +#include #include //------------------------------------------------------------------------------------------- @@ -33,22 +35,6 @@ LIBCOMMON_EXPORT void CenterDialog(HWND hDlg); // otherwise the open dialog is shown. If "Owner" is NULL GetActiveWindow() is used. // The selected filename is placed in "Path" // -// setpath() sets "Path" before calling show(). -// -// setDefExt(), setInitialDir(), setFilter(), setFlags(), and setTitle() set the -// elements in the OPENFILENAME structure that is used by get save/open file() -// -// path() returns a pointer to Path. -// getpath gets a copy of "Path". -// getdir() returns a copy of the directory portion of "Path" -// getupath() gets a copy of "Path" with all '\' chars replaced by '/' -// -// upath_ is a copy of path_ with backslashes converted to slashes -// -// There is an effort to standardize VCC using slashes as directory seperators -// but GetSaveFileName(&ofn_) and GetOpenFileName are older windows functions -// that do not play well with slashes so there is a bit of converting in and out; -// //------------------------------------------------------------------------------------------- #include #include @@ -57,31 +43,123 @@ class LIBCOMMON_EXPORT FileDialog { public: - FileDialog(); + FileDialog() { + ZeroMemory(&ofn_, sizeof(ofn_)); + ofn_.lStructSize = sizeof(ofn_); + flags_ = OFN_HIDEREADONLY; + } + + void init() { + ZeroMemory(&ofn_, sizeof(ofn_)); + ofn_.lStructSize = sizeof(ofn_); + flags_ = OFN_HIDEREADONLY; + } bool show(BOOL Save = FALSE, HWND Owner = nullptr); - void setpath(const char * Path); - void setDefExt(const char * DefExt); - void setInitialDir(const char * InitialDir); - void setFilter(const char * Filter); - void setFlags(unsigned int Flags); - void setTitle(const char * Title); - void getdir(char * Dir, int maxsize = MAX_PATH) const; - void getpath(char * Path, int maxsize = MAX_PATH) const; - void getupath(char * Path, int maxsize = MAX_PATH) const; - void gettype(char * Type, int maxsize = 4) const; - std::string getdir(); - std::string gettype(); - std::string getpath(); - - const char *path() const; - const char *upath() const; + + void setDefExt(const char * DefExt) + { + sDefext_.assign(DefExt ? DefExt : ""); + } + + void setInitialDir(const char * InitialDir) + { + sInitDir_.assign(InitialDir ? InitialDir : ""); + } + + //Set null terminated Filter items + void setFilter(const char* Filter) + { + sFilter_.clear(); + if (!Filter) return; + const char* p = Filter; + while (*p) p += std::strlen(p) + 1; + sFilter_.assign(Filter, p + 1 - Filter); + } + + void setFlags(unsigned int Flags) + { + flags_ = Flags | OFN_HIDEREADONLY; + } + + void setTitle(const char * Title) + { + sTitle_.assign(Title ? Title : ""); + } + + void setpath(const char * File) + { + sFile_.assign(File ? File : ""); + } + + // Return selected file + std::string getpath() const + { + return sFile_; + } + + // Return selected directory + std::string getdir() const + { + return VCC::Util::GetDirectoryPart(sFile_); + } + + // Return selected filetype + std::string gettype() const + { + std::string s = VCC::Util::GetFileNamePart(sFile_); + size_t pos = s.rfind('.'); + if (pos == std::string::npos || pos == s.size()-1) return {}; + return s.substr(pos+1); + } + + // Get a copy of the selected file path + void getpath(char * PathCopy, int maxsize=MAX_PATH) const + { + if (PathCopy == nullptr) return; + strncpy(PathCopy,sFile_.c_str(),maxsize); + } + + // Copy of the selected file path + void getupath(char * PathCopy, int maxsize=MAX_PATH) const + { + if (PathCopy == nullptr) return; + strncpy(PathCopy,sFile_.c_str(),maxsize); + } + + // copy the directory to c string + void getdir(char * Dir, int maxsize=MAX_PATH) const + { + if (Dir == nullptr) return; + VCC::Util::copy_to_char(getdir(),Dir,maxsize); + } + + // copy the file type to c string + void gettype(char * Type, int maxsize=16) const + { + if (Type == nullptr) return; + VCC::Util::copy_to_char(gettype(),Type,maxsize); + } + + // Get a pointer to the selected file path + const char *path() const + { + return sFile_.c_str(); + } + + const char *upath() const + { + return sFile_.c_str(); + } private: OPENFILENAME ofn_; - char path_[MAX_PATH] = {}; - char upath_[MAX_PATH] = {}; - char initialdir_[MAX_PATH] = {}; + std::string sFile_ {}; + std::string sInitDir_ {}; + std::string sTitle_ {}; + std::string sFilter_ {}; + std::string sDefext_ {}; + DWORD flags_; }; diff --git a/libcommon/include/vcc/util/fileutil.h b/libcommon/include/vcc/util/fileutil.h index 5ae1c1fc..c4bb74f4 100644 --- a/libcommon/include/vcc/util/fileutil.h +++ b/libcommon/include/vcc/util/fileutil.h @@ -161,23 +161,29 @@ namespace VCC::Util { // TODO: In line functions that should go elsewhere //------------------------------------------------------------------------ - // Return string with case conversion + // Return string with case conversion + inline std::string to_lower(std::string s) { - std::transform(s.begin(), s.end(), s.begin(), - [](unsigned char c){ return std::tolower(c); }); - return s; - } + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { + return static_cast(std::tolower(c)); + }); + return s; + } inline std::string to_upper(std::string s) { std::transform(s.begin(), s.end(), s.begin(), - [](unsigned char c){ return std::toupper(c); }); + [](unsigned char c) { + return static_cast(std::toupper(c)); + }); return s; } - // Convert case of string inplace inline void make_lower(std::string& s) { - std::transform(s.begin(), s.end(), s.begin(), - [](unsigned char c){ return std::tolower(c); }); + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { + return static_cast(std::tolower(c)); + }); } inline void make_lower(char* s) { @@ -187,8 +193,10 @@ namespace VCC::Util { } inline void make_upper(std::string& s) { - std::transform(s.begin(), s.end(), s.begin(), - [](unsigned char c){ return std::toupper(c); }); + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { + return static_cast(std::toupper(c)); + }); } inline void make_upper(char* s) { diff --git a/libcommon/src/util/DialogOps.cpp b/libcommon/src/util/DialogOps.cpp index 9649591a..96510885 100644 --- a/libcommon/src/util/DialogOps.cpp +++ b/libcommon/src/util/DialogOps.cpp @@ -20,7 +20,6 @@ #include #include #include -//#include //------------------------------------------------------------------------------------------- // FileDialog class shows a dialog for user to select a file for open or save. @@ -30,19 +29,23 @@ // do not play well with slashes so there is a bit of converting in and out; // // "path" refers to reverse slash path -// "upath" refers to forward slash path // //------------------------------------------------------------------------------------------- -// FileDialog constructor initializes open file name structure. -FileDialog::FileDialog() { - ZeroMemory(&ofn_, sizeof(ofn_)); - ofn_.lStructSize = sizeof(ofn_); - ofn_.Flags = OFN_HIDEREADONLY; -} - // FileDialog::show calls GetOpenFileName() or GetSaveFileName() bool FileDialog::show(BOOL Save, HWND Owner) { + + char file[MAX_PATH]; + ::VCC::Util::copy_to_char(sFile_,file,sizeof(file)); + ::VCC::Util::RevDirSlashes(file); + ofn_.nMaxFile = sizeof(file); + ofn_.lpstrFile = file; + + char idir[MAX_PATH]; + ::VCC::Util::copy_to_char(sInitDir_,idir,sizeof(idir)); + ::VCC::Util::RevDirSlashes(idir); + ofn_.lpstrInitialDir = idir; + // instance is that of the current module ofn_.hInstance = GetModuleHandle(nullptr); @@ -53,8 +56,10 @@ bool FileDialog::show(BOOL Save, HWND Owner) { ofn_.hwndOwner = GetActiveWindow(); } - ofn_.nMaxFile = sizeof(path_); - ofn_.lpstrFile = path_; + ofn_.lpstrDefExt = sDefext_.c_str(); + ofn_.lpstrFilter = sFilter_.c_str(); + ofn_.lpstrTitle = sTitle_.c_str(); + ofn_.Flags |= flags_; // Call Save or Open per boolean int rc; @@ -64,101 +69,14 @@ bool FileDialog::show(BOOL Save, HWND Owner) { rc = GetOpenFileName(&ofn_) ; } - // upath_ is the forward slash version - if (rc == 1) { - strncpy (upath_,path_,MAX_PATH); - VCC::Util::FixDirSlashes(upath_); - } - - return ((rc == 1) && (*path_ != '\0')); -} - -void FileDialog::setDefExt(const char * DefExt) { - ofn_.lpstrDefExt = DefExt; -} - -void FileDialog::setInitialDir(const char * InitialDir) { - if (InitialDir == nullptr) return; - strncpy(initialdir_,InitialDir,MAX_PATH); - VCC::Util::RevDirSlashes(initialdir_); - ofn_.lpstrInitialDir = initialdir_; -} - -void FileDialog::setFilter(const char * Filter) { - ofn_.lpstrFilter = Filter; -} - -void FileDialog::setFlags(unsigned int Flags) { - ofn_.Flags |= Flags; -} - -void FileDialog::setTitle(const char * Title) { - ofn_.lpstrTitle = Title; -} - -// Overwrite what is currently in path_ -void FileDialog::setpath(const char * NewPath) { - if (NewPath == nullptr) return; - strncpy(path_,NewPath,MAX_PATH); - VCC::Util::RevDirSlashes(path_); - strncpy(upath_,NewPath,MAX_PATH); - VCC::Util::FixDirSlashes(upath_); -} - -// Get a copy of the selected file path -void FileDialog::getpath(char * PathCopy, int maxsize) const { - if (PathCopy == nullptr) return; - strncpy(PathCopy,path_,maxsize); -} - -// Get a copy of the selected file path with slash delimiters -void FileDialog::getupath(char * PathCopy, int maxsize) const { - if (PathCopy == nullptr) return; - strncpy(PathCopy,upath_,maxsize); -} - -// Get a pointer to the selected file path -const char *FileDialog::path() const -{ - return path_; -} -const char *FileDialog::upath() const -{ - return upath_; -} - -// FileDialog::getdir() returns the directory portion of the file path -void FileDialog::getdir(char * Dir, int maxsize) const { - if (Dir == nullptr) return; - std::string s = VCC::Util::GetDirectoryPart(path_); - VCC::Util::copy_to_char(s,Dir,maxsize); -} + // Copy file selected back + if ((rc == 1) && (*file != '\0')) { + sFile_.assign(file ? file : ""); + VCC::Util::FixDirSlashes(sFile_); + return true; + }; -// FileDialog::gettype() returns the file type -void FileDialog::gettype(char * Type, int maxsize) const { - if (Type == nullptr) return; - if (maxsize < 1) return; - *Type = '\0'; - std::string s = VCC::Util::GetFileNamePart(path_); - size_t pos = s.rfind('.'); - if (pos == std::string::npos || pos == s.size()-1) return; - VCC::Util::copy_to_char(s.substr(pos+1),Type,maxsize); -} - -// String overloads for path_ and path_ components -std::string FileDialog::getpath() { - return path_; -} -std::string FileDialog::getdir() { - if (path_ == nullptr) return ""; - std::filesystem::path p(path_); - return p.parent_path().string(); - -} -std::string FileDialog::gettype() { - if (path_ == nullptr) return ""; - std::filesystem::path p(path_); - return p.extension().string(); + return false; } //------------------------------------------------------------