diff --git a/Cassette.cpp b/Cassette.cpp index bdaed93a..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 @@ -450,9 +451,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"); @@ -465,8 +464,10 @@ unsigned int LoadTape() } } dlg.getdir(CassPath); - if (strcmp(CassPath, "") != 0) - WritePrivateProfileString("DefaultPaths","CassPath",CassPath,IniFilePath); + 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/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..1585e005 100644 --- a/Vcc.cpp +++ b/Vcc.cpp @@ -68,6 +68,7 @@ #include "CommandLine.h" #include #include +#include #include "memdump.h" #include "MemoryMap.h" @@ -81,7 +82,7 @@ #include "IDisplayDebug.h" #endif -using namespace VCC; +using namespace ::VCC; // namespace is everything VCC static HANDLE hout=nullptr; @@ -134,6 +135,8 @@ bool IsShiftKeyDown(); CartridgeMenu CartMenu; +static bool gHasFocus {}; + //static CRITICAL_SECTION FrameRender; /*--------------------------------------------------------------------------*/ int APIENTRY WinMain(_In_ HINSTANCE hInstance, @@ -210,8 +213,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 +260,6 @@ int APIENTRY WinMain(_In_ HINSTANCE hInstance, CloseScreen(); timeEndPeriod(1); UnloadDll(); - WriteIniFile(); //Save Any changes to ini File return Msg.wParam; } @@ -439,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: @@ -914,24 +923,24 @@ unsigned char SetAutoStart(unsigned char Tmp) // LoadIniFile allows user to browse for an ini file and reloads the config from it. void LoadIniFile() { + FlushSettings(); // Make sure all current settings are flused to store + + 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() ) { - WriteIniFile(); // Flush current profile - SetIniFilePath(dlg.path()); // Set new ini file path - ReadIniFile(); // Load it - UpdateConfig(); - 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; } @@ -939,6 +948,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,13 +961,10 @@ 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) ) { 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 +1021,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 +1032,7 @@ unsigned __stdcall EmuLoop(HANDLE hEvent) break; case 4: - UpdateConfig(); + UpdateConfig(); // Apply video and cpy changes DoCls(&EmuState); break; 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/config.cpp b/config.cpp index 5943633d..fadb1c0b 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 @@ -24,9 +26,11 @@ This file is part of VCC (Virtual Color Computer). #include #include #include +//#include #include #include #include +#include #pragma warning(push) #pragma warning(disable:4091) @@ -35,7 +39,6 @@ This file is part of VCC (Virtual Color Computer). #include "defines.h" #include "resource.h" -#include "config.h" #include "tcc1014graphics.h" #include "mc6821.h" #include "Vcc.h" @@ -51,6 +54,10 @@ This file is part of VCC (Virtual Color Computer). #include "Cassette.h" #include "CommandLine.h" #include +#include +#include + +#include "config.h" using namespace std; using namespace VCC; @@ -64,6 +71,13 @@ void RefreshJoystickStatus(); unsigned char TranslateDisp2Scan(int); 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); @@ -116,17 +130,15 @@ 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]=""; extern SystemState EmuState; extern char StickName[MAXSTICKS][STRLEN]; @@ -160,205 +172,238 @@ 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 */ /***********************************************************/ + +//--------------------------------------------------------------- +// Initial config. This should be called only once when VCC starts up +//--------------------------------------------------------------- void LoadConfig(SystemState *LCState) { - HANDLE hr=nullptr; - int lasterror; - buildTransDisp2ScanTable(); - 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"); + // Make sure scanlines is off + LCState->ScanLines=0; + + // Get sound cards from audio.cpp + NumberOfSoundCards=GetSoundCardList(SoundCards); - if (_mkdir(AppDataPath) != 0) { - OutputDebugString("Unable to create VCC config folder."); + // 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); - LCState->ScanLines=0; - NumberOfSoundCards=GetSoundCardList(SoundCards); + // Establish application path + char AppName[MAX_LOADSTRING]=""; + LoadString(nullptr, IDS_APP_TITLE,AppName, MAX_LOADSTRING); + Setting().write("Version","Release",AppName); + + // Load settings ReadIniFile(); + CurrentConfig.RebootNow=0; UpdateConfig(); + RefreshJoystickStatus(); - if (EmuState.WindowHandle != nullptr) - InitSound(); - -// 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(); +} + +//--------------------------------------------------------------- +// 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(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(gcIniFilePath); } + return *gpSettings; +} + +//--------------------------------------------------------------- +// Change the ini file path. (from Vcc File menu) +//--------------------------------------------------------------- +void SetIniFilePath(const char * path) +{ + DLOG_C("SetgcIniFilePath %s\n",path); + if (Util::ValidateRWFile(path)) { + strcpy(gcIniFilePath,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 + strncpy(Path,gcIniFilePath,MAX_PATH); + DLOG_C("GetIniFilePath %s\n",Path); + return; +} + +//--------------------------------------------------------------- +// Flush the settings store +//--------------------------------------------------------------- +void FlushSettings() +{ + Setting().flush(); } +//--------------------------------------------------------------- +// Set up sound cards +//--------------------------------------------------------------- void InitSound() { SoundInit(EmuState.WindowHandle, SoundCards[CurrentConfig.SndOutDev].Guid, CurrentConfig.AudioRate); } -/***********************************************************/ -/* Save Configuration Settings to ini file */ -/***********************************************************/ -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) { - Rect winRect = GetCurWindowSize(); - GetCurrentModule(CurrentConfig.ModulePath); - ValidatePath(CurrentConfig.ModulePath); - - WritePrivateProfileString("Version","Release",AppName,IniFilePath); - 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); - - WritePrivateProfileString("Audio","SndCard",CurrentConfig.SoundCardName,IniFilePath); - WritePrivateProfileInt("Audio","Rate",CurrentConfig.AudioRate,IniFilePath); - - 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("Memory","RamSize",CurrentConfig.RamSize,IniFilePath); - - WritePrivateProfileInt("Misc","AutoStart",CurrentConfig.AutoStart,IniFilePath); - WritePrivateProfileInt("Misc","CartAutoStart",CurrentConfig.CartAutoStart,IniFilePath); - WritePrivateProfileInt("Misc","ShowMousePointer",CurrentConfig.ShowMousePointer,IniFilePath); - 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); - - 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); - - // Force flush inifile - WritePrivateProfileString(nullptr,nullptr,nullptr,IniFilePath); - 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 +//--------------------------------------------------------------- + unsigned char ReadIniFile() { 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.FrameSkip = Setting().read("Video","FrameSkip",1); + + 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 - GetPrivateProfileString("Misc","CustomKeyMapFile","",KeyMapFilePath,MAX_PATH,IniFilePath); + 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); vccKeyboardBuildRuntimeTable((keyboardlayout_e)CurrentConfig.KeyMap); - 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); + // 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++) { @@ -371,10 +416,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}; @@ -382,6 +427,65 @@ unsigned char ReadIniFile() return 0; } +//--------------------------------------------------------------- +// Save Configuration Settings to ini file +//--------------------------------------------------------------- +void WriteCPUSettings() { + 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); +} +void WriteAudioSettings() { + Setting().write("Audio","SndCard",CurrentConfig.SoundCardName); + Setting().write("Audio","Rate",CurrentConfig.AudioRate); +} +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","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); +} +void WriteJoystickSettings() { + 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); +} + void LoadModule() { if (CurrentConfig.ModulePath[0]) @@ -390,7 +494,7 @@ void LoadModule() } } -void SetWindowRect(const Rect& rect) +void SetWindowRect(const Rect& rect) { if (EmuState.WindowHandle != nullptr) { @@ -409,62 +513,52 @@ 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 +// 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); - WritePrivateProfileString("Misc","CustomKeyMapFile",KeyMapFilePath,IniFilePath); + strcpy(KeyMapFilePath,Path); + Setting().write("Misc","CustomKeyMapFile",KeyMapFilePath); } -/*****************************************************/ -/* 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 +630,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 +656,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 + WriteCPUSettings(); + // Exit dialog if IDOK if (LOWORD(wParam)==IDOK) { hCpuDlg = nullptr; @@ -616,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); } } @@ -631,7 +732,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); } @@ -643,7 +744,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); @@ -651,6 +752,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 */ @@ -661,14 +763,15 @@ 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); 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 +824,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 +971,8 @@ 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(); + WriteAudioSettings(); + if (LOWORD(wParam)==IDOK) { hAudioDlg = nullptr; DestroyWindow(hDlg); @@ -1000,6 +1105,10 @@ 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 + WriteVideoSettings(); + if (LOWORD(wParam)==IDOK) { hDisplayDlg = nullptr; DestroyWindow(hDlg); @@ -1066,21 +1175,23 @@ 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: case IDAPPLY: vccKeyboardBuildRuntimeTable((keyboardlayout_e)CurrentConfig.KeyMap); UpdateConfig(); + WriteKeyboardSettings(); + if (LOWORD(wParam)==IDOK) { hInputDlg = nullptr; DestroyWindow(hDlg); @@ -1146,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; @@ -1317,6 +1429,8 @@ LRESULT CALLBACK JoyStickConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM / SetStickNumbers(LeftJS.DiDevice,RightJS.DiDevice); CurrentConfig.ShowMousePointer = tmpcfg.ShowMousePointer; UpdateConfig(); + WriteJoystickSettings(); + if (LOWORD(wParam)==IDOK) { hJoyStickDlg = nullptr; DestroyWindow(hDlg); @@ -1521,6 +1635,7 @@ LRESULT CALLBACK BitBanger(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lPar case IDOK: case IDAPPLY: UpdateConfig(); + // FIXME Should save bitbanger here if (LOWORD(wParam)==IDOK) { hBitBangerDlg = nullptr; DestroyWindow(hDlg); @@ -1572,8 +1687,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"); @@ -1587,8 +1701,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 1044a6e0..3cd02a98 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); void UpdateTapeCounter(unsigned int,unsigned char,bool force = false); diff --git a/libcommon/include/vcc/util/DialogOps.h b/libcommon/include/vcc/util/DialogOps.h index d58c5940..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,16 +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 '/' -// //------------------------------------------------------------------------------------------- #include #include @@ -51,28 +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; + + 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] = {}; + 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 eeb5075b..c4bb74f4 100644 --- a/libcommon/include/vcc/util/fileutil.h +++ b/libcommon/include/vcc/util/fileutil.h @@ -35,26 +35,189 @@ namespace VCC::Util { // Get most recent windows error text LIBCOMMON_EXPORT std::string LastErrorString(); - const char * LastErrorTxt(); + LIBCOMMON_EXPORT const char* LastErrorTxt(); - // Convert backslashes to slashes in directory string - LIBCOMMON_EXPORT void FixDirSlashes(std::string &dir); + // Get path of loaded module or current application + LIBCOMMON_EXPORT std::string get_module_path(HMODULE module_handle); + + // 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 - LIBCOMMON_EXPORT std::string trim_right_spaces(const std::string &s); + 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 + 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 + 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; + } + + // 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(); + } + + // 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 - LIBCOMMON_EXPORT std::string GetDirectoryPart(const std::string& input); + inline 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 - LIBCOMMON_EXPORT std::string GetFileNamePart(const std::string& input); + //------------------------------------------------------------------------ + // TODO: In line functions that should go elsewhere + //------------------------------------------------------------------------ - // Determine if path is a direcory - LIBCOMMON_EXPORT bool IsDirectory(const std::string& path); + // 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 static_cast(std::tolower(c)); + }); + return s; + } - // Get path of loaded module or current application - LIBCOMMON_EXPORT std::string get_module_path(HMODULE module_handle); + inline std::string to_upper(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { + return static_cast(std::toupper(c)); + }); + return s; + } - // If path is in the application directory strip directory - LIBCOMMON_EXPORT std::string strip_application_path(std::string path); + inline void make_lower(std::string& s) { + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { + return static_cast(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 static_cast(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))); + } + + 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(); + } + + 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/include/vcc/util/initial_settings.h b/libcommon/include/vcc/util/initial_settings.h deleted file mode 100644 index 820d5d9c..00000000 --- a/libcommon/include/vcc/util/initial_settings.h +++ /dev/null @@ -1,47 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// 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 . -//////////////////////////////////////////////////////////////////////////////// -#pragma once -#include // defines LIBCOMMON_EXPORT if libcommon is a DLL -#include - -namespace VCC::Core -{ - - class initial_settings - { - public: - - using path_type = std::string; - using string_type = std::string; - - public: - - 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, 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; - - - private: - - const path_type path_; - }; -} diff --git a/libcommon/include/vcc/util/settings.h b/libcommon/include/vcc/util/settings.h new file mode 100644 index 00000000..70b977e7 --- /dev/null +++ b/libcommon/include/vcc/util/settings.h @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 . +//////////////////////////////////////////////////////////////////////////////// +#pragma once +#include // defines LIBCOMMON_EXPORT if libcommon is a DLL +#include +#include + +namespace VCC::Util +{ + class settings + { + public: + using path_type = std::string; + + public: + LIBCOMMON_EXPORT explicit settings(path_type path); + + // write string value + LIBCOMMON_EXPORT void write + (const std::string& section, + const std::string& key, + const std::string& value) const; + + // write int value + LIBCOMMON_EXPORT void write + (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 std::string& section, + 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_; + }; +} 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/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/DialogOps.cpp b/libcommon/src/util/DialogOps.cpp index 46bcb3c1..96510885 100644 --- a/libcommon/src/util/DialogOps.cpp +++ b/libcommon/src/util/DialogOps.cpp @@ -16,24 +16,36 @@ // VCC (Virtual Color Computer). If not, see . //////////////////////////////////////////////////////////////////////////////// #include +#include +#include #include #include -//#include //------------------------------------------------------------------------------------------- // 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 +// //------------------------------------------------------------------------------------------- -// 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); @@ -44,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; @@ -54,93 +68,15 @@ bool FileDialog::show(BOOL Save, HWND Owner) { } else { rc = GetOpenFileName(&ofn_) ; } - return ((rc == 1) && (*path_ != '\0')); -} - -void FileDialog::setDefExt(const char * DefExt) { - ofn_.lpstrDefExt = DefExt; -} - -void FileDialog::setInitialDir(const char * 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); -} - -// Get a copy of the selected file path -void FileDialog::getpath(char * PathCopy, int maxsize) const { - if (PathCopy == nullptr || path_ == nullptr || maxsize < 1) return; - strncpy(PathCopy,path_,maxsize); -} - -// Get a copy of the selected file path with unix dir 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'; -} - -// Get a pointer to the selected file path -const char *FileDialog::path() const -{ - return path_; -} + // Copy file selected back + if ((rc == 1) && (*file != '\0')) { + sFile_.assign(file ? file : ""); + VCC::Util::FixDirSlashes(sFile_); + return true; + }; -// 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'; -} - -// 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); -} - -// 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; } //------------------------------------------------------------ diff --git a/libcommon/src/util/fileutil.cpp b/libcommon/src/util/fileutil.cpp index ba1e989b..f8b35143 100644 --- a/libcommon/src/util/fileutil.cpp +++ b/libcommon/src/util/fileutil.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -54,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); + } } //------------------------------------------------------------------- @@ -139,4 +90,20 @@ namespace VCC::Util } return path; } + + //--------------------------------------------------------------- + // 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/libcommon/src/util/initial_settings.cpp b/libcommon/src/util/initial_settings.cpp deleted file mode 100644 index 8f635c08..00000000 --- a/libcommon/src/util/initial_settings.cpp +++ /dev/null @@ -1,74 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// 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 . -//////////////////////////////////////////////////////////////////////////////// -#include -#include - - -namespace VCC::Core -{ - - initial_settings::initial_settings(path_type path) - : path_(move(path)) - { - } - - void initial_settings::write( - const string_type& section, - const string_type& key, - int value) const - { - WritePrivateProfileString( - section.c_str(), - key.c_str(), - std::to_string(value).c_str(), - path_.c_str()); - } - - void initial_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()); - } - - int initial_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( - 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( - section.c_str(), - key.c_str(), - default_value.c_str(), - loaded_string, - MAX_PATH, - path_.c_str()); - - return loaded_string; - } - -} diff --git a/libcommon/src/util/settings.cpp b/libcommon/src/util/settings.cpp new file mode 100644 index 00000000..95c643c8 --- /dev/null +++ b/libcommon/src/util/settings.cpp @@ -0,0 +1,106 @@ +//#define USE_LOGGING +//////////////////////////////////////////////////////////////////////////////// +// 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 . +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +namespace VCC::Util +{ + settings::settings(path_type path) + : path_(move(path)) + {} + + // save string value + void settings::write( + 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( + section.c_str(), + key.c_str(), + std::to_string(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); + } + + // 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 + { + ::GetPrivateProfileStringA( + section.c_str(), + key.c_str(), + default_value.c_str(), + buffer, + static_cast(buffer_size), + path_.c_str()); + } + + // get string value + std::string settings::read + (const std::string& section, + const std::string& key, + const std::string& default_value) const + { + char buffer[MAX_PATH] = {}; + read(section, key, default_value, buffer, sizeof(buffer)); + return std::string(buffer); + } + + // 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 fe6894bd..1e1415f7 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 +#include -using settings = ::VCC::Core::initial_settings; +using settings = ::VCC::Util::settings; // Constructor sets the section multipak_configuration::multipak_configuration(string_type section) @@ -40,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/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) { diff --git a/sdc/sdc.cpp b/sdc/sdc.cpp index 9439cc20..2454090b 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)) { @@ -489,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; @@ -520,13 +520,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 +545,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); } - - 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 +572,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 +648,7 @@ void InitCardBox() void UpdateFlashItem(int index) { + util::settings settings(IniFile); char filename[MAX_PATH]={}; if ((index < 0) | (index > 7)) return; @@ -666,10 +662,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 +674,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 +740,7 @@ void InitSDC() // Load SDC settings LoadConfig(); - LoadRom(StartupBank); + LoadRom(gStartupBank); SDCSetCurDir(""); // May be changed by ParseStartup() @@ -819,54 +817,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 +882,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 +1050,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 +1149,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 +1311,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 +1501,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 +1551,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 +1567,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 +1641,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 +1786,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 +1935,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", 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();