diff --git a/.gitignore b/.gitignore index a426adb..c8cc379 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ *.sdf *.user *.opensdf -Release \ No newline at end of file +.vs +Release +Debug +*.suo +*.opendb +*.filters diff --git a/Actions.def b/Actions.def index 304dab2..5000784 100644 --- a/Actions.def +++ b/Actions.def @@ -36,3 +36,5 @@ ACTION(userTrigger, SDLOG(0, "================================================== ACTION(reloadHUDVertices, RSManager::get().reloadHudVertices()); ACTION(togglePaused, RSManager::get().togglePaused()); + +ACTION(manualBackup, SaveManager::get().backup();); diff --git a/DATA/DSfixKeys.ini b/DATA/DSfixKeys.ini index 6df0a9e..ce1f0c1 100644 --- a/DATA/DSfixKeys.ini +++ b/DATA/DSfixKeys.ini @@ -26,6 +26,8 @@ toggle30FPSLimit VK_BACK #userTrigger VK_F1 #togglePaused VK_F9 +manualBackup VK_F12 + # Available Actions: # toggleCursorVisibility, toggleCursorCapture, toggleBorderlessFullscreen, takeHudlessScreenshot, toggleHUD, # toggleSMAA, toggleVSSAO, toggleDofGauss, toggleHudChange, reloadSSAOEffect, singleFrameFullCapture, userTrigger diff --git a/KeyActions.cpp b/KeyActions.cpp index e177513..1f9d82f 100644 --- a/KeyActions.cpp +++ b/KeyActions.cpp @@ -10,6 +10,7 @@ using namespace std; #include "WindowManager.h" #include "Settings.h" #include "RenderstateManager.h" +#include "SaveManager.h" KeyActions KeyActions::instance; diff --git a/SaveManager.cpp b/SaveManager.cpp index 0a53d32..07cee51 100644 --- a/SaveManager.cpp +++ b/SaveManager.cpp @@ -1,6 +1,7 @@ #include "SaveManager.h" #include +#include #include "Settings.h" @@ -10,127 +11,161 @@ #define TIMESTAMP_LENGTH 12 #define TIMESTAMP_LENGTH_STR EXPAND_AND_QUOTE(TIMESTAMP_LENGTH) +#define SAVE_EXT ".sl2" +#define BACKUP_EXT ".bak" + +string getFileNameFromPath(const string& path) { + size_t pos = path.rfind('\\'); + if (pos != path.npos) { + return path.substr(pos + 1); + } + else { + SDLOG(0, "ERROR: SaveManager could not extract file name from path %s\n", path.c_str()); + } + return path; +} + +bool operator>(const FILETIME& lhs, const FILETIME& rhs) { + ULARGE_INTEGER uiL, uiR; + + uiL.LowPart = lhs.dwLowDateTime; + uiL.HighPart = lhs.dwHighDateTime; + + uiR.LowPart = rhs.dwLowDateTime; + uiR.HighPart = rhs.dwHighDateTime; + + return uiL.QuadPart > uiR.QuadPart; +} + +bool fileDataWriteTimeCompareGreater(const WIN32_FIND_DATA& lhs, const WIN32_FIND_DATA& rhs) { + return lhs.ftLastWriteTime > rhs.ftLastWriteTime; +} + +time_t fileTimeToEpoch(const FILETIME& ft) { + ULARGE_INTEGER ull; + ull.LowPart = ft.dwLowDateTime; + ull.HighPart = ft.dwHighDateTime; + + return ull.QuadPart / 10000000ULL - 11644473600ULL; +} + +string joinPath(const string& base, const string& comp) { + char buffer[MAX_PATH]; + sprintf_s(buffer, "%s\\%s", base.c_str(), comp.c_str()); + return string(buffer); +} + SaveManager SaveManager::instance; void SaveManager::init() { - if(Settings::get().getEnableBackups()) { - CHAR documents[MAX_PATH]; - HRESULT hr = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, documents); - char buffer[MAX_PATH]; - sprintf_s(buffer, "%s%s", documents, "\\NBGI\\DarkSouls\\*"); - - // find user save folder - WIN32_FIND_DATA userSaveFolderData; - HANDLE searchHandle = FindFirstFile(buffer, &userSaveFolderData); - bool found = false; - if(searchHandle != INVALID_HANDLE_VALUE) { - do { - std::string fn = userSaveFolderData.cFileName; - bool dir = userSaveFolderData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - bool saveFile = fn.substr(fn.find_last_of(".") + 1) == "sl2"; - // newer versions don't contain an additional folder under NBGI\\DarkSouls - if (fn.size() > 2 && (dir || saveFile)) { - if (dir) - sprintf_s(buffer, "%s%s%s", documents, "\\NBGI\\DarkSouls\\", userSaveFolderData.cFileName); - else - sprintf_s(buffer, "%s%s", documents, "\\NBGI\\DarkSouls"); - userSaveFolder = string(buffer); - SDLOG(0, "SaveManager: user save folder is %s\n", userSaveFolder.c_str()); - found = true; - break; - } - } while(FindNextFile(searchHandle, &userSaveFolderData)); - } - if(!found) { - SDLOG(0, "SaveManager: could not determine user save folder\n"); - return; - } - - removeOldBackups(); - } + if (Settings::get().getEnableBackups()) { + char documents[MAX_PATH]; + HRESULT hr = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, documents); + + if (hr == S_OK) + { + userSaveFolder = joinPath(documents, "NBGI\\DarkSouls"); + SDLOG(0, "SaveManager: Found save folder at %s\n", userSaveFolder); + removeOldBackups(); + } + } } void SaveManager::tick() { - if(Settings::get().getEnableBackups()) { - time_t curTime = time(NULL); - if(curTime - getLastBackupTime() > Settings::get().getBackupInterval()) { - backup(curTime); - lastBackupTime = curTime; - } - } + if (Settings::get().getEnableBackups() && !userSaveFolder.empty()) { + time_t ls, curTime = time(NULL); + time_t backupInterval = Settings::get().getBackupInterval(); + if (curTime - getLastBackupTime() > backupInterval && + getLastSaveTime() - getLastBackupSaveTime() > backupInterval) { + SDLOG(2, "SaveManager: last backup time %ld\n", lastBackupTime); + SDLOG(2, "SaveManager: last backup save time %ld\n", lastBackupSaveTime); + backup(); + } + } } time_t SaveManager::getLastBackupTime() { - if(lastBackupTime == 0) { - vector backupFiles = getSaveFiles(".bak"); - if(!backupFiles.empty()) { - string fn = getFileNameFromPath(backupFiles.front()); - sscanf_s(fn.c_str(), "%lu", &lastBackupTime); - } - } - SDLOG(3, "SaveManager: last backup time %ld\n", lastBackupTime); - return lastBackupTime; + if (lastBackupTime == 0) { + vector backupFiles = getSaveFiles(BACKUP_EXT); + if (!backupFiles.empty()) { + lastBackupTime = lastBackupSaveTime = fileTimeToEpoch(backupFiles[0].ftLastWriteTime); + } + } + return lastBackupTime; } -vector SaveManager::getSaveFiles(const char* ending /*= ".sl2"*/) { - SDLOG(2, "SaveManager: searching for files ending on %s\n", ending); - vector ret; - // find saved files - if(userSaveFolder.length() > 0) { - char buffer[MAX_PATH]; - sprintf_s(buffer, "%s\\*%s", userSaveFolder.c_str(), ending); - WIN32_FIND_DATA saveFileData; - HANDLE searchHandle = FindFirstFile(buffer, &saveFileData); - if(searchHandle != INVALID_HANDLE_VALUE) { - do { - char buff2[MAX_PATH]; - sprintf_s(buff2, "%s\\%s", userSaveFolder.c_str(), saveFileData.cFileName); - ret.push_back(string(buff2)); - } while(FindNextFile(searchHandle, &saveFileData)); - } - std::sort(ret.begin(), ret.end()); - std::reverse(ret.begin(), ret.end()); - for(size_t i=0; i backupFiles = getSaveFiles(BACKUP_EXT); + if (!backupFiles.empty()) { + lastBackupSaveTime = fileTimeToEpoch(backupFiles[0].ftLastWriteTime); + } + } + return lastBackupSaveTime; } -void SaveManager::backup(const time_t curTime) { - SDLOG(1, "SaveManager: Backing up save files\n"); - char buffer[MAX_PATH]; - vector saveFiles = getSaveFiles(); - for(size_t i=0; i saveFiles = getSaveFiles(SAVE_EXT); + return saveFiles.empty() ? 0 : fileTimeToEpoch(saveFiles[0].ftLastWriteTime); } -void SaveManager::removeOldBackups() { - vector backupFiles = getSaveFiles(".bak"); - if(Settings::get().getMaxBackups() < backupFiles.size()) { - SDLOG(1, "SaveManager: Removing %u old backups\n", backupFiles.size() - Settings::get().getMaxBackups()); - for(size_t i=Settings::get().getMaxBackups(); i SaveManager::getSaveFiles(const char* ending) { + vector ret; + + if (!userSaveFolder.empty()) { + SDLOG(4, "SaveManager: searching for files ending with %s\n", ending); + + char buffer[MAX_PATH]; + sprintf_s(buffer, "%s\\*%s", userSaveFolder.c_str(), ending); + + WIN32_FIND_DATA saveFileData; + HANDLE sh = FindFirstFile(buffer, &saveFileData); + if (sh != INVALID_HANDLE_VALUE) + { + do + { + ret.push_back(saveFileData); + SDLOG(3, "SaveManager: found save file %s\\%s\n", userSaveFolder.c_str(), saveFileData.cFileName); + } while (FindNextFile(sh, &saveFileData)); + std::sort(ret.begin(), ret.end(), fileDataWriteTimeCompareGreater); + } + } + return ret; } -string SaveManager::getFileNameFromPath(const string& path) { - size_t pos = path.rfind('\\'); - if(pos != path.npos) { - pos += 1; - return path.substr(pos); - } else { - SDLOG(0, "ERROR: SaveManager could not extract file name from path %s\n", path.c_str()); - } - return path; +void SaveManager::backup() { + SDLOG(3, "SaveManager: Backing up save files\n"); + + vector saveFiles = getSaveFiles(SAVE_EXT); + + if (!saveFiles.empty()) { + WIN32_FIND_DATA saveFile = saveFiles[0]; + string saveFullPath = joinPath(userSaveFolder, saveFile.cFileName); + + char newPath[MAX_PATH]; + time_t curTime = time(NULL); + sprintf_s(newPath, "%s\\%0" TIMESTAMP_LENGTH_STR "llu_%s" BACKUP_EXT, userSaveFolder.c_str(), curTime, saveFile.cFileName); + + if (CopyFile(saveFullPath.c_str(), newPath, false) == 0) { + SDLOG(0, "ERROR: SaveManager failed to back up file! (Copying %s to %s)\n", saveFullPath.c_str(), newPath); + } + else { + SDLOG(1, "SaveManager: Backed up %s to %s\n", saveFullPath.c_str(), newPath); + lastBackupTime = curTime; + lastBackupSaveTime = fileTimeToEpoch(saveFile.ftLastWriteTime); + removeOldBackups(); + } + } +} + +void SaveManager::removeOldBackups() { + vector backupFiles = getSaveFiles(BACKUP_EXT); + if (Settings::get().getMaxBackups() < backupFiles.size()) { + SDLOG(1, "SaveManager: Removing %u old backups\n", backupFiles.size() - Settings::get().getMaxBackups()); + for (size_t i = Settings::get().getMaxBackups(); i < backupFiles.size(); ++i) { + DeleteFile(joinPath(userSaveFolder, backupFiles[i].cFileName).c_str()); + } + } } diff --git a/SaveManager.h b/SaveManager.h index 307f582..0437727 100644 --- a/SaveManager.h +++ b/SaveManager.h @@ -14,17 +14,18 @@ using std::string; class SaveManager { static SaveManager instance; - + string userSaveFolder; - time_t lastBackupTime; + time_t lastBackupTime; + time_t lastBackupSaveTime; + + vector getSaveFiles(const char* ending); - string getFileNameFromPath(const string& path); - vector getSaveFiles(const char* ending = ".sl2"); - - void backup(const time_t curTime); void removeOldBackups(); time_t getLastBackupTime(); + time_t getLastBackupSaveTime(); + time_t getLastSaveTime(); public: static SaveManager& get() { @@ -35,5 +36,6 @@ class SaveManager { void init(); void tick(); + void backup(); };