WIN32: Add Portable Mode
Adds support for a self-contained portable mode in which the executable's directory is used for application files instead of directories in the user's profile. Portable mode is activated by placing a scummvm.ini file in the executable's directory. The directory must be outside of the system's Program Files directory to comply with UAC.
This commit is contained in:
parent
84872f6b8d
commit
3d6524c9f4
6 changed files with 167 additions and 64 deletions
|
@ -59,6 +59,10 @@
|
|||
|
||||
#define DEFAULT_CONFIG_FILE "scummvm.ini"
|
||||
|
||||
OSystem_Win32::OSystem_Win32() :
|
||||
_isPortable(false) {
|
||||
}
|
||||
|
||||
void OSystem_Win32::init() {
|
||||
// Initialize File System Factory
|
||||
_fsFactory = new WindowsFilesystemFactory();
|
||||
|
@ -112,8 +116,8 @@ void OSystem_Win32::initBackend() {
|
|||
}
|
||||
|
||||
// Create the savefile manager
|
||||
if (_savefileManager == 0)
|
||||
_savefileManager = new WindowsSaveFileManager();
|
||||
if (_savefileManager == nullptr)
|
||||
_savefileManager = new WindowsSaveFileManager(_isPortable);
|
||||
|
||||
#if defined(USE_SPARKLE)
|
||||
// Initialize updates manager
|
||||
|
@ -240,16 +244,19 @@ Common::String OSystem_Win32::getScreenshotsPath() {
|
|||
return screenshotsPath;
|
||||
}
|
||||
|
||||
// Use the My Pictures folder.
|
||||
TCHAR picturesPath[MAX_PATH];
|
||||
|
||||
if (SHGetFolderPathFunc(NULL, CSIDL_MYPICTURES, NULL, SHGFP_TYPE_CURRENT, picturesPath) != S_OK) {
|
||||
warning("Unable to access My Pictures directory");
|
||||
return Common::String();
|
||||
if (_isPortable) {
|
||||
Win32::getProcessDirectory(picturesPath, MAX_PATH);
|
||||
_tcscat(picturesPath, TEXT("\\Screenshots\\"));
|
||||
} else {
|
||||
// Use the My Pictures folder
|
||||
if (SHGetFolderPathFunc(NULL, CSIDL_MYPICTURES, NULL, SHGFP_TYPE_CURRENT, picturesPath) != S_OK) {
|
||||
warning("Unable to access My Pictures directory");
|
||||
return Common::String();
|
||||
}
|
||||
_tcscat(picturesPath, TEXT("\\ScummVM Screenshots\\"));
|
||||
}
|
||||
|
||||
_tcscat(picturesPath, TEXT("\\ScummVM Screenshots\\"));
|
||||
|
||||
// If the directory already exists (as it should in most cases),
|
||||
// we don't want to fail, but we need to stop on other errors (such as ERROR_PATH_NOT_FOUND)
|
||||
if (!CreateDirectory(picturesPath, NULL)) {
|
||||
|
@ -263,41 +270,48 @@ Common::String OSystem_Win32::getScreenshotsPath() {
|
|||
Common::String OSystem_Win32::getDefaultConfigFileName() {
|
||||
TCHAR configFile[MAX_PATH];
|
||||
|
||||
// Use the Application Data directory of the user profile.
|
||||
if (SHGetFolderPathFunc(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, configFile) == S_OK) {
|
||||
_tcscat(configFile, TEXT("\\ScummVM"));
|
||||
if (!CreateDirectory(configFile, NULL)) {
|
||||
if (GetLastError() != ERROR_ALREADY_EXISTS)
|
||||
error("Cannot create ScummVM application data folder");
|
||||
}
|
||||
// if this is the first time the default config file name is requested
|
||||
// then we need detect if we should run in portable mode. (and if it's
|
||||
// never requested before the backend is initialized then a config file
|
||||
// was provided on the command line and portable mode doesn't apply.)
|
||||
if (!backendInitialized()) {
|
||||
_isPortable = detectPortableConfigFile();
|
||||
}
|
||||
|
||||
if (_isPortable) {
|
||||
// Use the current process directory in portable mode
|
||||
Win32::getProcessDirectory(configFile, MAX_PATH);
|
||||
_tcscat(configFile, TEXT("\\" DEFAULT_CONFIG_FILE));
|
||||
} else {
|
||||
// Use the Application Data directory of the user profile
|
||||
if (Win32::getApplicationDataDirectory(configFile)) {
|
||||
_tcscat(configFile, TEXT("\\" DEFAULT_CONFIG_FILE));
|
||||
|
||||
FILE *tmp = NULL;
|
||||
if ((tmp = _tfopen(configFile, TEXT("r"))) == NULL) {
|
||||
// Check windows directory
|
||||
TCHAR oldConfigFile[MAX_PATH];
|
||||
uint ret = GetWindowsDirectory(oldConfigFile, MAX_PATH);
|
||||
if (ret == 0 || ret > MAX_PATH)
|
||||
error("Cannot retrieve the path of the Windows directory");
|
||||
FILE *tmp = NULL;
|
||||
if ((tmp = _tfopen(configFile, TEXT("r"))) == NULL) {
|
||||
// Check windows directory
|
||||
TCHAR oldConfigFile[MAX_PATH];
|
||||
uint ret = GetWindowsDirectory(oldConfigFile, MAX_PATH);
|
||||
if (ret == 0 || ret > MAX_PATH)
|
||||
error("Cannot retrieve the path of the Windows directory");
|
||||
|
||||
_tcscat(oldConfigFile, TEXT("\\" DEFAULT_CONFIG_FILE));
|
||||
if ((tmp = _tfopen(oldConfigFile, TEXT("r")))) {
|
||||
_tcscpy(configFile, oldConfigFile);
|
||||
_tcscat(oldConfigFile, TEXT("\\" DEFAULT_CONFIG_FILE));
|
||||
if ((tmp = _tfopen(oldConfigFile, TEXT("r")))) {
|
||||
_tcscpy(configFile, oldConfigFile);
|
||||
|
||||
fclose(tmp);
|
||||
}
|
||||
} else {
|
||||
fclose(tmp);
|
||||
}
|
||||
} else {
|
||||
fclose(tmp);
|
||||
}
|
||||
} else {
|
||||
warning("Unable to access application data directory");
|
||||
// Check windows directory
|
||||
uint ret = GetWindowsDirectory(configFile, MAX_PATH);
|
||||
if (ret == 0 || ret > MAX_PATH)
|
||||
error("Cannot retrieve the path of the Windows directory");
|
||||
// Check windows directory
|
||||
uint ret = GetWindowsDirectory(configFile, MAX_PATH);
|
||||
if (ret == 0 || ret > MAX_PATH)
|
||||
error("Cannot retrieve the path of the Windows directory");
|
||||
|
||||
_tcscat(configFile, TEXT("\\" DEFAULT_CONFIG_FILE));
|
||||
_tcscat(configFile, TEXT("\\" DEFAULT_CONFIG_FILE));
|
||||
}
|
||||
}
|
||||
|
||||
return Win32::tcharToString(configFile);
|
||||
|
@ -306,21 +320,54 @@ Common::String OSystem_Win32::getDefaultConfigFileName() {
|
|||
Common::String OSystem_Win32::getDefaultLogFileName() {
|
||||
TCHAR logFile[MAX_PATH];
|
||||
|
||||
// Use the Application Data directory of the user profile.
|
||||
if (SHGetFolderPathFunc(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, logFile) != S_OK) {
|
||||
warning("Unable to access application data directory");
|
||||
return Common::String();
|
||||
if (_isPortable) {
|
||||
Win32::getProcessDirectory(logFile, MAX_PATH);
|
||||
} else {
|
||||
// Use the Application Data directory of the user profile
|
||||
if (!Win32::getApplicationDataDirectory(logFile)) {
|
||||
return Common::String();
|
||||
}
|
||||
_tcscat(logFile, TEXT("\\Logs"));
|
||||
CreateDirectory(logFile, NULL);
|
||||
}
|
||||
|
||||
_tcscat(logFile, TEXT("\\ScummVM"));
|
||||
CreateDirectory(logFile, NULL);
|
||||
_tcscat(logFile, TEXT("\\Logs"));
|
||||
CreateDirectory(logFile, NULL);
|
||||
_tcscat(logFile, TEXT("\\scummvm.log"));
|
||||
|
||||
return Win32::tcharToString(logFile);
|
||||
}
|
||||
|
||||
bool OSystem_Win32::detectPortableConfigFile() {
|
||||
// ScummVM operates in a "portable mode" if there is a config file in the
|
||||
// same directory as the executable. In this mode, the executable's
|
||||
// directory is used instead of the user's profile for application files.
|
||||
// This approach is modeled off of the portable mode in Notepad++.
|
||||
|
||||
// Check if there is a config file in the same directory as the executable.
|
||||
TCHAR portableConfigFile[MAX_PATH];
|
||||
Win32::getProcessDirectory(portableConfigFile, MAX_PATH);
|
||||
_tcscat(portableConfigFile, TEXT("\\" DEFAULT_CONFIG_FILE));
|
||||
FILE *file = _tfopen(portableConfigFile, TEXT("r"));
|
||||
if (file == NULL) {
|
||||
return false;
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
// Check if we're running from Program Files on Vista+.
|
||||
// If so then don't attempt to use local files due to UAC.
|
||||
// (Notepad++ does this too.)
|
||||
if (Win32::confirmWindowsVersion(6, 0)) {
|
||||
TCHAR programFiles[MAX_PATH];
|
||||
if (SHGetFolderPathFunc(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, programFiles) == S_OK) {
|
||||
_tcscat(portableConfigFile, TEXT("\\"));
|
||||
if (_tcsstr(portableConfigFile, programFiles) == portableConfigFile) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class Win32ResourceArchive final : public Common::Archive {
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
class OSystem_Win32 final : public OSystem_SDL {
|
||||
public:
|
||||
OSystem_Win32();
|
||||
|
||||
virtual void init() override;
|
||||
virtual void initBackend() override;
|
||||
|
||||
|
@ -58,6 +60,10 @@ protected:
|
|||
virtual AudioCDManager *createAudioCDManager() override;
|
||||
|
||||
HWND getHwnd() { return ((SdlWindow_Win32*)_window)->getHwnd(); }
|
||||
|
||||
private:
|
||||
bool _isPortable;
|
||||
bool detectPortableConfigFile();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,8 +28,10 @@
|
|||
#define _WIN32_IE 0x400
|
||||
#endif
|
||||
#include <shlobj.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "backends/platform/sdl/win32/win32_wrapper.h"
|
||||
|
||||
// VerSetConditionMask, VerifyVersionInfo and SHGetFolderPath didn't appear until Windows 2000,
|
||||
|
@ -72,6 +74,33 @@ HRESULT SHGetFolderPathFunc(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags,
|
|||
|
||||
namespace Win32 {
|
||||
|
||||
bool getApplicationDataDirectory(TCHAR *applicationDataDirectory) {
|
||||
if (SHGetFolderPathFunc(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, applicationDataDirectory) != S_OK) {
|
||||
warning("Unable to access application data directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
_tcscat(applicationDataDirectory, TEXT("\\ScummVM"));
|
||||
if (!CreateDirectory(applicationDataDirectory, NULL)) {
|
||||
if (GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
error("Cannot create ScummVM application data folder");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void getProcessDirectory(TCHAR *processDirectory, DWORD size) {
|
||||
GetModuleFileName(NULL, processDirectory, size);
|
||||
processDirectory[size - 1] = '\0'; // termination not guaranteed
|
||||
|
||||
// remove executable and final path separator
|
||||
TCHAR *lastSeparator = _tcsrchr(processDirectory, '\\');
|
||||
if (lastSeparator != NULL) {
|
||||
*lastSeparator = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
bool confirmWindowsVersion(int majorVersion, int minorVersion) {
|
||||
OSVERSIONINFOEXA versionInfo;
|
||||
DWORDLONG conditionMask = 0;
|
||||
|
@ -173,4 +202,4 @@ void freeArgvUtf8(int argc, char **argv) {
|
|||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
} // End of namespace Win32
|
||||
|
|
|
@ -31,6 +31,29 @@ HRESULT SHGetFolderPathFunc(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags,
|
|||
// Helper functions
|
||||
namespace Win32 {
|
||||
|
||||
/**
|
||||
* Gets the full path to the ScummVM application data directory
|
||||
* in the user's profile and creates it if it doesn't exist.
|
||||
*
|
||||
* @param profileDirectory MAX_PATH sized output array
|
||||
*
|
||||
* @return True if the user's profile directory was found, false if
|
||||
* it was not.
|
||||
*
|
||||
* @note if the user's profile directory is found but the "ScummVM"
|
||||
* subdirectory can't be created then this function calls error().
|
||||
*/
|
||||
bool getApplicationDataDirectory(TCHAR *profileDirectory);
|
||||
|
||||
/**
|
||||
* Gets the full path to the directory that the currently executing
|
||||
* process resides in.
|
||||
*
|
||||
* @param processDirectory output array
|
||||
* @param size size in characters of output array
|
||||
*/
|
||||
void getProcessDirectory(TCHAR *processDirectory, DWORD size);
|
||||
|
||||
/**
|
||||
* Checks if the current running Windows version is greater or equal to the specified version.
|
||||
* See: https://docs.microsoft.com/en-us/windows/desktop/sysinfo/operating-system-version
|
||||
|
@ -115,6 +138,6 @@ char **getArgvUtf8(int *argc);
|
|||
void freeArgvUtf8(int argc, char **argv);
|
||||
#endif
|
||||
|
||||
}
|
||||
} // End of namespace Win32
|
||||
|
||||
#endif
|
||||
|
|
|
@ -36,27 +36,25 @@
|
|||
#include "backends/saves/windows/windows-saves.h"
|
||||
#include "backends/platform/sdl/win32/win32_wrapper.h"
|
||||
|
||||
WindowsSaveFileManager::WindowsSaveFileManager() {
|
||||
WindowsSaveFileManager::WindowsSaveFileManager(bool isPortable) {
|
||||
TCHAR defaultSavepath[MAX_PATH];
|
||||
|
||||
// Use the Application Data directory of the user profile.
|
||||
if (SHGetFolderPathFunc(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, defaultSavepath) == S_OK) {
|
||||
_tcscat(defaultSavepath, TEXT("\\ScummVM"));
|
||||
if (!CreateDirectory(defaultSavepath, NULL)) {
|
||||
if (GetLastError() != ERROR_ALREADY_EXISTS)
|
||||
error("Cannot create ScummVM application data folder");
|
||||
}
|
||||
|
||||
_tcscat(defaultSavepath, TEXT("\\Saved games"));
|
||||
if (!CreateDirectory(defaultSavepath, NULL)) {
|
||||
if (GetLastError() != ERROR_ALREADY_EXISTS)
|
||||
error("Cannot create ScummVM Saved games folder");
|
||||
}
|
||||
|
||||
ConfMan.registerDefault("savepath", Win32::tcharToString(defaultSavepath));
|
||||
if (isPortable) {
|
||||
Win32::getProcessDirectory(defaultSavepath, MAX_PATH);
|
||||
} else {
|
||||
warning("Unable to access application data directory");
|
||||
// Use the Application Data directory of the user profile
|
||||
if (!Win32::getApplicationDataDirectory(defaultSavepath)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_tcscat(defaultSavepath, TEXT("\\Saved games"));
|
||||
if (!CreateDirectory(defaultSavepath, NULL)) {
|
||||
if (GetLastError() != ERROR_ALREADY_EXISTS)
|
||||
error("Cannot create ScummVM Saved games folder");
|
||||
}
|
||||
|
||||
ConfMan.registerDefault("savepath", Win32::tcharToString(defaultSavepath));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -26,11 +26,11 @@
|
|||
#include "backends/saves/default/default-saves.h"
|
||||
|
||||
/**
|
||||
* Provides a default savefile manager implementation for common platforms.
|
||||
* Provides a savefile manager implementation for Windows.
|
||||
*/
|
||||
class WindowsSaveFileManager final : public DefaultSaveFileManager {
|
||||
public:
|
||||
WindowsSaveFileManager();
|
||||
WindowsSaveFileManager(bool isPortable);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue