From 4455178da2b2da6489839274c098010d3f8a8c60 Mon Sep 17 00:00:00 2001 From: Souryo Date: Sun, 30 Jul 2017 09:03:54 -0400 Subject: [PATCH] Code refactoring (patch/archive handling) + allow HD packs to be loaded from archives --- Core/BaseMapper.cpp | 7 +- Core/BaseMapper.h | 2 - Core/BizhawkMovie.cpp | 5 +- Core/Console.cpp | 99 +++++--------- Core/Console.h | 15 +-- Core/Core.vcxproj | 2 - Core/Core.vcxproj.filters | 6 - Core/FdsLoader.h | 5 +- Core/FileLoader.cpp | 112 ---------------- Core/FileLoader.h | 15 --- Core/GameInformationMessage.h | 4 +- Core/GameServerConnection.cpp | 2 +- Core/HdPackBuilder.cpp | 22 +-- Core/HdPackLoader.cpp | 179 +++++++++++++++++++------ Core/HdPackLoader.h | 13 +- Core/MesenMovie.cpp | 8 +- Core/RecordedRomTest.cpp | 6 +- Core/RomData.h | 1 + Core/RomLoader.cpp | 74 +++++----- Core/RomLoader.h | 7 +- Core/SaveStateManager.cpp | 8 +- Core/SaveStateManager.h | 2 +- Core/VsControlManager.h | 2 +- GUI.NET/Config/Configuration.cs | 21 ++- GUI.NET/Controls/ctrlRecentGames.cs | 5 +- GUI.NET/Debugger/frmDebugger.cs | 2 +- GUI.NET/Forms/Cheats/frmCheat.cs | 6 +- GUI.NET/Forms/Cheats/frmCheatImport.cs | 6 +- GUI.NET/Forms/ResourcePath.cs | 45 +++++++ GUI.NET/Forms/frmMain.cs | 110 +++++++-------- GUI.NET/Forms/frmSelectRom.cs | 32 ++--- GUI.NET/GUI.NET.csproj | 1 + GUI.NET/InteropEmu.cs | 6 +- InteropDLL/ConsoleWrapper.cpp | 20 +-- PGOHelper/PGOHelper.cpp | 6 +- TestHelper/TestHelper.cpp | 5 +- Utilities/ArchiveReader.cpp | 35 ++++- Utilities/ArchiveReader.h | 6 +- Utilities/BpsPatcher.cpp | 173 ++++++++++++------------ Utilities/BpsPatcher.h | 5 +- Utilities/FolderUtilities.cpp | 5 +- Utilities/FolderUtilities.h | 3 +- Utilities/IpsPatcher.cpp | 103 +++++++------- Utilities/IpsPatcher.h | 3 +- Utilities/PNGHelper.cpp | 42 +++--- Utilities/PNGHelper.h | 1 + Utilities/SZReader.cpp | 11 +- Utilities/SZReader.h | 2 +- Utilities/UpsPatcher.cpp | 137 +++++++++---------- Utilities/UpsPatcher.h | 5 +- Utilities/Utilities.vcxproj | 1 + Utilities/Utilities.vcxproj.filters | 9 +- Utilities/VirtualFile.h | 155 +++++++++++++++++++++ Utilities/ZipReader.cpp | 13 +- Utilities/ZipReader.h | 2 +- Utilities/ZipWriter.cpp | 3 +- 56 files changed, 890 insertions(+), 685 deletions(-) delete mode 100644 Core/FileLoader.cpp delete mode 100644 Core/FileLoader.h create mode 100644 GUI.NET/Forms/ResourcePath.cs create mode 100644 Utilities/VirtualFile.h diff --git a/Core/BaseMapper.cpp b/Core/BaseMapper.cpp index 2f7aea9c..fed1e3b7 100644 --- a/Core/BaseMapper.cpp +++ b/Core/BaseMapper.cpp @@ -521,9 +521,9 @@ void BaseMapper::Initialize(RomData &romData) _gameSystem = romData.System; _hashInfo.Crc32Hash = romData.Crc32; + _hashInfo.PrgCrc32Hash = romData.PrgCrc32; _hashInfo.Sha1Hash = romData.Sha1; _hashInfo.PrgChrMd5Hash = romData.PrgChrMd5; - _prgCrc32 = romData.PrgCrc32; switch(romData.BusConflicts) { case BusConflictType::Default: _hasBusConflicts = HasBusConflicts(); break; case BusConflictType::Yes: _hasBusConflicts = true; break; @@ -717,11 +717,6 @@ HashInfo BaseMapper::GetHashInfo() return _hashInfo; } -uint32_t BaseMapper::GetPrgCrc32() -{ - return _prgCrc32; -} - MirroringType BaseMapper::GetMirroringType() { return _mirroringType; diff --git a/Core/BaseMapper.h b/Core/BaseMapper.h index 2f9d25c0..69ada421 100644 --- a/Core/BaseMapper.h +++ b/Core/BaseMapper.h @@ -46,7 +46,6 @@ private: uint32_t _chrPageNumbers[64]; HashInfo _hashInfo; - uint32_t _prgCrc32 = 0; vector _originalPrgRom; @@ -164,7 +163,6 @@ public: GameSystem GetGameSystem(); HashInfo GetHashInfo(); - uint32_t GetPrgCrc32(); string GetRomName(); RomFormat GetRomFormat(); diff --git a/Core/BizhawkMovie.cpp b/Core/BizhawkMovie.cpp index f63ef0d3..5968ff7b 100644 --- a/Core/BizhawkMovie.cpp +++ b/Core/BizhawkMovie.cpp @@ -81,8 +81,9 @@ bool BizhawkMovie::InitializeGameData(ZipReader & reader) std::getline(ss, line); if(line.compare(0, 4, "SHA1", 4) == 0) { if(line.size() >= 45) { - string sha1 = line.substr(5, 40); - if(Console::LoadROM("", sha1)) { + HashInfo hashInfo; + hashInfo.Sha1Hash = line.substr(5, 40); + if(Console::LoadROM("", hashInfo)) { result = true; } } diff --git a/Core/Console.cpp b/Core/Console.cpp index dff67e66..c27ffce2 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include #include "Console.h" -#include "FileLoader.h" #include "CPU.h" #include "PPU.h" #include "APU.h" @@ -19,6 +18,7 @@ #include "../Utilities/Timer.h" #include "../Utilities/FolderUtilities.h" #include "../Utilities/PlatformUtilities.h" +#include "../Utilities/VirtualFile.h" #include "HdBuilderPpu.h" #include "HdPpu.h" #include "NsfPpu.h" @@ -36,7 +36,6 @@ Console::Console() { _resetRequested = false; _lagCounter = 0; - _archiveFileIndex = -1; } Console::~Console() @@ -55,7 +54,7 @@ void Console::Release() Console::Instance.reset(new Console()); } -bool Console::Initialize(string romFilename, stringstream *filestream, string patchFilename, int32_t archiveFileIndex) +bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile) { SoundMixer::StopAudio(); @@ -64,26 +63,30 @@ bool Console::Initialize(string romFilename, stringstream *filestream, string pa _mapper->SaveBattery(); //Save current game state before loading another one - SaveStateManager::SaveRecentGame(_mapper->GetRomName(), _romFilepath, _patchFilename, _archiveFileIndex); + SaveStateManager::SaveRecentGame(_mapper->GetRomName(), _romFilepath, _patchFilename); } - vector fileData; - if(FileLoader::LoadFile(romFilename, filestream, archiveFileIndex, fileData)) { - LoadHdPack(romFilename, fileData, patchFilename); - if(!patchFilename.empty()) { - FileLoader::ApplyPatch(patchFilename, fileData); + if(romFile.IsValid()) { + LoadHdPack(romFile, patchFile); + if(patchFile.IsValid()) { + if(romFile.ApplyPatch(patchFile)) { + MessageManager::DisplayMessage("Patch", "ApplyingPatch", FolderUtilities::GetFilename(patchFile.GetFilePath(), true)); + } else { + //Patch failed + } } + vector fileData; + romFile.ReadFile(fileData); - shared_ptr mapper = MapperFactory::InitializeFromFile(romFilename, fileData); + shared_ptr mapper = MapperFactory::InitializeFromFile(romFile.GetFileName(), fileData); if(mapper) { if(_mapper) { //Send notification only if a game was already running and we successfully loaded the new one MessageManager::SendNotification(ConsoleNotificationType::GameStopped); } - _romFilepath = romFilename; - _patchFilename = patchFilename; - _archiveFileIndex = archiveFileIndex; + _romFilepath = romFile; + _patchFilename = patchFile; _autoSaveManager.reset(new AutoSaveManager()); VideoDecoder::GetInstance()->StopThread(); @@ -128,7 +131,7 @@ bool Console::Initialize(string romFilename, stringstream *filestream, string pa VideoDecoder::GetInstance()->StartThread(); - FolderUtilities::AddKnownGameFolder(FolderUtilities::GetFolderName(romFilename)); + FolderUtilities::AddKnownGameFolder(romFile.GetFolderPath()); string modelName = _model == NesModel::PAL ? "PAL" : (_model == NesModel::Dendy ? "Dendy" : "NTSC"); string messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")"; @@ -140,35 +143,21 @@ bool Console::Initialize(string romFilename, stringstream *filestream, string pa } } - MessageManager::DisplayMessage("Error", "CouldNotLoadFile", FolderUtilities::GetFilename(romFilename, true)); + MessageManager::DisplayMessage("Error", "CouldNotLoadFile", romFile.GetFileName()); return false; } -bool Console::LoadROM(string filepath, stringstream *filestream, int32_t archiveFileIndex, string patchFilepath) +bool Console::LoadROM(VirtualFile romFile, VirtualFile patchFile) { Console::Pause(); - bool result = Instance->Initialize(filepath, filestream, patchFilepath, archiveFileIndex); + bool result = Instance->Initialize(romFile, patchFile); Console::Resume(); return result; } -bool Console::LoadROM(string romName, uint32_t crc32Hash) -{ - HashInfo hashInfo; - hashInfo.Crc32Hash = crc32Hash; - return Console::LoadROM(romName, hashInfo); -} - -bool Console::LoadROM(string romName, string sha1Hash) -{ - HashInfo hashInfo; - hashInfo.Sha1Hash = sha1Hash; - return Console::LoadROM(romName, hashInfo); -} - bool Console::LoadROM(string romName, HashInfo hashInfo) { - string currentRomFilepath = Console::GetROMPath(); + string currentRomFilepath = Console::GetRomPath(); string currentFolder = FolderUtilities::GetFolderName(currentRomFilepath); if(!currentRomFilepath.empty()) { HashInfo gameHashInfo = Instance->_mapper->GetHashInfo(); @@ -178,28 +167,27 @@ bool Console::LoadROM(string romName, HashInfo hashInfo) } } - int32_t archiveFileIndex = -1; for(string folder : FolderUtilities::GetKnownGameFolders()) { - string match = RomLoader::FindMatchingRomInFolder(folder, romName, hashInfo, true, archiveFileIndex); + string match = RomLoader::FindMatchingRomInFolder(folder, romName, hashInfo, true); if(!match.empty()) { - return Console::LoadROM(match, nullptr, archiveFileIndex); + return Console::LoadROM(match); } } //Perform slow CRC32 search for ROM for(string folder : FolderUtilities::GetKnownGameFolders()) { - string match = RomLoader::FindMatchingRomInFolder(folder, romName, hashInfo, false, archiveFileIndex); + string match = RomLoader::FindMatchingRomInFolder(folder, romName, hashInfo, false); if(!match.empty()) { - return Console::LoadROM(match, nullptr, archiveFileIndex); + return Console::LoadROM(match); } } return false; } -string Console::GetROMPath() +string Console::GetRomPath() { - return Instance->_romFilepath; + return static_cast(Instance->_romFilepath).GetFilePath(); } string Console::GetRomName() @@ -229,24 +217,14 @@ bool Console::IsChrRam() } } -uint32_t Console::GetCrc32() +HashInfo Console::GetHashInfo() { if(Instance->_mapper) { - return Instance->_mapper->GetHashInfo().Crc32Hash; + return Instance->_mapper->GetHashInfo(); } else { - return 0; + return {}; } } - -uint32_t Console::GetPrgCrc32() -{ - if(Instance->_mapper) { - return Instance->_mapper->GetPrgCrc32(); - } else { - return 0; - } -} - NesModel Console::GetModel() { return Instance->_model; @@ -255,7 +233,7 @@ NesModel Console::GetModel() void Console::PowerCycle() { if(Instance->_initialized && !Instance->_romFilepath.empty()) { - LoadROM(Instance->_romFilepath, nullptr, Instance->_archiveFileIndex, Instance->_patchFilename); + LoadROM(Instance->_romFilepath, Instance->_patchFilename); } } @@ -274,7 +252,7 @@ void Console::Reset(bool softReset) Instance->ResetComponents(softReset); } else { //Full reset of all objects to ensure the emulator always starts in the exact same state - Instance->Initialize(Instance->_romFilepath); + LoadROM(Instance->_romFilepath, Instance->_patchFilename); } Console::Resume(); } @@ -458,7 +436,7 @@ void Console::Run() } if(!crashed) { - SaveStateManager::SaveRecentGame(_mapper->GetRomName(), _romFilepath, _patchFilename, _archiveFileIndex); + SaveStateManager::SaveRecentGame(_mapper->GetRomName(), _romFilepath, _patchFilename); } MessageManager::SendNotification(ConsoleNotificationType::GameStopped); @@ -641,20 +619,17 @@ HdPackData* Console::GetHdData() return Instance->_hdData.get(); } -void Console::LoadHdPack(string romFilename, vector &fileData, string &patchFilename) +void Console::LoadHdPack(VirtualFile &romFile, VirtualFile &patchFile) { _hdData.reset(); if(EmulationSettings::CheckFlag(EmulationFlags::UseHdPacks)) { - string hdPackFolder = FolderUtilities::CombinePath(FolderUtilities::GetHdPackFolder(), FolderUtilities::GetFilename(romFilename, false)); - string hdPackDefinitionFile = FolderUtilities::CombinePath(hdPackFolder, "hires.txt"); _hdData.reset(new HdPackData()); - if(!HdPackLoader::LoadHdNesPack(hdPackDefinitionFile, *_hdData.get())) { + if(!HdPackLoader::LoadHdNesPack(romFile, *_hdData.get())) { _hdData.reset(); } else { - string sha1hash = SHA1::GetHash(fileData); - auto result = _hdData->PatchesByHash.find(sha1hash); + auto result = _hdData->PatchesByHash.find(romFile.GetSha1Hash()); if(result != _hdData->PatchesByHash.end()) { - patchFilename = FolderUtilities::CombinePath(hdPackFolder, result->second); + patchFile = result->second; } } } diff --git a/Core/Console.h b/Core/Console.h index e8252090..a7de54fd 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -3,6 +3,7 @@ #include "stdafx.h" #include #include "../Utilities/SimpleLock.h" +#include "../Utilities/VirtualFile.h" #include "RomData.h" class Debugger; @@ -46,7 +47,6 @@ class Console string _romFilepath; string _patchFilename; - int32_t _archiveFileIndex; bool _stop = false; @@ -57,10 +57,10 @@ class Console bool _initialized = false; - void LoadHdPack(string romFilename, vector &fileData, string &patchFilename); + void LoadHdPack(VirtualFile &romFile, VirtualFile &patchFile); void ResetComponents(bool softReset); - bool Initialize(string filename, stringstream *filestream = nullptr, string patchFilename = "", int32_t archiveFileIndex = -1); + bool Initialize(VirtualFile &romFile, VirtualFile &patchFile); void UpdateNesModel(bool sendNotification); double GetFrameDelay(); @@ -86,16 +86,13 @@ class Console static void LoadState(istream &loadStream); static void LoadState(uint8_t *buffer, uint32_t bufferSize); - static bool LoadROM(string filepath, stringstream *filestream = nullptr, int32_t archiveFileIndex = -1, string patchFilepath = ""); + static bool LoadROM(VirtualFile romFile, VirtualFile patchFile = {}); static bool LoadROM(string romName, HashInfo hashInfo); - static bool LoadROM(string romName, uint32_t crc32Hash); - static bool LoadROM(string romName, string sha1Hash); - static string GetROMPath(); + static string GetRomPath(); static string GetRomName(); static bool IsChrRam(); static RomFormat GetRomFormat(); - static uint32_t GetCrc32(); - static uint32_t GetPrgCrc32(); + static HashInfo GetHashInfo(); static NesModel GetModel(); static uint32_t GetLagCounter(); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 01f6de57..4f427997 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -413,7 +413,6 @@ - @@ -779,7 +778,6 @@ - diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 060e1765..ef3a5be7 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1192,9 +1192,6 @@ VideoDecoder\HD - - Nes\RomLoader - @@ -1428,8 +1425,5 @@ VideoDecoder\HD - - Misc - \ No newline at end of file diff --git a/Core/FdsLoader.h b/Core/FdsLoader.h index a0330fba..50807ccc 100644 --- a/Core/FdsLoader.h +++ b/Core/FdsLoader.h @@ -152,7 +152,10 @@ public: { //Apply save data (saved as an IPS file), if found string fdsSaveFilepath = FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), FolderUtilities::GetFilename(filename, false) + ".ips"); - romFile = IpsPatcher::PatchBuffer(fdsSaveFilepath, romFile); + vector patchedData; + if(IpsPatcher::PatchBuffer(fdsSaveFilepath, romFile, patchedData)) { + romFile = patchedData; + } RomData romData; diff --git a/Core/FileLoader.cpp b/Core/FileLoader.cpp deleted file mode 100644 index 6a7dc56c..00000000 --- a/Core/FileLoader.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "stdafx.h" -#include "FileLoader.h" -#include "../Utilities/ZipReader.h" -#include "../Utilities/SZReader.h" -#include "../Utilities/BpsPatcher.h" -#include "../Utilities/IpsPatcher.h" -#include "../Utilities/UpsPatcher.h" -#include "../Utilities/FolderUtilities.h" -#include "MessageManager.h" - -void FileLoader::ReadFile(istream &file, vector &fileData) -{ - file.seekg(0, ios::end); - uint32_t fileSize = (uint32_t)file.tellg(); - file.seekg(0, ios::beg); - - fileData = vector(fileSize, 0); - file.read((char*)fileData.data(), fileSize); -} - -bool FileLoader::LoadFromArchive(istream &zipFile, ArchiveReader &reader, int32_t archiveFileIndex, vector &fileData) -{ - ReadFile(zipFile, fileData); - reader.LoadArchive(fileData.data(), fileData.size()); - - vector fileList = reader.GetFileList({ ".nes", ".fds", ".nsf", ".nsfe", ".unf" }); - if(fileList.empty() || archiveFileIndex > (int32_t)fileList.size()) { - return false; - } - - if(archiveFileIndex == -1) { - archiveFileIndex = 0; - } - reader.ExtractFile(fileList[archiveFileIndex], fileData); - return true; -} - -bool FileLoader::LoadFile(string filename, istream *filestream, int32_t archiveFileIndex, vector &fileData) -{ - ifstream file; - istream* input = nullptr; - if(!filestream) { - file.open(filename, ios::in | ios::binary); - if(file) { - input = &file; - } - } else { - input = filestream; - } - - if(input) { - char header[15]; - input->seekg(0, ios::beg); - input->read(header, 15); - input->seekg(0, ios::beg); - if(memcmp(header, "PK", 2) == 0) { - ZipReader reader; - return LoadFromArchive(*input, reader, archiveFileIndex, fileData); - } else if(memcmp(header, "7z", 2) == 0) { - SZReader reader; - return LoadFromArchive(*input, reader, archiveFileIndex, fileData); - } else { - if(archiveFileIndex > 0) { - return false; - } - - ReadFile(*input, fileData); - return true; - } - } - return false; -} - -void FileLoader::ApplyPatch(string patchPath, vector &fileData) -{ - //Apply patch file - MessageManager::DisplayMessage("Patch", "ApplyingPatch", FolderUtilities::GetFilename(patchPath, true)); - ifstream patchFile(patchPath, ios::binary | ios::in); - if(patchFile.good()) { - char buffer[5] = {}; - patchFile.read(buffer, 5); - patchFile.close(); - if(memcmp(buffer, "PATCH", 5) == 0) { - fileData = IpsPatcher::PatchBuffer(patchPath, fileData); - } else if(memcmp(buffer, "UPS1", 4) == 0) { - fileData = UpsPatcher::PatchBuffer(patchPath, fileData); - } else if(memcmp(buffer, "BPS1", 4) == 0) { - fileData = BpsPatcher::PatchBuffer(patchPath, fileData); - } - } -} - -vector FileLoader::GetArchiveRomList(string filename) -{ - ifstream in(filename, ios::in | ios::binary); - if(in) { - uint8_t header[2]; - in.read((char*)header, 2); - in.close(); - - if(memcmp(header, "PK", 2) == 0) { - ZipReader reader; - reader.LoadArchive(filename); - return reader.GetFileList({ ".nes", ".fds", ".nsf", ".nsfe", "*.unf" }); - } else if(memcmp(header, "7z", 2) == 0) { - SZReader reader; - reader.LoadArchive(filename); - return reader.GetFileList({ ".nes", ".fds", ".nsf", ".nsfe", "*.unf" }); - } - } - return{}; -} diff --git a/Core/FileLoader.h b/Core/FileLoader.h deleted file mode 100644 index 72bc92b5..00000000 --- a/Core/FileLoader.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "stdafx.h" -#include "../Utilities/ArchiveReader.h" - -class FileLoader -{ -private: - static void ReadFile(istream &file, vector &fileData); - static bool LoadFromArchive(istream &zipFile, ArchiveReader& reader, int32_t archiveFileIndex, vector &fileData); - -public: - static bool LoadFile(string filename, istream *filestream, int32_t archiveFileIndex, vector &fileData); - static void ApplyPatch(string patchPath, vector &fileData); - static vector GetArchiveRomList(string filename); -}; \ No newline at end of file diff --git a/Core/GameInformationMessage.h b/Core/GameInformationMessage.h index ebd488d0..5e5ac3ed 100644 --- a/Core/GameInformationMessage.h +++ b/Core/GameInformationMessage.h @@ -39,7 +39,9 @@ public: { string filename = _romFilename; if(filename.size() > 0) { - if(Console::LoadROM(filename, _crc32Hash)) { + HashInfo hashInfo; + hashInfo.Crc32Hash = _crc32Hash; + if(Console::LoadROM(filename, hashInfo)) { return true; } else { MessageManager::DisplayMessage("NetPlay", "CouldNotFindRom"); diff --git a/Core/GameServerConnection.cpp b/Core/GameServerConnection.cpp index 472451b4..18557612 100644 --- a/Core/GameServerConnection.cpp +++ b/Core/GameServerConnection.cpp @@ -38,7 +38,7 @@ GameServerConnection::~GameServerConnection() void GameServerConnection::SendGameInformation() { Console::Pause(); - GameInformationMessage gameInfo(Console::GetRomName(), Console::GetCrc32(), _controllerPort, EmulationSettings::CheckFlag(EmulationFlags::Paused)); + GameInformationMessage gameInfo(Console::GetRomName(), Console::GetHashInfo().Crc32Hash, _controllerPort, EmulationSettings::CheckFlag(EmulationFlags::Paused)); SendNetMessage(gameInfo); SaveStateMessage saveState; SendNetMessage(saveState); diff --git a/Core/HdPackBuilder.cpp b/Core/HdPackBuilder.cpp index 9d927981..d33ebe83 100644 --- a/Core/HdPackBuilder.cpp +++ b/Core/HdPackBuilder.cpp @@ -1,5 +1,6 @@ #include #include "stdafx.h" +#include "../Utilities/VirtualFile.h" #include "HdPackBuilder.h" HdPackBuilder* HdPackBuilder::_instance = nullptr; @@ -197,6 +198,7 @@ void HdPackBuilder::SaveHdPack() int pngIndex = 0; ss << "100" << std::endl; ss << "" << _hdData.Scale << std::endl; + ss << "" << Console::GetRomName() << std::endl; int tileDimension = 8 * _hdData.Scale; int pngDimension = 16 * tileDimension; @@ -238,26 +240,6 @@ void HdPackBuilder::SaveHdPack() }; for(std::pair>> &kvp : _tilesByChrBankByPalette) { - /*if(true) { //flatten palette - for(int i = 0; i < 256; i++) { - auto readItt = kvp.second.begin(); - auto writeItt = kvp.second.begin(); - while(writeItt != kvp.second.end() && writeItt->second[i]) { - readItt++; - writeItt++; - } - for(; readItt != kvp.second.end() && writeItt != kvp.second.end(); readItt++) { - if(writeItt->second[i] == nullptr && readItt->second[i] != nullptr) { - writeItt->second[i] = readItt->second[i]; - readItt->second[i] = nullptr; - while(writeItt != kvp.second.end() && writeItt->second[i]) { - writeItt++; - } - } - } - } - }*/ - if(_flags & HdPackRecordFlags::SortByUsageFrequency) { for(int i = 0; i < 256; i++) { vector> tiles; diff --git a/Core/HdPackLoader.cpp b/Core/HdPackLoader.cpp index b5d961f2..3c1c2458 100644 --- a/Core/HdPackLoader.cpp +++ b/Core/HdPackLoader.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include #include +#include "../Utilities/ZipReader.h" #include "../Utilities/FolderUtilities.h" #include "../Utilities/StringUtilities.h" #include "../Utilities/HexUtilities.h" @@ -8,32 +9,110 @@ #include "Console.h" #include "HdPackLoader.h" -HdPackLoader::HdPackLoader(string hdPackDefinitionFile, HdPackData *data) +HdPackLoader::HdPackLoader() { - _hdPackDefinitionFile = hdPackDefinitionFile; - _hdPackFolder = FolderUtilities::GetFolderName(_hdPackDefinitionFile); - _data = data; } -bool HdPackLoader::LoadHdNesPack(string hdPackDefinitionFile, HdPackData &outData) +bool HdPackLoader::InitializeLoader(VirtualFile &romFile, HdPackData *data) { - //outData = HdPackData(); + _data = data; - HdPackLoader loader(hdPackDefinitionFile, &outData); - return loader.LoadPack(); + string romName = FolderUtilities::GetFilename(romFile.GetFileName(), false); + string hdPackFolder = FolderUtilities::GetHdPackFolder(); + string zipName = romName + ".hdn"; + string definitionPath = FolderUtilities::CombinePath(romName, "hires.txt"); + + string legacyPath = FolderUtilities::CombinePath(hdPackFolder, definitionPath); + if(ifstream(legacyPath)) { + _loadFromZip = false; + _hdPackFolder = FolderUtilities::GetFolderName(legacyPath); + return true; + } else { + vector hdnPackages = FolderUtilities::GetFilesInFolder(romFile.GetFolderPath(), { ".hdn" }, false); + vector more = FolderUtilities::GetFilesInFolder(hdPackFolder, { ".hdn", ".zip" }, false); + hdnPackages.insert(hdnPackages.end(), more.begin(), more.end()); + + string sha1Hash = romFile.GetSha1Hash(); + for(string path : hdnPackages) { + _reader.LoadArchive(path); + + vector hdDefinition; + if(_reader.ExtractFile("hires.txt", hdDefinition)) { + if(FolderUtilities::GetFilename(path, false) == romName) { + _loadFromZip = true; + _hdPackFolder = path; + return true; + } else { + for(string line : StringUtilities::Split(string(hdDefinition.data(), hdDefinition.data() + hdDefinition.size()), '\n')) { + std::transform(line.begin(), line.end(), line.begin(), ::tolower); + if(line.find("") != string::npos && line.find(sha1Hash) != string::npos) { + _loadFromZip = true; + _hdPackFolder = path; + return true; + } + } + } + } + } + } + return false; +} + +bool HdPackLoader::LoadHdNesPack(string definitionFile, HdPackData &outData) +{ + HdPackLoader loader; + if(ifstream(definitionFile)) { + loader._data = &outData; + loader._loadFromZip = false; + loader._hdPackFolder = FolderUtilities::GetFolderName(definitionFile); + return loader.LoadPack(); + } + return false; +} + +bool HdPackLoader::LoadHdNesPack(VirtualFile &romFile, HdPackData &outData) +{ + HdPackLoader loader; + if(loader.InitializeLoader(romFile, &outData)) { + return loader.LoadPack(); + } + return false; +} + +bool HdPackLoader::LoadFile(string filename, vector &fileData) +{ + fileData.clear(); + + if(_loadFromZip) { + if(_reader.ExtractFile(filename, fileData)) { + return true; + } + } else { + ifstream file(FolderUtilities::CombinePath(_hdPackFolder, filename), ios::in | ios::binary); + if(file.good()) { + file.seekg(0, ios::end); + uint32_t fileSize = (uint32_t)file.tellg(); + file.seekg(0, ios::beg); + + fileData = vector(fileSize, 0); + file.read((char*)fileData.data(), fileSize); + + return true; + } + } + + return false; } bool HdPackLoader::LoadPack() { try { - ifstream packDefinition(_hdPackDefinitionFile, ios::in | ios::binary); - if(!packDefinition.good()) { + vector hdDefinition; + if(!LoadFile("hires.txt", hdDefinition)) { return false; } - while(packDefinition.good()) { - string lineContent; - std::getline(packDefinition, lineContent); + for(string lineContent : StringUtilities::Split(string(hdDefinition.data(), hdDefinition.data() + hdDefinition.size()), '\n')) { lineContent = lineContent.substr(0, lineContent.length() - 1); vector conditions; @@ -51,10 +130,9 @@ bool HdPackLoader::LoadPack() _data->Scale = std::stoi(lineContent); } else if(lineContent.substr(0, 5) == "") { lineContent = lineContent.substr(5); - HdPackBitmapInfo bitmapInfo; - string imageFile = FolderUtilities::CombinePath(_hdPackFolder, lineContent); - PNGHelper::ReadPNG(imageFile, bitmapInfo.PixelData, bitmapInfo.Width, bitmapInfo.Height); - _hdNesBitmaps.push_back(bitmapInfo); + if(!ProcessImgTag(lineContent)) { + return false; + } } else if(lineContent.substr(0, 7) == "") { tokens = StringUtilities::Split(lineContent.substr(7), ','); ProcessPatchTag(tokens); @@ -76,7 +154,6 @@ bool HdPackLoader::LoadPack() LoadCustomPalette(); InitializeHdPack(); - packDefinition.close(); return true; } catch(std::exception ex) { MessageManager::Log(string("[HDPack] Error loading HDPack: ") + ex.what()); @@ -84,19 +161,38 @@ bool HdPackLoader::LoadPack() } } +bool HdPackLoader::ProcessImgTag(string src) +{ + HdPackBitmapInfo bitmapInfo; + vector fileData; + LoadFile(src, fileData); + if(PNGHelper::ReadPNG(fileData, bitmapInfo.PixelData, bitmapInfo.Width, bitmapInfo.Height)) { + _hdNesBitmaps.push_back(bitmapInfo); + return true; + } else { + MessageManager::Log("[HDPack] Error loading HDPack: PNG file " + src + " could not be read."); + return false; + } +} + void HdPackLoader::ProcessPatchTag(vector &tokens) { if(tokens[1].size() != 40) { MessageManager::Log(string("[HDPack] Invalid SHA1 hash for patch (" + tokens[0] + "): " + tokens[1])); return; } - if(!ifstream(FolderUtilities::CombinePath(_hdPackFolder, tokens[0]))) { + vector fileData; + if(!LoadFile(tokens[0], fileData)) { MessageManager::Log(string("[HDPack] Patch file not found: " + tokens[1])); return; } - std::transform(tokens[1].begin(), tokens[1].end(), tokens[1].begin(), ::toupper); - _data->PatchesByHash[tokens[1]] = tokens[0]; + std::transform(tokens[1].begin(), tokens[1].end(), tokens[1].begin(), ::tolower); + if(_loadFromZip) { + _data->PatchesByHash[tokens[1]] = VirtualFile(_hdPackFolder, tokens[0]); + } else { + _data->PatchesByHash[tokens[1]] = FolderUtilities::CombinePath(_hdPackFolder, tokens[0]); + } } void HdPackLoader::ProcessTileTag(vector &tokens, vector conditions) @@ -231,30 +327,32 @@ void HdPackLoader::ProcessConditionTag(vector &tokens) void HdPackLoader::ProcessBackgroundTag(vector &tokens, vector conditions) { - HdBackgroundFileData* fileData = nullptr; + HdBackgroundFileData* bgFileData = nullptr; for(unique_ptr &bgData : _data->BackgroundFileData) { if(bgData->PngName == tokens[0]) { - fileData = bgData.get(); + bgFileData = bgData.get(); } } - if(!fileData) { + if(!bgFileData) { vector pixelData; uint32_t width, height; - string imageFile = FolderUtilities::CombinePath(_hdPackFolder, tokens[0]); - if(PNGHelper::ReadPNG(imageFile, pixelData, width, height)) { - _data->BackgroundFileData.push_back(unique_ptr(new HdBackgroundFileData())); - fileData = _data->BackgroundFileData.back().get(); - fileData->PixelData = pixelData; - fileData->Width = width; - fileData->Height = height; - fileData->PngName = tokens[0]; + vector fileContent; + if(LoadFile(tokens[0], fileContent)) { + if(PNGHelper::ReadPNG(fileContent, pixelData, width, height)) { + _data->BackgroundFileData.push_back(unique_ptr(new HdBackgroundFileData())); + bgFileData = _data->BackgroundFileData.back().get(); + bgFileData->PixelData = pixelData; + bgFileData->Width = width; + bgFileData->Height = height; + bgFileData->PngName = tokens[0]; + } } } HdBackgroundInfo backgroundInfo; - if(fileData) { - backgroundInfo.Data = fileData; + if(bgFileData) { + backgroundInfo.Data = bgFileData; backgroundInfo.Brightness = (uint8_t)(std::stof(tokens[1]) * 255); backgroundInfo.Conditions = conditions; @@ -297,17 +395,12 @@ vector HdPackLoader::ParseConditionString(string conditionStri void HdPackLoader::LoadCustomPalette() { - string customPalettePath = FolderUtilities::CombinePath(_hdPackFolder, "palette.dat"); - ifstream file(customPalettePath, ios::binary); - if(file.good()) { + vector fileData; + if(LoadFile("palette.dat", fileData)) { vector paletteData; - uint8_t rgb[3]; - while(!file.eof()) { - file.read((char*)rgb, 3); - if(!file.eof()) { - paletteData.push_back(0xFF000000 | (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]); - } + for(int i = 0; i < fileData.size(); i+= 3){ + paletteData.push_back(0xFF000000 | (fileData[i] << 16) | (fileData[i+1] << 8) | fileData[i+2]); } if(paletteData.size() == 0x40) { diff --git a/Core/HdPackLoader.h b/Core/HdPackLoader.h index bf00d36d..e2750e78 100644 --- a/Core/HdPackLoader.h +++ b/Core/HdPackLoader.h @@ -1,24 +1,33 @@ #pragma once #include "stdafx.h" #include "HdData.h" +#include "../Utilities/ZipReader.h" +#include "../Utilities/VirtualFile.h" class HdPackLoader { public: - static bool LoadHdNesPack(string hdPackDefinitionFile, HdPackData &data); + static bool LoadHdNesPack(string definitionFile, HdPackData &outData); + static bool LoadHdNesPack(VirtualFile &romFile, HdPackData &outData); private: HdPackData* _data; + bool _loadFromZip = false; + ZipReader _reader; string _hdPackDefinitionFile; string _hdPackFolder; vector _hdNesBitmaps; - HdPackLoader(string hdPackDefinitionFile, HdPackData *data); + HdPackLoader(); + + bool InitializeLoader(VirtualFile &romPath, HdPackData *data); + bool LoadFile(string filename, vector &fileData); bool LoadPack(); void InitializeHdPack(); void LoadCustomPalette(); + bool ProcessImgTag(string src); void ProcessPatchTag(vector &tokens); void ProcessConditionTag(vector &tokens); void ProcessTileTag(vector &tokens, vector conditions); diff --git a/Core/MesenMovie.cpp b/Core/MesenMovie.cpp index cc9b9155..d0480257 100644 --- a/Core/MesenMovie.cpp +++ b/Core/MesenMovie.cpp @@ -174,7 +174,7 @@ bool MesenMovie::Save() header.MesenVersion = EmulationSettings::GetMesenVersion(); header.MovieFormatVersion = MesenMovie::MovieFormatVersion; header.SaveStateFormatVersion = SaveStateManager::FileFormatVersion; - header.RomCrc32 = Console::GetCrc32(); + header.RomCrc32 = Console::GetHashInfo().Crc32Hash; header.Region = (uint32_t)Console::GetModel(); header.ConsoleType = (uint32_t)EmulationSettings::GetConsoleType(); header.ExpansionDevice = (uint32_t)EmulationSettings::GetExpansionDevice(); @@ -336,9 +336,11 @@ bool MesenMovie::Load(std::stringstream &file, bool autoLoadRom) bool loadedGame = true; if(autoLoadRom) { string currentRom = Console::GetRomName(); - if(currentRom.empty() || header.RomCrc32 != Console::GetCrc32()) { + if(currentRom.empty() || header.RomCrc32 != Console::GetHashInfo().Crc32Hash) { //Loaded game isn't the same as the game used for the movie, attempt to load the correct game - loadedGame = Console::LoadROM(romFilename, header.RomCrc32); + HashInfo hashInfo; + hashInfo.Crc32Hash = header.RomCrc32; + loadedGame = Console::LoadROM(romFilename, hashInfo); } else { Console::Reset(false); } diff --git a/Core/RecordedRomTest.cpp b/Core/RecordedRomTest.cpp index 8850c727..3354a75c 100644 --- a/Core/RecordedRomTest.cpp +++ b/Core/RecordedRomTest.cpp @@ -172,7 +172,7 @@ void RecordedRomTest::RecordFromTest(string newTestFilename, string existingTest if(testMovie && testRom) { Console::Pause(); - Console::LoadROM("TestRom", &testRom); + Console::LoadROM(testRom); testRom.seekg(0, ios::beg); _romStream << testRom.rdbuf(); @@ -236,7 +236,7 @@ int32_t RecordedRomTest::Run(string filename) _runningTest = true; //Start playing movie - Console::LoadROM(testName, &testRom); + Console::LoadROM(testRom); MovieManager::Play(testMovie, false); Console::Resume(); @@ -300,7 +300,7 @@ void RecordedRomTest::Save() writer.AddFile(mmoFilename, "TestMovie.mmo"); std::remove(mmoFilename.c_str()); - writer.AddFile(Console::GetROMPath(), "TestRom.nes"); + writer.AddFile(Console::GetRomPath(), "TestRom.nes"); } diff --git a/Core/RomData.h b/Core/RomData.h index 275e9b31..1185f30e 100644 --- a/Core/RomData.h +++ b/Core/RomData.h @@ -40,6 +40,7 @@ enum class BusConflictType struct HashInfo { uint32_t Crc32Hash = 0; + uint32_t PrgCrc32Hash = 0; string Sha1Hash; string PrgChrMd5Hash; }; diff --git a/Core/RomLoader.cpp b/Core/RomLoader.cpp index d3289a9e..9f0d5831 100644 --- a/Core/RomLoader.cpp +++ b/Core/RomLoader.cpp @@ -1,21 +1,23 @@ #include "stdafx.h" #include +#include +#include "../Utilities/VirtualFile.h" #include "../Utilities/FolderUtilities.h" #include "../Utilities/CRC32.h" #include "../Utilities/sha1.h" #include "RomLoader.h" -#include "FileLoader.h" #include "iNesLoader.h" #include "FdsLoader.h" #include "NsfLoader.h" #include "NsfeLoader.h" #include "UnifLoader.h" -bool RomLoader::LoadFile(string filename, int32_t archiveFileIndex) +bool RomLoader::LoadFile(VirtualFile romFile) { vector fileData; - if(FileLoader::LoadFile(filename, nullptr, archiveFileIndex, fileData)) { - return LoadFile(filename, fileData); + if(romFile.IsValid()) { + romFile.ReadFile(fileData); + return LoadFile(romFile.GetFileName(), fileData); } else { return false; } @@ -23,6 +25,10 @@ bool RomLoader::LoadFile(string filename, int32_t archiveFileIndex) bool RomLoader::LoadFile(string filename, vector &fileData) { + if(fileData.size() < 10) { + return false; + } + _filename = filename; string romName = FolderUtilities::GetFilename(filename, true); @@ -88,56 +94,58 @@ RomData RomLoader::GetRomData() return _romData; } -int32_t RomLoader::FindMatchingRomInFile(string filename, HashInfo hashInfo) +string RomLoader::FindMatchingRomInFile(string filePath, HashInfo hashInfo) { - vector fileData; - int fileIndex = 0; - while(FileLoader::LoadFile(filename, nullptr, fileIndex, fileData)) { - RomLoader loader; - if(loader.LoadFile(filename, fileData)) { - if(hashInfo.Crc32Hash == loader._romData.Crc32 || hashInfo.Sha1Hash.compare(loader._romData.Sha1) == 0) { - return fileIndex; + shared_ptr reader = ArchiveReader::GetReader(filePath); + if(reader) { + for(string file : reader->GetFileList({ ".nes", ".fds", "*.unif", "*.unif", "*.nsf", "*.nsfe" })) { + RomLoader loader; + vector fileData; + if(loader.LoadFile(filePath)) { + if(hashInfo.Crc32Hash == loader._romData.Crc32 || hashInfo.Sha1Hash.compare(loader._romData.Sha1) == 0) { + return filePath+"\n"+file; + } + } + } + } else { + RomLoader loader; + vector fileData; + if(loader.LoadFile(filePath)) { + if(hashInfo.Crc32Hash == loader._romData.Crc32 || hashInfo.Sha1Hash.compare(loader._romData.Sha1) == 0) { + return filePath; } - fileIndex++; } } - return -1; + return ""; } -string RomLoader::FindMatchingRomInFolder(string folder, string romFilename, HashInfo hashInfo, bool useFastSearch, int32_t &archiveFileIndex) +string RomLoader::FindMatchingRomInFolder(string folder, string romFilename, HashInfo hashInfo, bool useFastSearch) { std::transform(romFilename.begin(), romFilename.end(), romFilename.begin(), ::tolower); - vector validExtensions = { { ".nes", ".zip", ".7z", ".fds" } }; - vector romFiles; - - for(string extension : validExtensions) { - for(string file : FolderUtilities::GetFilesInFolder(folder, extension, true)) { - romFiles.push_back(file); - } - } + 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 - string originalFilename = romFile; - std::transform(romFile.begin(), romFile.end(), romFile.begin(), ::tolower); - if(FolderUtilities::GetFilename(romFile, true).compare(romFilename) == 0) { - archiveFileIndex = RomLoader::FindMatchingRomInFile(romFile, hashInfo); - if(archiveFileIndex >= 0) { - return originalFilename; + string lcRomFile = romFile; + std::transform(lcRomFile.begin(), lcRomFile.end(), lcRomFile.begin(), ::tolower); + if(FolderUtilities::GetFilename(lcRomFile, false).compare(FolderUtilities::GetFilename(romFilename, false)) == 0) { + string match = RomLoader::FindMatchingRomInFile(romFile, hashInfo); + if(!match.empty()) { + return match; } } } } else { for(string romFile : romFiles) { //Slower search by CRC value - archiveFileIndex = RomLoader::FindMatchingRomInFile(romFile, hashInfo); - if(archiveFileIndex >= 0) { - return romFile; + string match = RomLoader::FindMatchingRomInFile(romFile, hashInfo); + if(!match.empty()) { + return match; } } } - archiveFileIndex = -1; return ""; } \ No newline at end of file diff --git a/Core/RomLoader.h b/Core/RomLoader.h index f0ac8a9d..4fa64e7d 100644 --- a/Core/RomLoader.h +++ b/Core/RomLoader.h @@ -1,5 +1,6 @@ #pragma once #include "stdafx.h" +#include "../Utilities/VirtualFile.h" #include "RomData.h" class ArchiveReader; @@ -9,12 +10,12 @@ class RomLoader RomData _romData; string _filename; - static int32_t FindMatchingRomInFile(string filename, HashInfo hashInfo); + static string FindMatchingRomInFile(string filePath, HashInfo hashInfo); public: - bool LoadFile(string filename, int32_t archiveFileIndex); + bool LoadFile(VirtualFile romFile); bool LoadFile(string filename, vector &fileData); RomData GetRomData(); - static string FindMatchingRomInFolder(string folder, string romFilename, HashInfo hashInfo, bool useFastSearch, int32_t &archiveFileIndex); + static string FindMatchingRomInFolder(string folder, string romFilename, HashInfo hashInfo, bool useFastSearch); }; diff --git a/Core/SaveStateManager.cpp b/Core/SaveStateManager.cpp index 0cbc8143..15f3682b 100644 --- a/Core/SaveStateManager.cpp +++ b/Core/SaveStateManager.cpp @@ -134,7 +134,7 @@ bool SaveStateManager::LoadState(int stateIndex) return result; } -void SaveStateManager::SaveRecentGame(string romName, string romPath, string patchPath, int32_t archiveFileIndex) +void SaveStateManager::SaveRecentGame(string romName, string romPath, string patchPath) { if(!EmulationSettings::CheckFlag(EmulationFlags::ConsoleMode) && !EmulationSettings::CheckFlag(EmulationFlags::DisableGameSelectionScreen) && Console::GetRomFormat() != RomFormat::Nsf) { string filename = FolderUtilities::GetFilename(Console::GetRomName(), false) + ".rgd"; @@ -152,7 +152,6 @@ void SaveStateManager::SaveRecentGame(string romName, string romPath, string pat romInfoStream << romName << std::endl; romInfoStream << romPath << std::endl; romInfoStream << patchPath << std::endl; - romInfoStream << std::to_string(archiveFileIndex) << std::endl; writer.AddFile(romInfoStream, "RomInfo.txt"); } } @@ -165,15 +164,14 @@ void SaveStateManager::LoadRecentGame(string filename, bool resetGame) std::stringstream romInfoStream = reader.GetStream("RomInfo.txt"); std::stringstream stateStream = reader.GetStream("Savestate.mst"); - string romName, romPath, patchPath, archiveIndex; + string romName, romPath, patchPath; std::getline(romInfoStream, romName); std::getline(romInfoStream, romPath); std::getline(romInfoStream, patchPath); - std::getline(romInfoStream, archiveIndex); Console::Pause(); try { - Console::LoadROM(romPath, nullptr, std::stoi(archiveIndex.c_str()), patchPath); + Console::LoadROM(romPath, patchPath); if(!resetGame) { SaveStateManager::LoadState(stateStream); } diff --git a/Core/SaveStateManager.h b/Core/SaveStateManager.h index feaffb77..26363043 100644 --- a/Core/SaveStateManager.h +++ b/Core/SaveStateManager.h @@ -23,7 +23,7 @@ public: static bool LoadState(istream &stream); static bool LoadState(int stateIndex); - static void SaveRecentGame(string romName, string romPath, string patchPath, int32_t archiveFileIndex); + static void SaveRecentGame(string romName, string romPath, string patchPath); static void LoadRecentGame(string filename, bool resetGame); static void MoveToNextSlot(); diff --git a/Core/VsControlManager.h b/Core/VsControlManager.h index 3bcbd9e8..d484836a 100644 --- a/Core/VsControlManager.h +++ b/Core/VsControlManager.h @@ -191,7 +191,7 @@ public: uint8_t value = 0; - uint32_t crc = Console::GetPrgCrc32(); + uint32_t crc = Console::GetHashInfo().PrgCrc32Hash; switch(addr) { case 0x4016: diff --git a/GUI.NET/Config/Configuration.cs b/GUI.NET/Config/Configuration.cs index a1fd788f..3d92bbd2 100644 --- a/GUI.NET/Config/Configuration.cs +++ b/GUI.NET/Config/Configuration.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; +using Mesen.GUI.Forms; namespace Mesen.GUI.Config { @@ -87,13 +88,13 @@ namespace Mesen.GUI.Config PreferenceInfo.InitializeDefaults(); } - public void AddRecentFile(string filepath, string romName, int archiveFileIndex) + public void AddRecentFile(ResourcePath romFile, ResourcePath? patchFile) { - RecentItem existingItem = RecentFiles.Where((item) => item.Path == filepath && item.ArchiveFileIndex == archiveFileIndex).FirstOrDefault(); + RecentItem existingItem = RecentFiles.Where((item) => item.RomFile == romFile && item.PatchFile == patchFile).FirstOrDefault(); if(existingItem != null) { RecentFiles.Remove(existingItem); } - RecentItem recentItem = new RecentItem { RomName = romName, Path = filepath, ArchiveFileIndex = archiveFileIndex }; + RecentItem recentItem = new RecentItem { RomFile = romFile, PatchFile = patchFile }; RecentFiles.Insert(0, recentItem); if(RecentFiles.Count > Configuration.MaxRecentFiles) { @@ -149,8 +150,16 @@ namespace Mesen.GUI.Config public class RecentItem { - public string Path; - public string RomName; - public int ArchiveFileIndex; + public ResourcePath RomFile; + public ResourcePath? PatchFile; + + public override string ToString() + { + string text = Path.GetFileName(RomFile.FileName).Replace("&", "&&"); + if(PatchFile.HasValue) { + text += " [" + Path.GetFileName(PatchFile.Value) + "]"; + } + return text; + } } } diff --git a/GUI.NET/Controls/ctrlRecentGames.cs b/GUI.NET/Controls/ctrlRecentGames.cs index 33153bf2..39995f9c 100644 --- a/GUI.NET/Controls/ctrlRecentGames.cs +++ b/GUI.NET/Controls/ctrlRecentGames.cs @@ -11,6 +11,7 @@ using System.IO; using Mesen.GUI.Config; using System.Drawing.Text; using System.IO.Compression; +using Mesen.GUI.Forms; namespace Mesen.GUI.Controls { @@ -37,7 +38,7 @@ namespace Mesen.GUI.Controls { public string FileName { get; set; } public string RomName { get; set; } - public string RomPath { get; set; } + public ResourcePath RomPath { get; set; } public DateTime Timestamp { get; set; } } @@ -106,7 +107,7 @@ namespace Mesen.GUI.Controls info.Timestamp = new FileInfo(file).LastWriteTime; info.FileName = file; - if(File.Exists(info.RomPath)) { + if(info.RomPath.Exists) { _recentGames.Add(info); } } catch { } diff --git a/GUI.NET/Debugger/frmDebugger.cs b/GUI.NET/Debugger/frmDebugger.cs index e3e7db9c..26b14cee 100644 --- a/GUI.NET/Debugger/frmDebugger.cs +++ b/GUI.NET/Debugger/frmDebugger.cs @@ -158,7 +158,7 @@ namespace Mesen.GUI.Debugger private void AutoLoadDbgFile(bool silent) { if(ConfigManager.Config.DebugInfo.AutoLoadDbgFiles) { - string dbgPath = Path.Combine(Path.GetDirectoryName(ConfigManager.Config.RecentFiles[0].Path), Path.GetFileNameWithoutExtension(ConfigManager.Config.RecentFiles[0].RomName) + ".dbg"); + string dbgPath = Path.Combine(ConfigManager.Config.RecentFiles[0].RomFile.Folder, Path.GetFileNameWithoutExtension(ConfigManager.Config.RecentFiles[0].RomFile.FileName) + ".dbg"); if(File.Exists(dbgPath)) { Ld65DbgImporter dbgImporter = new Ld65DbgImporter(); dbgImporter.Import(dbgPath, silent); diff --git a/GUI.NET/Forms/Cheats/frmCheat.cs b/GUI.NET/Forms/Cheats/frmCheat.cs index 406345be..5755a9c8 100644 --- a/GUI.NET/Forms/Cheats/frmCheat.cs +++ b/GUI.NET/Forms/Cheats/frmCheat.cs @@ -66,9 +66,9 @@ namespace Mesen.GUI.Forms.Cheats private void LoadGame(string romPath) { - int archiveFileIndex = -1; - if(frmSelectRom.SelectRom(romPath, ref archiveFileIndex)) { - RomInfo romInfo = InteropEmu.GetRomInfo(romPath, archiveFileIndex); + ResourcePath resource = romPath; + if(frmSelectRom.SelectRom(ref resource)) { + RomInfo romInfo = InteropEmu.GetRomInfo(resource); _gameCrc = romInfo.GetPrgCrcString(); if(_gameCrc != null) { ((CheatInfo)Entity).GameName = Path.GetFileNameWithoutExtension(romInfo.RomName); diff --git a/GUI.NET/Forms/Cheats/frmCheatImport.cs b/GUI.NET/Forms/Cheats/frmCheatImport.cs index d15bd5f0..f92da026 100644 --- a/GUI.NET/Forms/Cheats/frmCheatImport.cs +++ b/GUI.NET/Forms/Cheats/frmCheatImport.cs @@ -41,9 +41,9 @@ namespace Mesen.GUI.Forms.Cheats private void LoadGame(string romPath) { - int archiveFileIndex = -1; - if(frmSelectRom.SelectRom(romPath, ref archiveFileIndex)) { - RomInfo romInfo = InteropEmu.GetRomInfo(romPath, archiveFileIndex); + ResourcePath resource = romPath; + if(frmSelectRom.SelectRom(ref resource)) { + RomInfo romInfo = InteropEmu.GetRomInfo(resource); _gameCrc = romInfo.GetPrgCrcString(); _gameName = romInfo.GetRomName(); txtGameName.Text = _gameName; diff --git a/GUI.NET/Forms/ResourcePath.cs b/GUI.NET/Forms/ResourcePath.cs new file mode 100644 index 00000000..617d9cea --- /dev/null +++ b/GUI.NET/Forms/ResourcePath.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI.Forms +{ + public struct ResourcePath : IEquatable + { + public string Path { get; set; } + public string InnerFile { get; set; } + + public bool Exists { get { return File.Exists(Path); } } + public bool Compressed { get { return !string.IsNullOrWhiteSpace(InnerFile); } } + + public string FileName { get { return Compressed ? InnerFile : System.IO.Path.GetFileName(Path); } } + public string Folder { get { return System.IO.Path.GetDirectoryName(Path); } } + + public override string ToString() + { + return Path + (Compressed ? ("\x1" + InnerFile) : ""); + } + + static public implicit operator ResourcePath(string path) + { + string[] tokens = path.Split('\x1'); + return new ResourcePath() { + Path = tokens[0], + InnerFile = tokens.Length > 1 ? tokens[1] : "" + }; + } + + static public implicit operator string(ResourcePath resourcePath) + { + return resourcePath.ToString(); + } + + bool IEquatable.Equals(ResourcePath other) + { + return other.ToString() == this.ToString(); + } + } +} diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index 1a4526f0..693d7844 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -30,8 +30,7 @@ namespace Mesen.GUI.Forms private frmLogWindow _logWindow; private frmCheatList _cheatListWindow; private frmHdPackEditor _hdPackEditorWindow; - private string _currentRomPath = null; - private int _currentRomArchiveIndex = -1; + private ResourcePath? _currentRomPath = null; private string _currentGame = null; private bool _customSize = false; private FormWindowState _originalWindowState; @@ -65,7 +64,7 @@ namespace Mesen.GUI.Forms lblVersion.Font = new Font(_fonts.Families[0], 11); _commandLineArgs = args; - + Application.AddMessageFilter(this); this.Resize += ResizeRecentGames; this.FormClosed += (s, e) => Application.RemoveMessageFilter(this); @@ -123,22 +122,22 @@ namespace Mesen.GUI.Forms { base.OnLoad(e); - #if HIDETESTMENU +#if HIDETESTMENU mnuTests.Visible = false; - #endif +#endif _notifListener = new InteropEmu.NotificationListener(); _notifListener.OnNotification += _notifListener_OnNotification; menuTimer.Start(); - + this.ProcessCommandLineArguments(_commandLineArgs, true); VideoInfo.ApplyConfig(); InitializeVsSystemMenu(); InitializeFdsDiskMenu(); InitializeEmulationSpeedMenu(); - + UpdateVideoSettings(); InitializeEmu(); @@ -158,7 +157,7 @@ namespace Mesen.GUI.Forms CheckForUpdates(false); } } - + protected override void OnDeactivate(EventArgs e) { base.OnDeactivate(e); @@ -224,6 +223,8 @@ namespace Mesen.GUI.Forms //0.9.0's "Auto" has been renamed to "NoStretching" ConfigManager.Config.VideoInfo.AspectRatio = VideoAspectRatio.NoStretching; } + + ConfigManager.Config.RecentFiles.Clear(); } if(oldVersion <= new Version("0.5.3")) { @@ -264,14 +265,14 @@ namespace Mesen.GUI.Forms { InteropEmu.InitializeEmu(ConfigManager.HomeFolder, this.Handle, this.ctrlRenderer.Handle, _noAudio, _noVideo, _noInput); foreach(RecentItem recentItem in ConfigManager.Config.RecentFiles) { - InteropEmu.AddKnownGameFolder(Path.GetDirectoryName(recentItem.Path)); + InteropEmu.AddKnownGameFolder(recentItem.RomFile.Folder); } ConfigManager.Config.InitializeDefaults(); ConfigManager.ApplyChanges(); ConfigManager.Config.ApplyConfig(); - + UpdateEmulationFlags(); } @@ -391,7 +392,7 @@ namespace Mesen.GUI.Forms ctrlRenderer.Top = (panelRenderer.Height - ctrlRenderer.Height) / 2; } } - + private void SetScaleBasedOnWindowSize() { _customSize = true; @@ -421,7 +422,7 @@ namespace Mesen.GUI.Forms this.SetScale(_regularScale); this.UpdateScaleMenu(_regularScale); this.UpdateViewerSize(); - VideoInfo.ApplyConfig(); + VideoInfo.ApplyConfig(); } this.Resize += frmMain_Resize; mnuFullscreen.Checked = enabled; @@ -553,7 +554,7 @@ namespace Mesen.GUI.Forms using(OpenFileDialog ofd = new OpenFileDialog()) { ofd.SetFilter(ResourceHelper.GetMessage("FilterRomIps")); if(ConfigManager.Config.RecentFiles.Count > 0) { - ofd.InitialDirectory = Path.GetDirectoryName(ConfigManager.Config.RecentFiles[0].Path); + ofd.InitialDirectory = ConfigManager.Config.RecentFiles[0].RomFile.Folder; } if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { LoadFile(ofd.FileName); @@ -595,62 +596,56 @@ namespace Mesen.GUI.Forms using(OpenFileDialog ofd = new OpenFileDialog()) { ofd.SetFilter(ResourceHelper.GetMessage("FilterRom")); if(ConfigManager.Config.RecentFiles.Count > 0) { - ofd.InitialDirectory = Path.GetDirectoryName(ConfigManager.Config.RecentFiles[0].Path); + ofd.InitialDirectory = ConfigManager.Config.RecentFiles[0].RomFile.Folder; } if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { - LoadROM(ofd.FileName, true, -1, patchFile); + LoadROM(ofd.FileName, true, patchFile); } } } } else if(MesenMsgBox.Show("PatchAndReset", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.OK) { - LoadROM(_currentRomPath, true, _currentRomArchiveIndex, patchFile); + LoadROM(_currentRomPath.Value, true, patchFile); } } - private void LoadROM(string filename, bool autoLoadPatches = false, int archiveFileIndex = -1, string patchFileToApply = null) + private void LoadROM(ResourcePath romFile, bool autoLoadPatches = false, ResourcePath? patchFileToApply = null) { - _currentRomPath = filename; - _currentRomArchiveIndex = -1; - if(File.Exists(filename)) { - string romName; - if(frmSelectRom.SelectRom(filename, ref archiveFileIndex, out romName)) { - _currentRomArchiveIndex = archiveFileIndex; - if(archiveFileIndex >= 0) { + if(romFile.Exists) { + if(frmSelectRom.SelectRom(ref romFile)) { + _currentRomPath = romFile; + + if(romFile.Compressed) { Interlocked.Increment(ref _romLoadCounter); ctrlNsfPlayer.Visible = false; ctrlLoading.Visible = true; } - string patchFile = patchFileToApply; - if(patchFile == null) { + ResourcePath? patchFile = patchFileToApply; + if(patchFile == null && autoLoadPatches) { string[] extensions = new string[3] { ".ips", ".ups", ".bps" }; foreach(string ext in extensions) { - string file = Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename)) + ext; + string file = Path.Combine(romFile.Folder, Path.GetFileNameWithoutExtension(romFile.FileName)) + ext; if(File.Exists(file)) { patchFile = file; break; } } } - - if(!File.Exists(patchFile)) { - autoLoadPatches = false; - } Task loadRomTask = new Task(() => { lock(_loadRomLock) { - InteropEmu.LoadROM(filename, archiveFileIndex, autoLoadPatches ? patchFile : string.Empty); + InteropEmu.LoadROM(romFile, (patchFile.HasValue && patchFile.Value.Exists) ? (string)patchFile.Value : string.Empty); } }); loadRomTask.ContinueWith((Task prevTask) => { this.BeginInvoke((MethodInvoker)(() => { - if(archiveFileIndex >= 0) { + if(romFile.Compressed) { Interlocked.Decrement(ref _romLoadCounter); } - ConfigManager.Config.AddRecentFile(filename, romName, archiveFileIndex); + ConfigManager.Config.AddRecentFile(romFile, patchFileToApply); UpdateRecentFiles(); })); }); @@ -658,10 +653,10 @@ namespace Mesen.GUI.Forms loadRomTask.Start(); } } else { - MesenMsgBox.Show("FileNotFound", MessageBoxButtons.OK, MessageBoxIcon.Error, filename); + MesenMsgBox.Show("FileNotFound", MessageBoxButtons.OK, MessageBoxIcon.Error, romFile.Path); } } - + private void UpdateFocusFlag() { bool hasFocus = false; @@ -688,7 +683,6 @@ namespace Mesen.GUI.Forms } else { panelInfo.Visible = _emuThread == null; ctrlRecentGames.Visible = _emuThread == null; - mnuPowerOff.Enabled = _emuThread != null; ctrlLoading.Visible = (_romLoadCounter > 0); @@ -697,7 +691,7 @@ namespace Mesen.GUI.Forms bool isNetPlayClient = InteropEmu.IsConnected(); - mnuPause.Enabled = mnuPowerCycle.Enabled = mnuReset.Enabled = (_emuThread != null && !isNetPlayClient); + mnuPause.Enabled = mnuPowerCycle.Enabled = mnuReset.Enabled = mnuPowerOff.Enabled = (_emuThread != null && !isNetPlayClient); mnuSaveState.Enabled = (_emuThread != null && !isNetPlayClient && !InteropEmu.IsNsf()); mnuLoadState.Enabled = (_emuThread != null && !isNetPlayClient && !InteropEmu.IsNsf() && !InteropEmu.MoviePlaying() && !InteropEmu.MovieRecording()); @@ -782,7 +776,7 @@ namespace Mesen.GUI.Forms mnuRegionPal.Checked = ConfigManager.Config.Region == NesModel.PAL; mnuRegionDendy.Checked = ConfigManager.Config.Region == NesModel.Dendy; - bool autoInsertDisabled = !InteropEmu.FdsIsAutoInsertDiskEnabled(); + bool autoInsertDisabled = !InteropEmu.FdsIsAutoInsertDiskEnabled(); mnuSelectDisk.Enabled = autoInsertDisabled; mnuEjectDisk.Enabled = autoInsertDisabled; mnuSwitchDiskSide.Enabled = autoInsertDisabled; @@ -807,9 +801,9 @@ namespace Mesen.GUI.Forms mnuRecentFiles.DropDownItems.Clear(); foreach(RecentItem recentItem in ConfigManager.Config.RecentFiles) { ToolStripMenuItem tsmi = new ToolStripMenuItem(); - tsmi.Text = recentItem.RomName.Replace("&", "&&"); + tsmi.Text = recentItem.ToString(); tsmi.Click += (object sender, EventArgs args) => { - LoadROM(recentItem.Path, ConfigManager.Config.PreferenceInfo.AutoLoadIpsPatches, recentItem.ArchiveFileIndex); + LoadROM(recentItem.RomFile, ConfigManager.Config.PreferenceInfo.AutoLoadIpsPatches, recentItem.PatchFile); }; mnuRecentFiles.DropDownItems.Add(tsmi); } @@ -833,7 +827,7 @@ namespace Mesen.GUI.Forms } UpdateMenus(); } - + private void StopEmu() { InteropEmu.Stop(); @@ -893,7 +887,7 @@ namespace Mesen.GUI.Forms } } - #if !HIDETESTMENU +#if !HIDETESTMENU if(keyData == Keys.Pause) { if(InteropEmu.RomTestRecording()) { InteropEmu.RomTestStop(); @@ -901,7 +895,7 @@ namespace Mesen.GUI.Forms InteropEmu.RomTestRecord(ConfigManager.TestFolder + "\\" + InteropEmu.GetRomInfo().GetRomName() + ".mtp", true); } } - #endif +#endif if(keyData == Keys.Escape && _emuThread != null && mnuPause.Enabled) { PauseEmu(); @@ -1027,12 +1021,12 @@ namespace Mesen.GUI.Forms frm.ShowDialog(sender); } } - + private void mnuExit_Click(object sender, EventArgs e) { this.Close(); } - + private void mnuVideoConfig_Click(object sender, EventArgs e) { using(frmVideoConfig frm = new frmVideoConfig()) { @@ -1040,7 +1034,7 @@ namespace Mesen.GUI.Forms } UpdateVideoSettings(); } - + private void mnuDebugger_Click(object sender, EventArgs e) { if(_debugger == null) { @@ -1053,7 +1047,7 @@ namespace Mesen.GUI.Forms _debugger.Focus(); } } - + private void mnuSaveState_DropDownOpening(object sender, EventArgs e) { InitializeStateMenu(mnuSaveState, true); @@ -1070,7 +1064,7 @@ namespace Mesen.GUI.Forms } #endregion - + private void RecordMovie(bool resetEmu) { using(SaveFileDialog sfd = new SaveFileDialog()) { @@ -1262,7 +1256,7 @@ namespace Mesen.GUI.Forms if(_cheatListWindow.DialogResult == DialogResult.OK) { CheatInfo.ApplyCheats(); } - _cheatListWindow = null; + _cheatListWindow = null; }; } else { _cheatListWindow.Focus(); @@ -1325,7 +1319,7 @@ namespace Mesen.GUI.Forms ofd.SetFilter("*.nes|*.nes"); if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { string filename = ofd.FileName; - + Task.Run(() => { int result = InteropEmu.RunAutomaticTest(filename); }); @@ -1341,7 +1335,7 @@ namespace Mesen.GUI.Forms startInfo.WorkingDirectory = workingDirectory; Process.Start(startInfo); } - + private void mnuRunAllGameTests_Click(object sender, EventArgs e) { string workingDirectory = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)); @@ -1440,7 +1434,7 @@ namespace Mesen.GUI.Forms { SetVideoFilter(VideoFilterType.xBRZ2x); } - + private void mnuXBRZ3xFilter_Click(object sender, EventArgs e) { SetVideoFilter(VideoFilterType.xBRZ3x); @@ -1505,7 +1499,7 @@ namespace Mesen.GUI.Forms { SetVideoFilter(VideoFilterType.SuperEagle); } - + private void mnuPrescale2xFilter_Click(object sender, EventArgs e) { SetVideoFilter(VideoFilterType.Prescale2x); @@ -1600,7 +1594,7 @@ namespace Mesen.GUI.Forms string hash = MD5Helper.GetMD5Hash(ofd.FileName).ToLowerInvariant(); if(hash == "ca30b50f880eb660a320674ed365ef7a" || hash == "c1a9e9415a6adde3c8563c622d4c9fce") { File.Copy(ofd.FileName, Path.Combine(ConfigManager.HomeFolder, "FdsBios.bin")); - LoadROM(_currentRomPath, ConfigManager.Config.PreferenceInfo.AutoLoadIpsPatches); + LoadROM(_currentRomPath.Value, ConfigManager.Config.PreferenceInfo.AutoLoadIpsPatches); } else { MesenMsgBox.Show("InvalidFdsBios", MessageBoxButtons.OK, MessageBoxIcon.Error); } @@ -1742,7 +1736,7 @@ namespace Mesen.GUI.Forms { CheckForUpdates(true); } - + private void mnuReportBug_Click(object sender, EventArgs e) { Process.Start("http://www.mesen.ca/ReportBug.php"); @@ -1842,7 +1836,7 @@ namespace Mesen.GUI.Forms } this.ctrlNsfPlayer.Visible = true; this.ctrlNsfPlayer.Focus(); - + _currentGame = InteropEmu.NsfGetHeader().GetSongName(); } else if(this._isNsfPlayerMode) { this.MinimumSize = new Size(335, 320); @@ -1855,7 +1849,7 @@ namespace Mesen.GUI.Forms private void mnuRandomGame_Click(object sender, EventArgs e) { - IEnumerable gameFolders = ConfigManager.Config.RecentFiles.Select(recentFile => Path.GetDirectoryName(recentFile.Path).ToLowerInvariant()).Distinct(); + IEnumerable gameFolders = ConfigManager.Config.RecentFiles.Select(recentFile => recentFile.RomFile.Folder.ToLowerInvariant()).Distinct(); List gameRoms = new List(); foreach(string folder in gameFolders) { diff --git a/GUI.NET/Forms/frmSelectRom.cs b/GUI.NET/Forms/frmSelectRom.cs index 8f2b89d4..40b225d1 100644 --- a/GUI.NET/Forms/frmSelectRom.cs +++ b/GUI.NET/Forms/frmSelectRom.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -72,34 +73,25 @@ namespace Mesen.GUI.Forms txtSearch.Focus(); } - public static bool SelectRom(string filename, ref int archiveFileIndex) + public static bool SelectRom(ref ResourcePath resource) { - string romName; - return SelectRom(filename, ref archiveFileIndex, out romName); - } + List archiveRomList = InteropEmu.GetArchiveRomList(resource.Path); - public static bool SelectRom(string filename, ref int archiveFileIndex, out string romName) - { - romName = ""; + if(archiveRomList.Contains(resource.InnerFile)) { + return true; + } - List archiveRomList = InteropEmu.GetArchiveRomList(filename); if(archiveRomList.Count > 1) { - if(archiveFileIndex >= 0 && archiveFileIndex < archiveRomList.Count) { - romName = System.IO.Path.GetFileName(archiveRomList[archiveFileIndex]); - return true; + frmSelectRom frm = new frmSelectRom(archiveRomList); + if(frm.ShowDialog(null, Application.OpenForms[0]) == DialogResult.OK) { + resource.InnerFile = frm.lstRoms.SelectedItem.ToString(); } else { - frmSelectRom frm = new frmSelectRom(archiveRomList); - if(frm.ShowDialog(null, Application.OpenForms[0]) == DialogResult.OK) { - archiveFileIndex = frm.SelectedIndex; - romName = System.IO.Path.GetFileName(frm.lstRoms.SelectedItem.ToString()); - } else { - return false; - } + return false; } } else if(archiveRomList.Count == 1) { - romName = System.IO.Path.GetFileName(archiveRomList[0]); + resource.InnerFile = archiveRomList[0]; } else { - romName = System.IO.Path.GetFileName(filename); + resource.InnerFile = ""; } return true; diff --git a/GUI.NET/GUI.NET.csproj b/GUI.NET/GUI.NET.csproj index bf0d0390..06f44714 100644 --- a/GUI.NET/GUI.NET.csproj +++ b/GUI.NET/GUI.NET.csproj @@ -676,6 +676,7 @@ + diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 4d8678a2..02c678aa 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -26,7 +26,7 @@ namespace Mesen.GUI [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsRunning(); - [DllImport(DLLPath)] public static extern void LoadROM([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filename, Int32 archiveFileIndex, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string patchFile); + [DllImport(DLLPath)] public static extern void LoadROM([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filename, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string patchFile); [DllImport(DLLPath)] public static extern void AddKnownGameFolder([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string folder); [DllImport(DLLPath)] public static extern void LoadRecentGame([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filepath, [MarshalAs(UnmanagedType.I1)]bool resetGame); @@ -553,10 +553,10 @@ namespace Mesen.GUI return header; } - public static RomInfo GetRomInfo(string filename = "", Int32 archiveFileIndex = -1) + public static RomInfo GetRomInfo(string filename = "") { InteropRomInfo romInfo = new InteropRomInfo(); - InteropEmu.GetRomInfoWrapper(ref romInfo, filename, archiveFileIndex); + InteropEmu.GetRomInfoWrapper(ref romInfo, filename); return new RomInfo(romInfo); } diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index a80dcc3b..dd6e460c 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -15,7 +15,6 @@ #include "../Core/FDS.h" #include "../Core/VsControlManager.h" #include "../Core/SoundMixer.h" -#include "../Core/FileLoader.h" #include "../Core/RomLoader.h" #include "../Core/NsfMapper.h" #include "../Core/IRenderingDevice.h" @@ -116,14 +115,17 @@ namespace InteropEmu { DllExport bool __stdcall IsRunning() { return Console::IsRunning(); } - DllExport void __stdcall LoadROM(char* filename, int32_t archiveFileIndex, char* patchFile) { Console::LoadROM(filename, nullptr, archiveFileIndex, patchFile); } + DllExport void __stdcall LoadROM(char* filename, char* patchFile) { Console::LoadROM((string)filename, (string)patchFile); } DllExport void __stdcall AddKnownGameFolder(char* folder) { FolderUtilities::AddKnownGameFolder(folder); } DllExport void __stdcall LoadRecentGame(char* filepath, bool resetGame) { SaveStateManager::LoadRecentGame(filepath, resetGame); } DllExport const char* __stdcall GetArchiveRomList(char* filename) { std::ostringstream out; - for(string romName : FileLoader::GetArchiveRomList(filename)) { - out << romName << "[!|!]"; + shared_ptr reader = ArchiveReader::GetReader(filename); + if(reader) { + for(string romName : reader->GetFileList({ ".nes", ".fds", ".nsf", ".nsfe", "*.unf" })) { + out << romName << "[!|!]"; + } } _returnString = out.str(); return _returnString.c_str(); @@ -174,23 +176,25 @@ namespace InteropEmu { DllExport void __stdcall Stop() { if(Console::GetInstance()) { + GameServer::StopServer(); + GameClient::Disconnect(); Console::GetInstance()->Stop(); } } - DllExport const void __stdcall GetRomInfo(RomInfo &romInfo, char* filename, int32_t archiveFileIndex) + DllExport const void __stdcall GetRomInfo(RomInfo &romInfo, char* filename) { string romPath = filename; if(romPath.empty()) { _returnString = Console::GetRomName(); romInfo.RomName = _returnString.c_str(); - romInfo.Crc32 = Console::GetCrc32(); - romInfo.PrgCrc32 = Console::GetPrgCrc32(); + romInfo.Crc32 = Console::GetHashInfo().Crc32Hash; + romInfo.PrgCrc32 = Console::GetHashInfo().PrgCrc32Hash; romInfo.Format = Console::GetRomFormat(); romInfo.IsChrRam = Console::IsChrRam(); } else { RomLoader romLoader; - if(romLoader.LoadFile(filename, archiveFileIndex)) { + if(romLoader.LoadFile(romPath)) { RomData romData = romLoader.GetRomData(); _returnString = romData.RomName; diff --git a/PGOHelper/PGOHelper.cpp b/PGOHelper/PGOHelper.cpp index 2e252d55..f05920a3 100644 --- a/PGOHelper/PGOHelper.cpp +++ b/PGOHelper/PGOHelper.cpp @@ -38,7 +38,7 @@ extern "C" { void __stdcall SetFlags(uint64_t flags); void __stdcall SetVideoFilter(VideoFilterType filter); void __stdcall InitializeEmu(char* homeFolder, void*, void*, bool, bool, bool); - void __stdcall LoadROM(const char* filename, int32_t archiveFileIndex, char* patchFile); + void __stdcall LoadROM(const char* filename, char* patchFile); void __stdcall Run(); void __stdcall Stop(); } @@ -67,7 +67,7 @@ int main(int argc, char* argv[]) SetFlags(0x8000000000000000); //EmulationFlags::ConsoleMode InitializeEmu("C:\\Windows\\Temp\\Mesen", nullptr, nullptr, false, false, false); - LoadROM(testRoms[0], -1, ""); + LoadROM(testRoms[0], ""); std::cout << "Running: " << testRoms[0] << std::endl; thread testThread([testRoms] { @@ -91,7 +91,7 @@ int main(int argc, char* argv[]) std::this_thread::sleep_for(std::chrono::duration(5000)); std::cout << "Running: " << testRoms[i] << std::endl; SetVideoFilter(filterTypes[i % 13]); - LoadROM(testRoms[i], -1, ""); + LoadROM(testRoms[i], ""); } std::this_thread::sleep_for(std::chrono::duration(5000)); Stop(); diff --git a/TestHelper/TestHelper.cpp b/TestHelper/TestHelper.cpp index d253e768..a9ba2cfb 100644 --- a/TestHelper/TestHelper.cpp +++ b/TestHelper/TestHelper.cpp @@ -50,7 +50,6 @@ extern "C" { void __stdcall SetControllerType(uint32_t port, ControllerType type); int __stdcall RunAutomaticTest(char* filename); int __stdcall RunRecordedTest(char* filename); - void __stdcall LoadROM(char* filename); void __stdcall Run(); void __stdcall Stop(); INotificationListener* __stdcall RegisterNotificationCallback(NotificationListenerCallback callback); @@ -154,7 +153,7 @@ int main(int argc, char* argv[]) if(argc >= 3 && strcmp(argv[1], "/auto") == 0) { string romFolder = argv[2]; - testFilenames = FolderUtilities::GetFilesInFolder(romFolder, ".nes", true); + testFilenames = FolderUtilities::GetFilesInFolder(romFolder, { ".nes" }, true); automaticTests = true; } else if(argc <= 2) { string testFolder; @@ -163,7 +162,7 @@ int main(int argc, char* argv[]) } else { testFolder = argv[1]; } - testFilenames = FolderUtilities::GetFilesInFolder(testFolder, ".mtp", true); + testFilenames = FolderUtilities::GetFilesInFolder(testFolder, { ".mtp" }, true); automaticTests = false; } diff --git a/Utilities/ArchiveReader.cpp b/Utilities/ArchiveReader.cpp index 547059f9..b2ae2345 100644 --- a/Utilities/ArchiveReader.cpp +++ b/Utilities/ArchiveReader.cpp @@ -4,6 +4,8 @@ #include #include #include "FolderUtilities.h" +#include "ZipReader.h" +#include "SZReader.h" ArchiveReader::~ArchiveReader() { @@ -26,13 +28,17 @@ std::stringstream ArchiveReader::GetStream(string filename) vector ArchiveReader::GetFileList(std::initializer_list extensions) { + if(extensions.size() == 0) { + return InternalGetFileList(); + } + vector filenames; for(string filename : InternalGetFileList()) { string lcFilename = filename; std::transform(lcFilename.begin(), lcFilename.end(), lcFilename.begin(), ::tolower); - if(filename.length() > 4) { - for(string ext : extensions) { - if(lcFilename.substr(lcFilename.length() - 4, 4).compare(ext) == 0) { + for(string ext : extensions) { + if(lcFilename.size() >= ext.size()) { + if(lcFilename.substr(lcFilename.length() - ext.size(), ext.size()).compare(ext) == 0) { filenames.push_back(filename); } } @@ -76,4 +82,27 @@ bool ArchiveReader::LoadArchive(string filename) in.close(); } return false; +} + +shared_ptr ArchiveReader::GetReader(string filepath) +{ + ifstream in(filepath, std::ios::in | std::ios::binary); + if(in) { + uint8_t header[2] = { 0,0 }; + in.read((char*)header, 2); + in.close(); + + shared_ptr reader; + if(memcmp(header, "PK", 2) == 0) { + reader.reset(new ZipReader()); + } else if(memcmp(header, "7z", 2) == 0) { + reader.reset(new SZReader()); + } + + if(reader) { + reader->LoadArchive(filepath); + return reader; + } + } + return nullptr; } \ No newline at end of file diff --git a/Utilities/ArchiveReader.h b/Utilities/ArchiveReader.h index 6def557f..c287ce46 100644 --- a/Utilities/ArchiveReader.h +++ b/Utilities/ArchiveReader.h @@ -17,7 +17,9 @@ public: std::stringstream GetStream(string filename); - vector GetFileList(std::initializer_list extensions); + vector GetFileList(std::initializer_list extensions = {}); - virtual void ExtractFile(string filename, vector &output) = 0; + virtual bool ExtractFile(string filename, vector &output) = 0; + + static shared_ptr GetReader(string filepath); }; \ No newline at end of file diff --git a/Utilities/BpsPatcher.cpp b/Utilities/BpsPatcher.cpp index b038b76a..a6dd83dc 100644 --- a/Utilities/BpsPatcher.cpp +++ b/Utilities/BpsPatcher.cpp @@ -4,7 +4,7 @@ #include "BpsPatcher.h" #include "CRC32.h" -uint64_t BpsPatcher::ReadBase128Number(ifstream &file) +uint64_t BpsPatcher::ReadBase128Number(std::istream &file) { uint64_t result = 0; int shift = 0; @@ -25,103 +25,104 @@ uint64_t BpsPatcher::ReadBase128Number(ifstream &file) return result; } -vector BpsPatcher::PatchBuffer(string bpsFilepath, vector input) +bool BpsPatcher::PatchBuffer(string bpsFilepath, vector &input, vector &output) { ifstream bpsFile(bpsFilepath, std::ios::in | std::ios::binary); - if(bpsFile) { - bpsFile.seekg(0, std::ios::end); - size_t fileSize = (size_t)bpsFile.tellg(); - bpsFile.seekg(0, std::ios::beg); + return PatchBuffer(bpsFile, input, output); + } + return false; +} - char header[4]; - bpsFile.read((char*)&header, 4); - if(memcmp((char*)&header, "BPS1", 4) != 0) { - //Invalid BPS file - return input; - } +bool BpsPatcher::PatchBuffer(std::istream &bpsFile, vector &input, vector &output) +{ + bpsFile.seekg(0, std::ios::end); + size_t fileSize = (size_t)bpsFile.tellg(); + bpsFile.seekg(0, std::ios::beg); - uint64_t inputFileSize = ReadBase128Number(bpsFile); - uint64_t outputFileSize = ReadBase128Number(bpsFile); - if(inputFileSize == -1 || outputFileSize == -1) { + char header[4]; + bpsFile.read((char*)&header, 4); + if(memcmp((char*)&header, "BPS1", 4) != 0) { + //Invalid BPS file + return false; + } + + uint64_t inputFileSize = ReadBase128Number(bpsFile); + uint64_t outputFileSize = ReadBase128Number(bpsFile); + if(inputFileSize == -1 || outputFileSize == -1) { + //Invalid file + return false; + } + + uint64_t metadataSize = ReadBase128Number(bpsFile); + bpsFile.seekg(metadataSize, std::ios::cur); + + output.resize((size_t)outputFileSize); + + uint32_t outputOffset = 0; + uint32_t inputRelativeOffset = 0; + uint32_t outputRelativeOffset = 0; + while((size_t)bpsFile.tellg() < fileSize - 12) { + uint64_t data = ReadBase128Number(bpsFile); + if(data == -1) { //Invalid file - return input; + return false; } - uint64_t metadataSize = ReadBase128Number(bpsFile); - bpsFile.seekg(metadataSize, std::ios::cur); + uint8_t command = data & 0x03; + uint64_t length = (data >> 2) + 1; + switch(command) { + case 0: + //SourceRead + while(length--) { + output[outputOffset] = input[outputOffset]; + outputOffset++; + } + break; - vector output; - output.resize((size_t)outputFileSize); + case 1: + //TargetRead + while(length--) { + uint8_t value = 0; + bpsFile.read((char*)&value, 1); - uint32_t outputOffset = 0; - uint32_t inputRelativeOffset = 0; - uint32_t outputRelativeOffset = 0; - while((size_t)bpsFile.tellg() < fileSize - 12) { - uint64_t data = ReadBase128Number(bpsFile); - if(data == -1) { - //Invalid file - return input; + output[outputOffset++] = value; + } + break; + + case 2: { + //SourceCopy + uint32_t data = (uint32_t)ReadBase128Number(bpsFile); + inputRelativeOffset += (data & 1 ? -1 : +1) * (data >> 1); + while(length--) { + output[outputOffset++] = input[inputRelativeOffset++]; + } + break; } - uint8_t command = data & 0x03; - uint64_t length = (data >> 2) + 1; - switch(command) { - case 0: - //SourceRead - while(length--) { - output[outputOffset] = input[outputOffset]; - outputOffset++; - } - break; - - case 1: - //TargetRead - while(length--) { - uint8_t value = 0; - bpsFile.read((char*)&value, 1); - - output[outputOffset++] = value; - } - break; - - case 2: { - //SourceCopy - uint32_t data = (uint32_t)ReadBase128Number(bpsFile); - inputRelativeOffset += (data & 1 ? -1 : +1) * (data >> 1); - while(length--) { - output[outputOffset++] = input[inputRelativeOffset++]; - } - break; + case 3: { + //TargetCopy + uint32_t data = (uint32_t)ReadBase128Number(bpsFile); + outputRelativeOffset += (data & 1 ? -1 : +1) * (data >> 1); + while(length--) { + output[outputOffset++] = output[outputRelativeOffset++]; } - - case 3: { - //TargetCopy - uint32_t data = (uint32_t)ReadBase128Number(bpsFile); - outputRelativeOffset += (data & 1 ? -1 : +1) * (data >> 1); - while(length--) { - output[outputOffset++] = output[outputRelativeOffset++]; - } - break; - } - } - } - - uint8_t inputChecksum[4]; - uint8_t outputChecksum[4]; - bpsFile.read((char*)inputChecksum, 4); - bpsFile.read((char*)outputChecksum, 4); - uint32_t patchInputCrc = inputChecksum[0] | (inputChecksum[1] << 8) | (inputChecksum[2] << 16) | (inputChecksum[3] << 24); - uint32_t patchOutputCrc = outputChecksum[0] | (outputChecksum[1] << 8) | (outputChecksum[2] << 16) | (outputChecksum[3] << 24); - uint32_t inputCrc = CRC32::GetCRC(input.data(), input.size()); - uint32_t outputCrc = CRC32::GetCRC(output.data(), output.size()); - - if(patchInputCrc != inputCrc || patchOutputCrc != outputCrc) { - return input; - } - - bpsFile.close(); - return output; + break; + } + } } - return input; + + uint8_t inputChecksum[4]; + uint8_t outputChecksum[4]; + bpsFile.read((char*)inputChecksum, 4); + bpsFile.read((char*)outputChecksum, 4); + uint32_t patchInputCrc = inputChecksum[0] | (inputChecksum[1] << 8) | (inputChecksum[2] << 16) | (inputChecksum[3] << 24); + uint32_t patchOutputCrc = outputChecksum[0] | (outputChecksum[1] << 8) | (outputChecksum[2] << 16) | (outputChecksum[3] << 24); + uint32_t inputCrc = CRC32::GetCRC(input.data(), input.size()); + uint32_t outputCrc = CRC32::GetCRC(output.data(), output.size()); + + if(patchInputCrc != inputCrc || patchOutputCrc != outputCrc) { + return false; + } + return true; } diff --git a/Utilities/BpsPatcher.h b/Utilities/BpsPatcher.h index 86b3032e..6179f748 100644 --- a/Utilities/BpsPatcher.h +++ b/Utilities/BpsPatcher.h @@ -5,8 +5,9 @@ class BpsPatcher { private: - static uint64_t ReadBase128Number(ifstream &file); + static uint64_t ReadBase128Number(std::istream &file); public: - static vector PatchBuffer(string bpsFilepath, vector input); + static bool PatchBuffer(std::istream &bpsFile, vector &input, vector &output); + static bool PatchBuffer(string bpsFilepath, vector &input, vector &output); }; \ No newline at end of file diff --git a/Utilities/FolderUtilities.cpp b/Utilities/FolderUtilities.cpp index 6f614264..13caed65 100644 --- a/Utilities/FolderUtilities.cpp +++ b/Utilities/FolderUtilities.cpp @@ -4,6 +4,7 @@ #include namespace fs = std::experimental::filesystem; +#include #include #include "FolderUtilities.h" #include "UTF8Util.h" @@ -120,7 +121,7 @@ vector FolderUtilities::GetFolders(string rootFolder) return folders; } -vector FolderUtilities::GetFilesInFolder(string rootFolder, string mask, bool recursive) +vector FolderUtilities::GetFilesInFolder(string rootFolder, std::unordered_set extensions, bool recursive) { vector files; vector folders = { { rootFolder } }; @@ -139,7 +140,7 @@ vector FolderUtilities::GetFilesInFolder(string rootFolder, string mask, for(fs::directory_iterator i(fs::u8path(folder.c_str())), end; i != end; i++) { string extension = i->path().extension().u8string(); std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); - if(extension == mask) { + if(extensions.find(extension) != extensions.end()) { files.push_back(i->path().u8string()); } } diff --git a/Utilities/FolderUtilities.h b/Utilities/FolderUtilities.h index 6d1b1f9c..31c6ab98 100644 --- a/Utilities/FolderUtilities.h +++ b/Utilities/FolderUtilities.h @@ -1,6 +1,7 @@ #pragma once #include "stdafx.h" +#include class FolderUtilities { @@ -24,7 +25,7 @@ public: static string GetRecentGamesFolder(); static vector GetFolders(string rootFolder); - static vector GetFilesInFolder(string rootFolder, string mask, bool recursive); + static vector GetFilesInFolder(string rootFolder, std::unordered_set extensions, bool recursive); static string GetFilename(string filepath, bool includeExtension); static string GetFolderName(string filepath); diff --git a/Utilities/IpsPatcher.cpp b/Utilities/IpsPatcher.cpp index 15d8acb0..33d86e1d 100644 --- a/Utilities/IpsPatcher.cpp +++ b/Utilities/IpsPatcher.cpp @@ -14,7 +14,7 @@ public: uint16_t RepeatCount = 0; uint8_t Value = 0; - bool ReadRecord(ifstream &ipsFile) + bool ReadRecord(std::istream &ipsFile) { uint8_t buffer[3]; @@ -60,60 +60,61 @@ public: } }; -vector IpsPatcher::PatchBuffer(string ipsFilepath, vector input) +bool IpsPatcher::PatchBuffer(string ipsFilepath, vector &input, vector &output) { ifstream ipsFile(ipsFilepath, std::ios::in | std::ios::binary); - if(ipsFile) { - char header[5]; - ipsFile.read((char*)&header, 5); - if(memcmp((char*)&header, "PATCH", 5) != 0) { - //Invalid ips file - return input; - } - - vector records; - int32_t truncateOffset = -1; - size_t maxOutputSize = input.size(); - while(!ipsFile.eof()) { - IpsRecord record; - if(record.ReadRecord(ipsFile)) { - if(record.Address + record.Length + record.RepeatCount > maxOutputSize) { - maxOutputSize = record.Address + record.Length + record.RepeatCount; - } - records.push_back(record); - } else { - //EOF, try to read truncate offset record if it exists - uint8_t buffer[3]; - ipsFile.read((char*)buffer, 3); - if(!ipsFile.eof()) { - truncateOffset = buffer[2] | (buffer[1] << 8) | (buffer[0] << 16); - } - break; - } - } - - vector output; - output.resize(maxOutputSize); - std::copy(input.begin(), input.end(), output.begin()); - - for(IpsRecord record : records) { - if(record.Length == 0) { - std::fill(&output[record.Address], &output[record.Address]+record.RepeatCount, record.Value); - } else { - std::copy(record.Replacement.begin(), record.Replacement.end(), output.begin()+record.Address); - } - } - - if(truncateOffset != -1 && (int32_t)output.size() > truncateOffset) { - output.resize(truncateOffset); - } - - ipsFile.close(); - - return output; + return PatchBuffer(ipsFile, input, output); } - return input; + return false; +} + +bool IpsPatcher::PatchBuffer(std::istream &ipsFile, vector &input, vector &output) +{ + char header[5]; + ipsFile.read((char*)&header, 5); + if(memcmp((char*)&header, "PATCH", 5) != 0) { + //Invalid ips file + return false; + } + + vector records; + int32_t truncateOffset = -1; + size_t maxOutputSize = input.size(); + while(!ipsFile.eof()) { + IpsRecord record; + if(record.ReadRecord(ipsFile)) { + if(record.Address + record.Length + record.RepeatCount > maxOutputSize) { + maxOutputSize = record.Address + record.Length + record.RepeatCount; + } + records.push_back(record); + } else { + //EOF, try to read truncate offset record if it exists + uint8_t buffer[3]; + ipsFile.read((char*)buffer, 3); + if(!ipsFile.eof()) { + truncateOffset = buffer[2] | (buffer[1] << 8) | (buffer[0] << 16); + } + break; + } + } + + output.resize(maxOutputSize); + std::copy(input.begin(), input.end(), output.begin()); + + for(IpsRecord record : records) { + if(record.Length == 0) { + std::fill(&output[record.Address], &output[record.Address]+record.RepeatCount, record.Value); + } else { + std::copy(record.Replacement.begin(), record.Replacement.end(), output.begin()+record.Address); + } + } + + if(truncateOffset != -1 && (int32_t)output.size() > truncateOffset) { + output.resize(truncateOffset); + } + + return true; } vector IpsPatcher::CreatePatch(vector originalData, vector newData) diff --git a/Utilities/IpsPatcher.h b/Utilities/IpsPatcher.h index 8aa08915..db6589e0 100644 --- a/Utilities/IpsPatcher.h +++ b/Utilities/IpsPatcher.h @@ -5,6 +5,7 @@ class IpsPatcher { public: - static vector PatchBuffer(string ipsFilepath, vector input); + static bool PatchBuffer(string ipsFilepath, vector &input, vector &output); + static bool PatchBuffer(std::istream &ipsFile, vector &input, vector &output); static vector CreatePatch(vector originalData, vector newData); }; \ No newline at end of file diff --git a/Utilities/PNGHelper.cpp b/Utilities/PNGHelper.cpp index 4d69f785..57b1227c 100644 --- a/Utilities/PNGHelper.cpp +++ b/Utilities/PNGHelper.cpp @@ -38,11 +38,31 @@ bool PNGHelper::WritePNG(string filename, uint32_t* buffer, uint32_t xSize, uint return false; } +bool PNGHelper::ReadPNG(vector input, vector &output, uint32_t &pngWidth, uint32_t &pngHeight) +{ + unsigned long width = 0; + unsigned long height = 0; + + pngWidth = 0; + pngHeight = 0; + + if(DecodePNG(output, width, height, input.data(), input.size()) == 0) { + uint32_t *pngDataPtr = (uint32_t*)output.data(); + for(size_t i = 0, len = output.size() / 4; i < len; i++) { + //ABGR to ARGB + pngDataPtr[i] = (pngDataPtr[i] & 0xFF00FF00) | ((pngDataPtr[i] & 0xFF0000) >> 16) | ((pngDataPtr[i] & 0xFF) << 16); + } + pngWidth = width; + pngHeight = height; + + return true; + } else { + return false; + } +} + bool PNGHelper::ReadPNG(string filename, vector &pngData, uint32_t &pngWidth, uint32_t &pngHeight) { - unsigned long width; - unsigned long height; - pngWidth = 0; pngHeight = 0; @@ -52,19 +72,9 @@ bool PNGHelper::ReadPNG(string filename, vector &pngData, uint32_t &png size_t fileSize = (size_t)pngFile.tellg(); pngFile.seekg(0, std::ios::beg); - uint8_t* buffer = new uint8_t[fileSize]; - pngFile.read((char*)buffer, fileSize); - DecodePNG(pngData, width, height, buffer, fileSize); - uint32_t *pngDataPtr = (uint32_t*)&pngData[0]; - for(size_t i = 0, len = pngData.size() / 4; i < len; i++) { - //ABGR to ARGB - pngDataPtr[i] = (pngDataPtr[i] & 0xFF00FF00) | ((pngDataPtr[i] & 0xFF0000) >> 16) | ((pngDataPtr[i] & 0xFF) << 16); - } - pngWidth = width; - pngHeight = height; - delete[] buffer; - - return true; + vector fileData(fileSize, 0); + pngFile.read((char*)fileData.data(), fileData.size()); + return ReadPNG(fileData, pngData, pngWidth, pngHeight); } return false; diff --git a/Utilities/PNGHelper.h b/Utilities/PNGHelper.h index f6c49e75..531a7094 100644 --- a/Utilities/PNGHelper.h +++ b/Utilities/PNGHelper.h @@ -10,4 +10,5 @@ public: static bool WritePNG(std::stringstream &stream, uint32_t* buffer, uint32_t xSize, uint32_t ySize, uint32_t bitsPerPixel = 32); static bool WritePNG(string filename, uint32_t* buffer, uint32_t xSize, uint32_t ySize, uint32_t bitsPerPixel = 32); static bool ReadPNG(string filename, vector &pngData, uint32_t &pngWidth, uint32_t &pngHeight); + static bool ReadPNG(vector input, vector &output, uint32_t &pngWidth, uint32_t &pngHeight); }; \ No newline at end of file diff --git a/Utilities/SZReader.cpp b/Utilities/SZReader.cpp index 89cd0605..982dc9a0 100644 --- a/Utilities/SZReader.cpp +++ b/Utilities/SZReader.cpp @@ -16,6 +16,11 @@ SZReader::~SZReader() bool SZReader::InternalLoadArchive(void* buffer, size_t size) { + if(_initialized) { + SzArEx_Free(&_archive, &_allocImp); + _initialized = false; + } + ISzAlloc allocImp{ SzAlloc, SzFree }; ISzAlloc allocTempImp{ SzAllocTemp, SzFreeTemp }; @@ -26,8 +31,9 @@ bool SZReader::InternalLoadArchive(void* buffer, size_t size) return !SzArEx_Open(&_archive, &_lookStream.s, &allocImp, &allocTempImp); } -void SZReader::ExtractFile(string filename, vector &output) +bool SZReader::ExtractFile(string filename, vector &output) { + bool result = false; if(_initialized) { char16_t *utf16Filename = (char16_t*)SzAlloc(nullptr, 2000); @@ -50,6 +56,7 @@ void SZReader::ExtractFile(string filename, vector &output) if(res == SZ_OK) { uint8_t* buf = new uint8_t[outSizeProcessed]; output = vector(outBuffer+offset, outBuffer+offset+outSizeProcessed); + result = true; } IAlloc_Free(&_allocImp, outBuffer); break; @@ -57,6 +64,8 @@ void SZReader::ExtractFile(string filename, vector &output) } SzFree(nullptr, utf16Filename); } + + return result; } vector SZReader::InternalGetFileList() diff --git a/Utilities/SZReader.h b/Utilities/SZReader.h index 7d41684d..9c3634f7 100644 --- a/Utilities/SZReader.h +++ b/Utilities/SZReader.h @@ -24,5 +24,5 @@ public: SZReader(); virtual ~SZReader(); - void ExtractFile(string filename, vector &output); + bool ExtractFile(string filename, vector &output); }; \ No newline at end of file diff --git a/Utilities/UpsPatcher.cpp b/Utilities/UpsPatcher.cpp index 6f267a45..3e69117d 100644 --- a/Utilities/UpsPatcher.cpp +++ b/Utilities/UpsPatcher.cpp @@ -4,7 +4,7 @@ #include "UpsPatcher.h" #include "CRC32.h" -uint64_t UpsPatcher::ReadBase128Number(ifstream &file) +uint64_t UpsPatcher::ReadBase128Number(std::istream &file) { uint64_t result = 0; int shift = 0; @@ -25,75 +25,76 @@ uint64_t UpsPatcher::ReadBase128Number(ifstream &file) return result; } -vector UpsPatcher::PatchBuffer(string upsFilepath, vector input) +bool UpsPatcher::PatchBuffer(string upsFilepath, vector &input, vector &output) { ifstream upsFile(upsFilepath, std::ios::in | std::ios::binary); - if(upsFile) { - upsFile.seekg(0, std::ios::end); - size_t fileSize = (size_t)upsFile.tellg(); - upsFile.seekg(0, std::ios::beg); - - char header[4]; - upsFile.read((char*)&header, 4); - if(memcmp((char*)&header, "UPS1", 4) != 0) { - //Invalid UPS file - return input; - } - - uint64_t inputFileSize = ReadBase128Number(upsFile); - uint64_t outputFileSize = ReadBase128Number(upsFile); - if(inputFileSize == -1 || outputFileSize == -1) { - //Invalid file - return input; - } - - vector output; - output.resize((size_t)outputFileSize); - std::copy(input.begin(), input.end(), output.begin()); - - uint32_t pos = 0; - while((size_t)upsFile.tellg() < fileSize - 12) { - uint32_t offset = (uint32_t)ReadBase128Number(upsFile); - if(offset == -1) { - //Invalid file - return input; - } - - pos += offset; - - while(true) { - uint8_t xorValue = 0; - upsFile.read((char*)&xorValue, 1); - if((size_t)upsFile.tellg() > fileSize - 12) { - //Invalid file - return input; - } - - output[pos] ^= xorValue; - pos++; - - if(!xorValue) { - break; - } - } - } - - uint8_t inputChecksum[4]; - uint8_t outputChecksum[4]; - upsFile.read((char*)inputChecksum, 4); - upsFile.read((char*)outputChecksum, 4); - uint32_t patchInputCrc = inputChecksum[0] | (inputChecksum[1] << 8) | (inputChecksum[2] << 16) | (inputChecksum[3] << 24); - uint32_t patchOutputCrc = outputChecksum[0] | (outputChecksum[1] << 8) | (outputChecksum[2] << 16) | (outputChecksum[3] << 24); - uint32_t inputCrc = CRC32::GetCRC(input.data(), input.size()); - uint32_t outputCrc = CRC32::GetCRC(output.data(), output.size()); - - if(patchInputCrc != inputCrc || patchOutputCrc != outputCrc) { - return input; - } - - upsFile.close(); - return output; + return PatchBuffer(upsFile, input, output); } - return input; + return false; +} + +bool UpsPatcher::PatchBuffer(std::istream &upsFile, vector &input, vector &output) +{ + upsFile.seekg(0, std::ios::end); + size_t fileSize = (size_t)upsFile.tellg(); + upsFile.seekg(0, std::ios::beg); + + char header[4]; + upsFile.read((char*)&header, 4); + if(memcmp((char*)&header, "UPS1", 4) != 0) { + //Invalid UPS file + return false; + } + + uint64_t inputFileSize = ReadBase128Number(upsFile); + uint64_t outputFileSize = ReadBase128Number(upsFile); + if(inputFileSize == -1 || outputFileSize == -1) { + //Invalid file + return false; + } + + output.resize((size_t)outputFileSize); + std::copy(input.begin(), input.end(), output.begin()); + + uint32_t pos = 0; + while((size_t)upsFile.tellg() < fileSize - 12) { + uint32_t offset = (uint32_t)ReadBase128Number(upsFile); + if(offset == -1) { + //Invalid file + return false; + } + + pos += offset; + + while(true) { + uint8_t xorValue = 0; + upsFile.read((char*)&xorValue, 1); + if((size_t)upsFile.tellg() > fileSize - 12) { + //Invalid file + return false; + } + + output[pos] ^= xorValue; + pos++; + + if(!xorValue) { + break; + } + } + } + + uint8_t inputChecksum[4]; + uint8_t outputChecksum[4]; + upsFile.read((char*)inputChecksum, 4); + upsFile.read((char*)outputChecksum, 4); + uint32_t patchInputCrc = inputChecksum[0] | (inputChecksum[1] << 8) | (inputChecksum[2] << 16) | (inputChecksum[3] << 24); + uint32_t patchOutputCrc = outputChecksum[0] | (outputChecksum[1] << 8) | (outputChecksum[2] << 16) | (outputChecksum[3] << 24); + uint32_t inputCrc = CRC32::GetCRC(input.data(), input.size()); + uint32_t outputCrc = CRC32::GetCRC(output.data(), output.size()); + + if(patchInputCrc != inputCrc || patchOutputCrc != outputCrc) { + return false; + } + return true; } diff --git a/Utilities/UpsPatcher.h b/Utilities/UpsPatcher.h index 5553b054..b6762b16 100644 --- a/Utilities/UpsPatcher.h +++ b/Utilities/UpsPatcher.h @@ -5,8 +5,9 @@ class UpsPatcher { private: - static uint64_t ReadBase128Number(ifstream &file); + static uint64_t ReadBase128Number(std::istream &file); public: - static vector PatchBuffer(string upsFilepath, vector input); + static bool PatchBuffer(std::istream &upsFile, vector &input, vector &output); + static bool PatchBuffer(string upsFilepath, vector &input, vector &output); }; \ No newline at end of file diff --git a/Utilities/Utilities.vcxproj b/Utilities/Utilities.vcxproj index 3c72a013..655be6bf 100644 --- a/Utilities/Utilities.vcxproj +++ b/Utilities/Utilities.vcxproj @@ -359,6 +359,7 @@ + diff --git a/Utilities/Utilities.vcxproj.filters b/Utilities/Utilities.vcxproj.filters index faad3576..8ba65399 100644 --- a/Utilities/Utilities.vcxproj.filters +++ b/Utilities/Utilities.vcxproj.filters @@ -116,9 +116,6 @@ Header Files - - Header Files - Header Files @@ -158,6 +155,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/Utilities/VirtualFile.h b/Utilities/VirtualFile.h new file mode 100644 index 00000000..eff61fdc --- /dev/null +++ b/Utilities/VirtualFile.h @@ -0,0 +1,155 @@ +#pragma once +#include "stdafx.h" +#include +#include +#include +#include "sha1.h" +#include "ArchiveReader.h" +#include "StringUtilities.h" +#include "FolderUtilities.h" +#include "BpsPatcher.h" +#include "IpsPatcher.h" +#include "UpsPatcher.h" + +class VirtualFile +{ +private: + string _path = ""; + string _innerFile = ""; + vector _data; + + void FromStream(std::istream &input, vector &output) + { + input.seekg(0, std::ios::end); + uint32_t fileSize = (uint32_t)input.tellg(); + input.seekg(0, std::ios::beg); + + output.resize(fileSize, 0); + input.read((char*)output.data(), fileSize); + } + + void LoadFile() + { + if(_data.size() == 0) { + if(!_innerFile.empty()) { + shared_ptr reader = ArchiveReader::GetReader(_path); + if(reader) { + reader->ExtractFile(_innerFile, _data); + } + } else { + ifstream input(_path, std::ios::in | std::ios::binary); + if(input.good()) { + FromStream(input, _data); + } + } + } + } + +public: + VirtualFile() + { + } + + VirtualFile(const string &archivePath, const string innerFile) + { + _path = archivePath; + _innerFile = innerFile; + } + + VirtualFile(const string &file) + { + vector tokens = StringUtilities::Split(file, '\x1'); + _path = tokens[0]; + if(tokens.size() > 1) { + _innerFile = tokens[1]; + } + } + + VirtualFile(std::istream &input) + { + FromStream(input, _data); + } + + operator std::string() const + { + if(_innerFile.empty()) { + return _path; + } else if(_path.empty()) { + throw std::runtime_error("Cannot convert to string"); + } else { + return _path + "\x1" + _innerFile; + } + } + + bool IsValid() + { + LoadFile(); + return _data.size() > 0; + } + + string GetFilePath() + { + return _path; + } + + string GetFolderPath() + { + return FolderUtilities::GetFolderName(_path); + } + + string GetFileName() + { + return _innerFile.empty() ? FolderUtilities::GetFilename(_path, false) : _innerFile; + } + + string GetSha1Hash() + { + string hash = SHA1::GetHash(_data); + std::transform(hash.begin(), hash.end(), hash.begin(), ::tolower); + return hash; + } + + bool ReadFile(vector &out) + { + LoadFile(); + if(_data.size() > 0) { + out.resize(_data.size(), 0); + std::copy(_data.begin(), _data.end(), out.begin()); + return true; + } + return false; + } + + bool ReadFile(std::stringstream &out) + { + LoadFile(); + if(_data.size() > 0) { + out.write((char*)_data.data(), _data.size()); + return true; + } + return false; + } + + bool ApplyPatch(VirtualFile &patch) + { + //Apply patch file + bool result = false; + if(patch.IsValid() && patch._data.size() >= 5 ) { + vector patchedData; + std::stringstream ss; + patch.ReadFile(ss); + + if(memcmp(patch._data.data(), "PATCH", 5) == 0) { + result = IpsPatcher::PatchBuffer(ss, _data, patchedData); + } else if(memcmp(patch._data.data(), "UPS1", 4) == 0) { + result = UpsPatcher::PatchBuffer(ss, _data, patchedData); + } else if(memcmp(patch._data.data(), "BPS1", 4) == 0) { + result = BpsPatcher::PatchBuffer(ss, _data, patchedData); + } + if(result) { + _data = patchedData; + } + } + return result; + } +}; \ No newline at end of file diff --git a/Utilities/ZipReader.cpp b/Utilities/ZipReader.cpp index 49bbe8a7..b824024a 100644 --- a/Utilities/ZipReader.cpp +++ b/Utilities/ZipReader.cpp @@ -17,6 +17,12 @@ ZipReader::~ZipReader() bool ZipReader::InternalLoadArchive(void* buffer, size_t size) { + if(_initialized) { + mz_zip_reader_end(&_zipArchive); + memset(&_zipArchive, 0, sizeof(mz_zip_archive)); + _initialized = false; + } + return mz_zip_reader_init_mem(&_zipArchive, buffer, size, 0) != 0; } @@ -36,18 +42,23 @@ vector ZipReader::InternalGetFileList() return fileList; } -void ZipReader::ExtractFile(string filename, vector &output) +bool ZipReader::ExtractFile(string filename, vector &output) { if(_initialized) { size_t uncompSize; void *p = mz_zip_reader_extract_file_to_heap(&_zipArchive, filename.c_str(), &uncompSize, 0); if(!p) { std::cout << "mz_zip_reader_extract_file_to_heap() failed!" << std::endl; + return false; } output = vector((uint8_t*)p, (uint8_t*)p + uncompSize); // We're done. mz_free(p); + + return true; } + + return false; } \ No newline at end of file diff --git a/Utilities/ZipReader.h b/Utilities/ZipReader.h index 797a17df..4d4573eb 100644 --- a/Utilities/ZipReader.h +++ b/Utilities/ZipReader.h @@ -16,5 +16,5 @@ public: ZipReader(); virtual ~ZipReader(); - void ExtractFile(string filename, vector &output); + bool ExtractFile(string filename, vector &output); }; \ No newline at end of file diff --git a/Utilities/ZipWriter.cpp b/Utilities/ZipWriter.cpp index c427c6d3..7bb27c52 100644 --- a/Utilities/ZipWriter.cpp +++ b/Utilities/ZipWriter.cpp @@ -34,7 +34,8 @@ void ZipWriter::AddFile(std::stringstream &filestream, string zipFilename) uint8_t* buffer = new uint8_t[bufferSize]; filestream.read((char*)buffer, bufferSize); - if(! mz_zip_writer_add_mem(&_zipArchive, zipFilename.c_str(), buffer, bufferSize, MZ_BEST_COMPRESSION)) { + if(!mz_zip_writer_add_mem(&_zipArchive, zipFilename.c_str(), buffer, bufferSize, MZ_BEST_COMPRESSION)) { std::cout << "mz_zip_writer_add_file() failed!" << std::endl; } + delete[] buffer; }