From 150d2eaec9d7042f2899a78f9d1a3d189857c633 Mon Sep 17 00:00:00 2001 From: Souryo Date: Fri, 1 Sep 2017 18:45:53 -0400 Subject: [PATCH] Savestates: Allow saving/loading to arbitrary files --- Core/Console.cpp | 23 +++-- Core/LuaApi.cpp | 2 +- Core/MessageManager.cpp | 10 ++- Core/RomLoader.cpp | 6 +- Core/RomLoader.h | 2 +- Core/SaveStateManager.cpp | 103 ++++++++++++++++------- Core/SaveStateManager.h | 6 +- GUI.NET/Debugger/frmDebugger.Designer.cs | 2 +- GUI.NET/Dependencies/resources.ca.xml | 4 + GUI.NET/Dependencies/resources.en.xml | 4 + GUI.NET/Dependencies/resources.es.xml | 4 + GUI.NET/Dependencies/resources.fr.xml | 4 + GUI.NET/Dependencies/resources.ja.xml | 4 + GUI.NET/Dependencies/resources.pt.xml | 4 + GUI.NET/Dependencies/resources.ru.xml | 4 + GUI.NET/Dependencies/resources.uk.xml | 4 + GUI.NET/Forms/frmMain.Designer.cs | 2 +- GUI.NET/Forms/frmMain.File.cs | 41 ++++++++- GUI.NET/InteropEmu.cs | 2 + InteropDLL/ConsoleWrapper.cpp | 4 +- Utilities/FolderUtilities.cpp | 11 ++- 21 files changed, 191 insertions(+), 55 deletions(-) diff --git a/Core/Console.cpp b/Core/Console.cpp index 3595f35e..807000bf 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -173,19 +173,24 @@ bool Console::LoadROM(string romName, HashInfo hashInfo) } } + string lcRomname = romName; + std::transform(lcRomname.begin(), lcRomname.end(), lcRomname.begin(), ::tolower); + std::unordered_set validExtensions = { { ".nes", ".fds", "*.unif", "*.unif", "*.nsf", "*.nsfe", "*.7z", "*.zip" } }; + vector romFiles; for(string folder : FolderUtilities::GetKnownGameFolders()) { - string match = RomLoader::FindMatchingRomInFolder(folder, romName, hashInfo, true); - if(!match.empty()) { - return Console::LoadROM(match); - } + vector files = FolderUtilities::GetFilesInFolder(folder, validExtensions, true); + romFiles.insert(romFiles.end(), files.begin(), files.end()); + } + + string match = RomLoader::FindMatchingRom(romFiles, romName, hashInfo, true); + if(!match.empty()) { + return Console::LoadROM(match); } //Perform slow CRC32 search for ROM - for(string folder : FolderUtilities::GetKnownGameFolders()) { - string match = RomLoader::FindMatchingRomInFolder(folder, romName, hashInfo, false); - if(!match.empty()) { - return Console::LoadROM(match); - } + match = RomLoader::FindMatchingRom(romFiles, romName, hashInfo, false); + if(!match.empty()) { + return Console::LoadROM(match); } return false; diff --git a/Core/LuaApi.cpp b/Core/LuaApi.cpp index 22168aa9..919152a6 100644 --- a/Core/LuaApi.cpp +++ b/Core/LuaApi.cpp @@ -501,7 +501,7 @@ int LuaApi::LoadSavestate(lua_State *lua) checkstartframe(); stringstream ss; ss << savestate; - l.Return(SaveStateManager::LoadState(ss)); + l.Return(SaveStateManager::LoadState(ss, true)); return l.ReturnCount(); } diff --git a/Core/MessageManager.cpp b/Core/MessageManager.cpp index fe1655f7..551358cb 100644 --- a/Core/MessageManager.cpp +++ b/Core/MessageManager.cpp @@ -54,6 +54,7 @@ std::unordered_map MessageManager::_enResources = { { "SaveStateIncompatibleVersion", u8"State #%1 is incompatible with this version of Mesen." }, { "SaveStateInvalidFile", u8"Invalid save state file." }, { "SaveStateLoaded", u8"State #%1 loaded." }, + { "SaveStateMissingRom", u8"Missing ROM required (%1) to load save state." }, { "SaveStateNewerVersion", u8"Cannot load save states created by a more recent version of Mesen. Please download the latest version." }, { "SaveStateSaved", u8"State #%1 saved." }, { "SaveStateSlotSelected", u8"Slot #%1 selected." }, @@ -123,6 +124,7 @@ std::unordered_map MessageManager::_frResources = { { "SaveStateIncompatibleVersion", u8"La sauvegarde #%1 est incompatible avec cette version de Mesen." }, { "SaveStateInvalidFile", u8"Fichier de sauvegarde invalide ou corrompu." }, { "SaveStateLoaded", u8"Sauvegarde #%1 chargée." }, + { "SaveStateMissingRom", u8"Le rom (%1) correspondant à la sauvegarde rapide sélectionnée est introuvable." }, { "SaveStateNewerVersion", u8"Impossible de charger une sauvegarde qui a été créée avec une version plus récente de Mesen. Veuillez mettre à jour Mesen." }, { "SaveStateSaved", u8"Sauvegarde #%1 sauvegardée." }, { "SaveStateSlotSelected", u8"Position de sauvegarde #%1 choisie." }, @@ -192,6 +194,7 @@ std::unordered_map MessageManager::_jaResources = { { "SaveStateIncompatibleVersion", u8"クイックセーブ%1は古いMesenのバージョンで作られたもので、ロードできませんでした。" }, { "SaveStateInvalidFile", u8"クイックセーブデータを読めませんでした。" }, { "SaveStateLoaded", u8"クイックセーブ%1をロードしました。" }, + { "SaveStateMissingRom", u8"クイックセーブデータをロードするためのゲームファイルを見つかりませんでした。(%1)" }, { "SaveStateNewerVersion", u8"クイックセーブデータは使用中のMesenより新しいバージョンで作られたため、ロードできません。 Mesenのサイトで最新のバージョンをダウンロードしてください。" }, { "SaveStateSaved", u8"クイックセーブ%1をセーブしました。" }, { "SaveStateSlotSelected", u8"クイックセーブスロット%1。" }, @@ -261,6 +264,7 @@ std::unordered_map MessageManager::_ruResources = { { "SaveStateIncompatibleVersion", u8"Сохранение #%1 несовместимо с вашей версией Mesen." }, { "SaveStateInvalidFile", u8"Некорректное сохранение." }, { "SaveStateLoaded", u8"Сохранение #%1 загружено." }, + { "SaveStateMissingRom", u8"Missing ROM required (%1) to load save state." }, { "SaveStateNewerVersion", u8"Сохранение создано в более новой версии Mesen. Пожалуйста загрузите последнюю версию." }, { "SaveStateSaved", u8"Сохранено в #%1 слот." }, { "ScanlineTimingWarning", u8"Тайминг PPU был изменён." }, @@ -330,9 +334,10 @@ std::unordered_map MessageManager::_esResources = { { "SaveStateIncompatibleVersion", u8"Partida guardada #%1 incompatible con esta versión de Mesen." }, { "SaveStateInvalidFile", u8"Partida guardada no válida." }, { "SaveStateLoaded", u8"Partida #%1 cargada." }, + { "SaveStateMissingRom", u8"Missing ROM required (%1) to load save state." }, { "SaveStateNewerVersion", u8"No se puede cargar una partida creada con una versión mas reciente de Mesen. Por favor descargue la última versión." }, { "SaveStateSaved", u8"Partida #%1 guardada." }, - { "SaveStateSlotSelected", u8"Espacio de guardado #%1 elegido." }, + { "SaveStateSlotSelected", u8"Espacio de guardado #%1 elegido." }, { "ScanlineTimingWarning", u8"El timing de PPU ha sido cambiado." }, { "ServerStarted", u8"Servidor iniciado (Puerto: %1)" }, { "ServerStopped", u8"Servidor detenido" }, @@ -400,6 +405,7 @@ std::unordered_map MessageManager::_ukResources = { { "SaveStateIncompatibleVersion", u8"Збереження #%1 несумісне з вашою версією Mesen." }, { "SaveStateInvalidFile", u8"Некоректне збереження." }, { "SaveStateLoaded", u8"Збереження #%1 завантажено." }, + { "SaveStateMissingRom", u8"Missing ROM required (%1) to load save state." }, { "SaveStateNewerVersion", u8"Збереження створено в більш нової версії Mesen. Будь ласка завантажте останню версію." }, { "SaveStateSaved", u8"Збережено в #%1 слот." }, { "ScanlineTimingWarning", u8"Таймiнг PPU був змінений." }, @@ -469,6 +475,7 @@ std::unordered_map MessageManager::_ptResources = { { "SaveStateIncompatibleVersion", u8"Save State #%1 incompatível com esta versão de Mesen." }, { "SaveStateInvalidFile", u8"Save State inválido." }, { "SaveStateLoaded", u8"Save State #%1 carregado." }, + { "SaveStateMissingRom", u8"Missing ROM required (%1) to load save state." }, { "SaveStateNewerVersion", u8"Não se pode carregar um save state com uma versão mais recente de Mesen. Por favor baixe a última versão." }, { "SaveStateSaved", u8"Save State #%1 salvo." }, { "SaveStateSlotSelected", u8"Slot do Save State #%1 elegido." }, @@ -538,6 +545,7 @@ std::unordered_map MessageManager::_caResources = { { "SaveStateIncompatibleVersion", u8"La partida guardada nº%1 és incompatible amb aquesta versió de Mesen." }, { "SaveStateInvalidFile", u8"Fitxer de partida guardada invàlid." }, { "SaveStateLoaded", u8"Partida guardada nº%1 carregada." }, + { "SaveStateMissingRom", u8"Missing ROM required (%1) to load save state." }, { "SaveStateNewerVersion", u8"Incapaç de carregar partides guardades creades per una versió més recent de Mesen. Si us plau, descarregueu-vos la darrera versió de Mesen." }, { "SaveStateSaved", u8"Partida guardada nº%1 desada." }, { "SaveStateSlotSelected", u8"Partida guardada nº%1 seleccionada." }, diff --git a/Core/RomLoader.cpp b/Core/RomLoader.cpp index 9f0d5831..dd6fa21b 100644 --- a/Core/RomLoader.cpp +++ b/Core/RomLoader.cpp @@ -119,12 +119,8 @@ string RomLoader::FindMatchingRomInFile(string filePath, HashInfo hashInfo) return ""; } -string RomLoader::FindMatchingRomInFolder(string folder, string romFilename, HashInfo hashInfo, bool useFastSearch) +string RomLoader::FindMatchingRom(vector romFiles, string romFilename, HashInfo hashInfo, bool useFastSearch) { - std::transform(romFilename.begin(), romFilename.end(), romFilename.begin(), ::tolower); - std::unordered_set validExtensions = { { ".nes", ".fds", "*.unif", "*.unif", "*.nsf", "*.nsfe", "*.7z", "*.zip" } }; - vector romFiles = FolderUtilities::GetFilesInFolder(folder, validExtensions, true); - if(useFastSearch) { for(string romFile : romFiles) { //Quick search by filename diff --git a/Core/RomLoader.h b/Core/RomLoader.h index 4fa64e7d..ffe0c9f6 100644 --- a/Core/RomLoader.h +++ b/Core/RomLoader.h @@ -17,5 +17,5 @@ class RomLoader bool LoadFile(string filename, vector &fileData); RomData GetRomData(); - static string FindMatchingRomInFolder(string folder, string romFilename, HashInfo hashInfo, bool useFastSearch); + static string FindMatchingRom(vector romFiles, string romFilename, HashInfo hashInfo, bool useFastSearch); }; diff --git a/Core/SaveStateManager.cpp b/Core/SaveStateManager.cpp index 15f3682b..5f85292c 100644 --- a/Core/SaveStateManager.cpp +++ b/Core/SaveStateManager.cpp @@ -61,27 +61,42 @@ void SaveStateManager::SaveState(ostream &stream) stream.write((char*)&emuVersion, sizeof(emuVersion)); stream.write((char*)&SaveStateManager::FileFormatVersion, sizeof(uint32_t)); + string sha1Hash = Console::GetHashInfo().Sha1Hash; + stream.write(sha1Hash.c_str(), sha1Hash.size()); + + string romName = Console::GetRomName(); + uint32_t nameLength = (uint32_t)romName.size(); + stream.write((char*)&nameLength, sizeof(uint32_t)); + stream.write(romName.c_str(), romName.size()); + Console::SaveState(stream); Console::Resume(); } +bool SaveStateManager::SaveState(string filepath) +{ + ofstream file(filepath, ios::out | ios::binary); + + if(file) { + SaveState(file); + file.close(); + return true; + } + return false; +} + void SaveStateManager::SaveState(int stateIndex, bool displayMessage) { string filepath = SaveStateManager::GetStateFilepath(stateIndex); - ofstream file(filepath, ios::out | ios::binary); - - if(file) { + if(SaveState(filepath)) { _lastIndex = stateIndex; - SaveState(file); - file.close(); - if(displayMessage) { MessageManager::DisplayMessage("SaveStates", "SaveStateSaved", std::to_string(stateIndex)); } } } -bool SaveStateManager::LoadState(istream &stream) +bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired) { char header[3]; stream.read(header, 3); @@ -95,9 +110,35 @@ bool SaveStateManager::LoadState(istream &stream) } stream.read((char*)&fileFormatVersion, sizeof(fileFormatVersion)); - if(fileFormatVersion != SaveStateManager::FileFormatVersion) { - MessageManager::DisplayMessage("SaveStates", "SaveStateIncompatibleVersion"); // , std::to_string(stateIndex)); + if(fileFormatVersion < 5) { + MessageManager::DisplayMessage("SaveStates", "SaveStateIncompatibleVersion"); return false; + } else if(fileFormatVersion == 5) { + //No SHA1 field in version 5 + if(hashCheckRequired) { + MessageManager::DisplayMessage("SaveStates", "SaveStateIncompatibleVersion"); + return false; + } + } else { + char hash[41] = {}; + stream.read(hash, 40); + + uint32_t nameLength = 0; + stream.read((char*)&nameLength, sizeof(uint32_t)); + + vector nameBuffer(nameLength); + stream.read(nameBuffer.data(), nameBuffer.size()); + string romName(nameBuffer.data()); + + if(Console::GetHashInfo().Sha1Hash != string(hash)) { + //Wrong game + HashInfo info; + info.Sha1Hash = hash; + if(!Console::LoadROM(romName, info)) { + MessageManager::DisplayMessage("SaveStates", "SaveStateMissingRom", romName); + return false; + } + } } Console::Pause(); @@ -106,32 +147,36 @@ bool SaveStateManager::LoadState(istream &stream) return true; } - + MessageManager::DisplayMessage("SaveStates", "SaveStateInvalidFile"); return false; } +bool SaveStateManager::LoadState(string filepath, bool hashCheckRequired) +{ + ifstream file(filepath, ios::in | ios::binary); + bool result = false; + + if(file.good()) { + if(LoadState(file, hashCheckRequired)) { + result = true; + } + file.close(); + } else { + MessageManager::DisplayMessage("SaveStates", "SaveStateEmpty"); + } + + return result; +} + bool SaveStateManager::LoadState(int stateIndex) { string filepath = SaveStateManager::GetStateFilepath(stateIndex); - ifstream file(filepath, ios::in | ios::binary); - bool result = false; - - if(file) { - if(LoadState(file)) { - _lastIndex = stateIndex; - MessageManager::DisplayMessage("SaveStates", "SaveStateLoaded", std::to_string(stateIndex)); - result = true; - } else { - MessageManager::DisplayMessage("SaveStates", "SaveStateInvalidFile"); - } - file.close(); - } - - if(!result) { - MessageManager::DisplayMessage("SaveStates", "SaveStateEmpty"); + if(LoadState(filepath, true)) { + _lastIndex = stateIndex; + MessageManager::DisplayMessage("SaveStates", "SaveStateLoaded", std::to_string(stateIndex)); + return true; } - - return result; + return false; } void SaveStateManager::SaveRecentGame(string romName, string romPath, string patchPath) @@ -173,7 +218,7 @@ void SaveStateManager::LoadRecentGame(string filename, bool resetGame) try { Console::LoadROM(romPath, patchPath); if(!resetGame) { - SaveStateManager::LoadState(stateStream); + SaveStateManager::LoadState(stateStream, false); } } catch(std::exception ex) { Console::GetInstance()->Stop(); diff --git a/Core/SaveStateManager.h b/Core/SaveStateManager.h index 26363043..61d67e8f 100644 --- a/Core/SaveStateManager.h +++ b/Core/SaveStateManager.h @@ -11,7 +11,7 @@ private: static string GetStateFilepath(int stateIndex); public: - static const uint32_t FileFormatVersion = 5; + static const uint32_t FileFormatVersion = 6; static uint64_t GetStateInfo(int stateIndex); @@ -19,8 +19,10 @@ public: static bool LoadState(); static void SaveState(ostream &stream); + static bool SaveState(string filepath); static void SaveState(int stateIndex, bool displayMessage = true); - static bool LoadState(istream &stream); + static bool LoadState(istream &stream, bool hashCheckRequired = true); + static bool LoadState(string filepath, bool hashCheckRequired = true); static bool LoadState(int stateIndex); static void SaveRecentGame(string romName, string romPath, string patchPath); diff --git a/GUI.NET/Debugger/frmDebugger.Designer.cs b/GUI.NET/Debugger/frmDebugger.Designer.cs index b4c2f244..c7f343fa 100644 --- a/GUI.NET/Debugger/frmDebugger.Designer.cs +++ b/GUI.NET/Debugger/frmDebugger.Designer.cs @@ -1160,7 +1160,7 @@ namespace Mesen.GUI.Debugger // this.mnuTraceLogger.Image = global::Mesen.GUI.Properties.Resources.LogWindow; this.mnuTraceLogger.Name = "mnuTraceLogger"; - this.mnuTraceLogger.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.L))); + this.mnuTraceLogger.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N))); this.mnuTraceLogger.Size = new System.Drawing.Size(196, 22); this.mnuTraceLogger.Text = "Trace Logger"; this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click); diff --git a/GUI.NET/Dependencies/resources.ca.xml b/GUI.NET/Dependencies/resources.ca.xml index 94a615fc..fef9bfe2 100644 --- a/GUI.NET/Dependencies/resources.ca.xml +++ b/GUI.NET/Dependencies/resources.ca.xml @@ -586,6 +586,10 @@ Tots els formats suportats (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips, *.bps, *.ups)|*.NES;*.ZIP;*.7z;*.IPS;*.BPS;*.UPS;*.FDS;*.NSF;*.NSFE;*.UNF|Roms de NES(*.nes, *.unf)|*.NES;*.UNF|Roms de Famicom Disk System (*.fds)|*.FDS|Fitxers NSF (*.nsf, *.nsfe)|*.NSF;*.NSFE|Arxius comprimits (*.zip)|*.ZIP|Arxius 7-Zip (*.7z)|*.7z|Pedaços IPS/UPS/BPS (*.ips, *.bps, *.ups)|*.IPS;*.BPS;*.UPS|Tots els fitxers (*.*)|*.* Fitxers de proves (*.mtp)|*.mtp|Tots els fitxers (*.*)|*.* Tots els formats suportats (*.cht, *.xml)|*.cht;*.xml + Mesen Savestates (*.mst)|*.mst|Tots els fitxers (*.*)|*.* + + Load from file... + Save to file... Continua Pausa diff --git a/GUI.NET/Dependencies/resources.en.xml b/GUI.NET/Dependencies/resources.en.xml index 31cf27e0..302842d7 100644 --- a/GUI.NET/Dependencies/resources.en.xml +++ b/GUI.NET/Dependencies/resources.en.xml @@ -10,6 +10,10 @@ All supported formats (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips, *.bps, *.ups)|*.NES;*.ZIP;*.7z;*.IPS;*.BPS;*.UPS;*.FDS;*.NSF;*.NSFE;*.UNF|NES Roms (*.nes, *.unf)|*.NES;*.UNF|Famicom Disk System Roms (*.fds)|*.FDS|NSF files (*.nsf, *.nsfe)|*.nsf;*.nsfe|ZIP Archives (*.zip)|*.ZIP|7-Zip Archives (*.7z)|*.7z|IPS/UPS/BPS Patches (*.ips, *.bps, *.ups)|*.IPS;*.BPS;*.UPS|All (*.*)|*.* Test files (*.mtp)|*.mtp|All (*.*)|*.* All supported formats (*.cht, *.xml)|*.cht;*.xml + Mesen Savestates (*.mst)|*.mst|All files (*.*)|*.* + + Load from file... + Save to file... Resume Pause diff --git a/GUI.NET/Dependencies/resources.es.xml b/GUI.NET/Dependencies/resources.es.xml index 04780d22..732b2796 100644 --- a/GUI.NET/Dependencies/resources.es.xml +++ b/GUI.NET/Dependencies/resources.es.xml @@ -620,6 +620,10 @@ Todos los formatos soportados (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips, *.bps, *.ups)|*.NES;*.ZIP;*.7z;*.IPS;*.BPS;*.UPS;*.FDS;*.NSF;*.NSFE;*.UNF|Roms de NES(*.nes, *.unf)|*.NES;*.UNF|Roms de Famicom Disk System (*.fds)|*.FDS|Archivos NSF (*.nsf, *.nsfe)|*.NSF;*.NSFE|Archivos ZIP (*.zip)|*.ZIP|Archivos 7-Zip (*.7z)|*.7z|Archivos IPS/UPS/BPS (*.ips, *.bps, *.ups)|*.IPS;*.BPS;*.UPS|Todos los archivos (*.*)|*.* Archivos de test (*.mtp)|*.mtp|Todos los archivos (*.*)|*.* Todos los formatos soportados (*.cht, *.xml)|*.cht;*.xml + Mesen Savestates (*.mst)|*.mst|Todos los archivos (*.*)|*.* + + Load from file... + Save to file... Continuar Pausa diff --git a/GUI.NET/Dependencies/resources.fr.xml b/GUI.NET/Dependencies/resources.fr.xml index 8746bbf6..cc4b0d65 100644 --- a/GUI.NET/Dependencies/resources.fr.xml +++ b/GUI.NET/Dependencies/resources.fr.xml @@ -634,7 +634,11 @@ Tous les formats supportés (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips, *.bps, *.ups)|*.NES;*.ZIP;*.7z;*.IPS;*.BPS;*.UPS;*.FDS;*.NSF;*.NSFE;*.UNF|Roms de NES (*.nes, *.unf)|*.NES;*.UNF|Roms du Famicom Disk System (*.fds)|*.FDS|Fichiers NSF (*.nsf, *.nsfe)|*.NSF;*.NSFE|Fichiers ZIP (*.zip)|*.ZIP|Fichiers 7-Zip (*.7z)|*.7z|Fichiers IPS/UPS/BPS (*.ips, *.bps, *.ups)|*.IPS;*.BPS;*.UPS|Tous les fichiers (*.*)|*.* Fichiers de test (*.mtp)|*.mtp|Tous les fichiers (*.*)|*.* Tous les formats supportés (*.cht, *.xml)|*.cht;*.xml + Sauvegardes d'états Mesen (*.mst)|*.mst|Tous les fichiers (*.*)|*.* + Charger à partir d'un fichier... + Sauvegarder dans un fichier... + Continuer Pause Démarrer serveur diff --git a/GUI.NET/Dependencies/resources.ja.xml b/GUI.NET/Dependencies/resources.ja.xml index cf5b659d..f608b240 100644 --- a/GUI.NET/Dependencies/resources.ja.xml +++ b/GUI.NET/Dependencies/resources.ja.xml @@ -617,6 +617,10 @@ 対応するすべてのファイル (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips, *.bps, *.ups)|*.NES;*.ZIP;*.7z;*.IPS;*.BPS;*.UPS;*.FDS;*.NSF;*.NSFE;*.UNF|ファミコンゲーム (*.nes, *.unf)|*.NES;*.UNF|ファミコンディスクシステムのゲーム (*.fds)|*.FDS|NSFファイル (*.nsf, *.nsfe)|*.NSF;*.NSFE|ZIPファイル (*.zip)|*.ZIP|7-Zipファイル (*.7z)|*.7z|パッチファイル (*.ips, *.bps, *.ups)|*.IPS;*.BPS;*.UPS|すべてのファイル (*.*)|*.* テストファイル (*.mtp)|*.mtp|すべてのファイル (*.*)|*.* 対応するすべてのファイル (*.cht, *.xml)|*.cht;*.xml + Mesenのクイックセーブデータ (*.mst)|*.mst|すべてのファイル (*.*)|*.* + + ファイルからロードする… + ファイルに保存する… 再開 ポーズ diff --git a/GUI.NET/Dependencies/resources.pt.xml b/GUI.NET/Dependencies/resources.pt.xml index c75aaa15..480316eb 100644 --- a/GUI.NET/Dependencies/resources.pt.xml +++ b/GUI.NET/Dependencies/resources.pt.xml @@ -620,6 +620,10 @@ Todos os formatos suportados (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips, *.bps, *.ups)|*.NES;*.ZIP;*.7z;*.IPS;*.BPS;*.UPS;*.FDS;*.NSF;*.NSFE;*.UNF|Roms de NES(*.nes, *.unf)|*.NES;*.UNF|Roms de Famicom Disk System (*.fds)|*.FDS|Arquivos NSF (*.nsf, *.nsfe)|*.NSF;*.NSFE|Arquivos ZIP (*.zip)|*.ZIP|Arquivos 7-Zip (*.7z)|*.7z|Arquivos IPS/UPS/BPS (*.ips, *.bps, *.ups)|*.IPS;*.BPS;*.UPS|Todos os arquivos (*.*)|*.* Arquivos de teste (*.mtp)|*.mtp|Todos os arquivos (*.*)|*.* Todos os formatos suportados (*.cht, *.xml)|*.cht;*.xml + Mesen Savestates (*.mst)|*.mst|Todos os arquivos (*.*)|*.* + + Load from file... + Save to file... Continuar Pausar diff --git a/GUI.NET/Dependencies/resources.ru.xml b/GUI.NET/Dependencies/resources.ru.xml index e94bee0e..dc58dce5 100644 --- a/GUI.NET/Dependencies/resources.ru.xml +++ b/GUI.NET/Dependencies/resources.ru.xml @@ -624,7 +624,11 @@ Все поддерживаемые форматы (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips, *.bps, *.ups)|*.NES;*.ZIP;*.7z;*.IPS;*.BPS;*.UPS;*.FDS;*.NSF;*.NSFE;*.UNF|NES Roms (*.nes, *.unf)|*.NES;*.UNF|Famicom Disk System Roms (*.fds)|*.FDS|NSF files (*.nsf, *.nsfe)|*.nsf;*.nsfe|ZIP Archives (*.zip)|*.ZIP|7-Zip Archives (*.7z)|*.7z|IPS/UPS/BPS Patches (*.ips, *.bps, *.ups)|*.IPS;*.BPS;*.UPS|All (*.*)|*.* Test files (*.mtp)|*.mtp|All (*.*)|*.* Все поддерживаемые форматы (*.cht, *.xml)|*.cht;*.xml + Mesen Savestates (*.mst)|*.mst|All Files (*.*)|*.* + Load from file... + Save to file... + Продолжить Пауза Запустить сервер diff --git a/GUI.NET/Dependencies/resources.uk.xml b/GUI.NET/Dependencies/resources.uk.xml index e6d49a02..5b6d0fec 100644 --- a/GUI.NET/Dependencies/resources.uk.xml +++ b/GUI.NET/Dependencies/resources.uk.xml @@ -624,7 +624,11 @@ Всі підтримувані формати (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips, *.bps, *.ups)|*.NES;*.ZIP;*.7z;*.IPS;*.BPS;*.UPS;*.FDS;*.NSF;*.NSFE;*.UNF|NES Roms (*.nes, *.unf)|*.NES;*.UNF|Famicom Disk System Roms (*.fds)|*.FDS|NSF files (*.nsf, *.nsfe)|*.nsf;*.nsfe|ZIP Archives (*.zip)|*.ZIP|7-Zip Archives (*.7z)|*.7z|IPS/UPS/BPS Patches (*.ips, *.bps, *.ups)|*.IPS;*.BPS;*.UPS|All (*.*)|*.* Test files (*.mtp)|*.mtp|All (*.*)|*.* Всі підтримувані формати (*.cht, *.xml)|*.cht;*.xml + Mesen Savestates (*.mst)|*.mst|All Files (*.*)|*.* + Load from file... + Save to file... + Продовжити Пауза Запустити сервер diff --git a/GUI.NET/Forms/frmMain.Designer.cs b/GUI.NET/Forms/frmMain.Designer.cs index 49a628fc..f87fe212 100644 --- a/GUI.NET/Forms/frmMain.Designer.cs +++ b/GUI.NET/Forms/frmMain.Designer.cs @@ -1507,7 +1507,7 @@ namespace Mesen.GUI.Forms // this.mnuTraceLogger.Image = global::Mesen.GUI.Properties.Resources.LogWindow; this.mnuTraceLogger.Name = "mnuTraceLogger"; - this.mnuTraceLogger.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.L))); + this.mnuTraceLogger.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N))); this.mnuTraceLogger.Size = new System.Drawing.Size(196, 22); this.mnuTraceLogger.Text = "Trace Logger"; this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click); diff --git a/GUI.NET/Forms/frmMain.File.cs b/GUI.NET/Forms/frmMain.File.cs index 025025e6..55526ac6 100644 --- a/GUI.NET/Forms/frmMain.File.cs +++ b/GUI.NET/Forms/frmMain.File.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Mesen.GUI.Config; +using Mesen.GUI.Properties; namespace Mesen.GUI.Forms { @@ -58,10 +59,48 @@ namespace Mesen.GUI.Forms if(!forSave) { menu.DropDownItems.Add("-"); addSaveStateInfo(NumberOfSaveSlots+1); + menu.DropDownItems.Add("-"); + ToolStripMenuItem loadFromFile = new ToolStripMenuItem(ResourceHelper.GetMessage("LoadFromFile"), Resources.FolderOpen); + loadFromFile.ShortcutKeys = Keys.Control | Keys.L; + loadFromFile.Click += LoadFromFile_Click; + menu.DropDownItems.Add(loadFromFile); + } else { + menu.DropDownItems.Add("-"); + ToolStripMenuItem saveToFile = new ToolStripMenuItem(ResourceHelper.GetMessage("SaveToFile"), Resources.Floppy); + saveToFile.ShortcutKeys = Keys.Control | Keys.S; + saveToFile.Click += SaveToFile_Click; + menu.DropDownItems.Add(saveToFile); } } } - + + private void LoadFromFile_Click(object sender, EventArgs e) + { + if(_emuThread != null) { + using(OpenFileDialog ofd = new OpenFileDialog()) { + ofd.InitialDirectory = ConfigManager.SaveStateFolder; + ofd.SetFilter(ResourceHelper.GetMessage("FilterSavestate")); + if(ofd.ShowDialog() == DialogResult.OK) { + InteropEmu.LoadStateFile(ofd.FileName); + } + } + } + } + + private void SaveToFile_Click(object sender, EventArgs e) + { + if(_emuThread != null) { + using(SaveFileDialog sfd = new SaveFileDialog()) { + sfd.InitialDirectory = ConfigManager.SaveStateFolder; + sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + ".mst"; + sfd.SetFilter(ResourceHelper.GetMessage("FilterSavestate")); + if(sfd.ShowDialog() == DialogResult.OK) { + InteropEmu.SaveStateFile(sfd.FileName); + } + } + } + } + private void mnuExit_Click(object sender, EventArgs e) { this.Close(); diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index b685e3f1..75671719 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -113,6 +113,8 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void SaveState(UInt32 stateIndex); [DllImport(DLLPath)] public static extern void LoadState(UInt32 stateIndex); + [DllImport(DLLPath)] public static extern void SaveStateFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filepath); + [DllImport(DLLPath)] public static extern void LoadStateFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filepath); [DllImport(DLLPath)] public static extern Int64 GetStateInfo(UInt32 stateIndex); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsNsf(); diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index 860cdb72..e1bba22e 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -312,7 +312,9 @@ namespace InteropEmu { } DllExport void __stdcall SaveState(uint32_t stateIndex) { SaveStateManager::SaveState(stateIndex); } - DllExport uint32_t __stdcall LoadState(uint32_t stateIndex) { return SaveStateManager::LoadState(stateIndex); } + DllExport void __stdcall LoadState(uint32_t stateIndex) { SaveStateManager::LoadState(stateIndex); } + DllExport void __stdcall SaveStateFile(char* filepath) { SaveStateManager::SaveState(filepath); } + DllExport void __stdcall LoadStateFile(char* filepath) { SaveStateManager::LoadState(filepath); } DllExport int64_t __stdcall GetStateInfo(uint32_t stateIndex) { return SaveStateManager::GetStateInfo(stateIndex); } DllExport void __stdcall MoviePlay(char* filename) { MovieManager::Play(filename); } diff --git a/Utilities/FolderUtilities.cpp b/Utilities/FolderUtilities.cpp index 59c1b9c9..dc87a854 100644 --- a/Utilities/FolderUtilities.cpp +++ b/Utilities/FolderUtilities.cpp @@ -37,7 +37,7 @@ void FolderUtilities::AddKnownGameFolder(string gameFolder) for(string folder : _gameFolders) { std::transform(folder.begin(), folder.end(), folder.begin(), ::tolower); - if(folder.compare(gameFolder) == 0) { + if(folder.compare(lowerCaseFolder) == 0) { alreadyExists = true; break; } @@ -131,8 +131,13 @@ vector FolderUtilities::GetFolders(string rootFolder) } for(fs::recursive_directory_iterator i(fs::u8path(rootFolder)), end; i != end; i++) { - if(fs::is_directory(i->path())) { - folders.push_back(i->path().u8string()); + if(i.depth() > 1) { + //Prevent excessive recursion + i.disable_recursion_pending(); + } else { + if(fs::is_directory(i->path())) { + folders.push_back(i->path().u8string()); + } } }