Code refactoring (patch/archive handling) + allow HD packs to be loaded from archives

This commit is contained in:
Souryo 2017-07-30 09:03:54 -04:00
parent 119ccd5881
commit 4455178da2
56 changed files with 890 additions and 685 deletions

View file

@ -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;

View file

@ -46,7 +46,6 @@ private:
uint32_t _chrPageNumbers[64];
HashInfo _hashInfo;
uint32_t _prgCrc32 = 0;
vector<uint8_t> _originalPrgRom;
@ -164,7 +163,6 @@ public:
GameSystem GetGameSystem();
HashInfo GetHashInfo();
uint32_t GetPrgCrc32();
string GetRomName();
RomFormat GetRomFormat();

View file

@ -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;
}
}

View file

@ -1,7 +1,6 @@
#include "stdafx.h"
#include <thread>
#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<uint8_t> 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<uint8_t> fileData;
romFile.ReadFile(fileData);
shared_ptr<BaseMapper> mapper = MapperFactory::InitializeFromFile(romFilename, fileData);
shared_ptr<BaseMapper> 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<VirtualFile>(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<uint8_t> &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;
}
}
}

View file

@ -3,6 +3,7 @@
#include "stdafx.h"
#include <atomic>
#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<uint8_t> &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();

View file

@ -413,7 +413,6 @@
<ClInclude Include="AutomaticRomTest.h" />
<ClInclude Include="BaseRenderer.h" />
<ClInclude Include="FceuxMovie.h" />
<ClInclude Include="FileLoader.h" />
<ClInclude Include="HdBuilderPpu.h" />
<ClInclude Include="HdData.h" />
<ClInclude Include="HdPackBuilder.h" />
@ -779,7 +778,6 @@
<ClCompile Include="AutomaticRomTest.cpp" />
<ClCompile Include="BaseRenderer.cpp" />
<ClCompile Include="FceuxMovie.cpp" />
<ClCompile Include="FileLoader.cpp" />
<ClCompile Include="HdNesPack.cpp" />
<ClCompile Include="HdPackBuilder.cpp" />
<ClCompile Include="HdPackLoader.cpp" />

View file

@ -1192,9 +1192,6 @@
<ClInclude Include="HdPackLoader.h">
<Filter>VideoDecoder\HD</Filter>
</ClInclude>
<ClInclude Include="FileLoader.h">
<Filter>Nes\RomLoader</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@ -1428,8 +1425,5 @@
<ClCompile Include="HdNesPack.cpp">
<Filter>VideoDecoder\HD</Filter>
</ClCompile>
<ClCompile Include="FileLoader.cpp">
<Filter>Misc</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -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<uint8_t> patchedData;
if(IpsPatcher::PatchBuffer(fdsSaveFilepath, romFile, patchedData)) {
romFile = patchedData;
}
RomData romData;

View file

@ -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<uint8_t> &fileData)
{
file.seekg(0, ios::end);
uint32_t fileSize = (uint32_t)file.tellg();
file.seekg(0, ios::beg);
fileData = vector<uint8_t>(fileSize, 0);
file.read((char*)fileData.data(), fileSize);
}
bool FileLoader::LoadFromArchive(istream &zipFile, ArchiveReader &reader, int32_t archiveFileIndex, vector<uint8_t> &fileData)
{
ReadFile(zipFile, fileData);
reader.LoadArchive(fileData.data(), fileData.size());
vector<string> 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<uint8_t> &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<uint8_t> &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<string> 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{};
}

View file

@ -1,15 +0,0 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/ArchiveReader.h"
class FileLoader
{
private:
static void ReadFile(istream &file, vector<uint8_t> &fileData);
static bool LoadFromArchive(istream &zipFile, ArchiveReader& reader, int32_t archiveFileIndex, vector<uint8_t> &fileData);
public:
static bool LoadFile(string filename, istream *filestream, int32_t archiveFileIndex, vector<uint8_t> &fileData);
static void ApplyPatch(string patchPath, vector<uint8_t> &fileData);
static vector<string> GetArchiveRomList(string filename);
};

View file

@ -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");

View file

@ -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);

View file

@ -1,5 +1,6 @@
#include <algorithm>
#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 << "<ver>100" << std::endl;
ss << "<scale>" << _hdData.Scale << std::endl;
ss << "<supportedRom>" << Console::GetRomName() << std::endl;
int tileDimension = 8 * _hdData.Scale;
int pngDimension = 16 * tileDimension;
@ -238,26 +240,6 @@ void HdPackBuilder::SaveHdPack()
};
for(std::pair<const uint32_t, std::map<uint32_t, vector<HdPackTileInfo*>>> &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<std::pair<uint32_t, HdPackTileInfo*>> tiles;

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include <algorithm>
#include <unordered_map>
#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<string> hdnPackages = FolderUtilities::GetFilesInFolder(romFile.GetFolderPath(), { ".hdn" }, false);
vector<string> 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<uint8_t> 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("<supportedrom>") != 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<uint8_t> &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<uint8_t>(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<uint8_t> 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<HdPackCondition*> conditions;
@ -51,10 +130,9 @@ bool HdPackLoader::LoadPack()
_data->Scale = std::stoi(lineContent);
} else if(lineContent.substr(0, 5) == "<img>") {
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) == "<patch>") {
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<uint8_t> 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<string> &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<uint8_t> 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<string> &tokens, vector<HdPackCondition*> conditions)
@ -231,30 +327,32 @@ void HdPackLoader::ProcessConditionTag(vector<string> &tokens)
void HdPackLoader::ProcessBackgroundTag(vector<string> &tokens, vector<HdPackCondition*> conditions)
{
HdBackgroundFileData* fileData = nullptr;
HdBackgroundFileData* bgFileData = nullptr;
for(unique_ptr<HdBackgroundFileData> &bgData : _data->BackgroundFileData) {
if(bgData->PngName == tokens[0]) {
fileData = bgData.get();
bgFileData = bgData.get();
}
}
if(!fileData) {
if(!bgFileData) {
vector<uint8_t> 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<HdBackgroundFileData>(new HdBackgroundFileData()));
fileData = _data->BackgroundFileData.back().get();
fileData->PixelData = pixelData;
fileData->Width = width;
fileData->Height = height;
fileData->PngName = tokens[0];
vector<uint8_t> fileContent;
if(LoadFile(tokens[0], fileContent)) {
if(PNGHelper::ReadPNG(fileContent, pixelData, width, height)) {
_data->BackgroundFileData.push_back(unique_ptr<HdBackgroundFileData>(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<HdPackCondition*> HdPackLoader::ParseConditionString(string conditionStri
void HdPackLoader::LoadCustomPalette()
{
string customPalettePath = FolderUtilities::CombinePath(_hdPackFolder, "palette.dat");
ifstream file(customPalettePath, ios::binary);
if(file.good()) {
vector<uint8_t> fileData;
if(LoadFile("palette.dat", fileData)) {
vector<uint32_t> 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) {

View file

@ -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<HdPackBitmapInfo> _hdNesBitmaps;
HdPackLoader(string hdPackDefinitionFile, HdPackData *data);
HdPackLoader();
bool InitializeLoader(VirtualFile &romPath, HdPackData *data);
bool LoadFile(string filename, vector<uint8_t> &fileData);
bool LoadPack();
void InitializeHdPack();
void LoadCustomPalette();
bool ProcessImgTag(string src);
void ProcessPatchTag(vector<string> &tokens);
void ProcessConditionTag(vector<string> &tokens);
void ProcessTileTag(vector<string> &tokens, vector<HdPackCondition*> conditions);

View file

@ -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);
}

View file

@ -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");
}

View file

@ -40,6 +40,7 @@ enum class BusConflictType
struct HashInfo
{
uint32_t Crc32Hash = 0;
uint32_t PrgCrc32Hash = 0;
string Sha1Hash;
string PrgChrMd5Hash;
};

View file

@ -1,21 +1,23 @@
#include "stdafx.h"
#include <algorithm>
#include <unordered_set>
#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<uint8_t> 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<uint8_t> &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<uint8_t> 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<ArchiveReader> reader = ArchiveReader::GetReader(filePath);
if(reader) {
for(string file : reader->GetFileList({ ".nes", ".fds", "*.unif", "*.unif", "*.nsf", "*.nsfe" })) {
RomLoader loader;
vector<uint8_t> 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<uint8_t> 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<string> validExtensions = { { ".nes", ".zip", ".7z", ".fds" } };
vector<string> romFiles;
for(string extension : validExtensions) {
for(string file : FolderUtilities::GetFilesInFolder(folder, extension, true)) {
romFiles.push_back(file);
}
}
std::unordered_set<string> validExtensions = { { ".nes", ".fds", "*.unif", "*.unif", "*.nsf", "*.nsfe", "*.7z", "*.zip" } };
vector<string> 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 "";
}

View file

@ -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<uint8_t> &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);
};

View file

@ -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);
}

View file

@ -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();

View file

@ -191,7 +191,7 @@ public:
uint8_t value = 0;
uint32_t crc = Console::GetPrgCrc32();
uint32_t crc = Console::GetHashInfo().PrgCrc32Hash;
switch(addr) {
case 0x4016:

View file

@ -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;
}
}
}

View file

@ -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 { }

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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<ResourcePath>
{
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<ResourcePath>.Equals(ResourcePath other)
{
return other.ToString() == this.ToString();
}
}
}

View file

@ -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<string> gameFolders = ConfigManager.Config.RecentFiles.Select(recentFile => Path.GetDirectoryName(recentFile.Path).ToLowerInvariant()).Distinct();
IEnumerable<string> gameFolders = ConfigManager.Config.RecentFiles.Select(recentFile => recentFile.RomFile.Folder.ToLowerInvariant()).Distinct();
List<string> gameRoms = new List<string>();
foreach(string folder in gameFolders) {

View file

@ -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<string> 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<string> 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;

View file

@ -676,6 +676,7 @@
</Compile>
<Compile Include="Forms\OpenSaveFileDialogExtensions.cs" />
<Compile Include="Forms\ResourceHelper.cs" />
<Compile Include="Forms\ResourcePath.cs" />
<Compile Include="GoogleDriveIntegration\CloudSyncHelper.cs" />
<Compile Include="GoogleDriveIntegration\GoogleDriveAccessor.cs" />
<Compile Include="GoogleDriveIntegration\MesenCodeReceiver.cs" />

View file

@ -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);
}

View file

@ -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<ArchiveReader> 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;

View file

@ -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<int, std::milli>(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<int, std::milli>(5000));
Stop();

View file

@ -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;
}

View file

@ -4,6 +4,8 @@
#include <sstream>
#include <algorithm>
#include "FolderUtilities.h"
#include "ZipReader.h"
#include "SZReader.h"
ArchiveReader::~ArchiveReader()
{
@ -26,13 +28,17 @@ std::stringstream ArchiveReader::GetStream(string filename)
vector<string> ArchiveReader::GetFileList(std::initializer_list<string> extensions)
{
if(extensions.size() == 0) {
return InternalGetFileList();
}
vector<string> 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> 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<ArchiveReader> 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;
}

View file

@ -17,7 +17,9 @@ public:
std::stringstream GetStream(string filename);
vector<string> GetFileList(std::initializer_list<string> extensions);
vector<string> GetFileList(std::initializer_list<string> extensions = {});
virtual void ExtractFile(string filename, vector<uint8_t> &output) = 0;
virtual bool ExtractFile(string filename, vector<uint8_t> &output) = 0;
static shared_ptr<ArchiveReader> GetReader(string filepath);
};

View file

@ -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<uint8_t> BpsPatcher::PatchBuffer(string bpsFilepath, vector<uint8_t> input)
bool BpsPatcher::PatchBuffer(string bpsFilepath, vector<uint8_t> &input, vector<uint8_t> &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<uint8_t> &input, vector<uint8_t> &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<uint8_t> 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;
}

View file

@ -5,8 +5,9 @@
class BpsPatcher
{
private:
static uint64_t ReadBase128Number(ifstream &file);
static uint64_t ReadBase128Number(std::istream &file);
public:
static vector<uint8_t> PatchBuffer(string bpsFilepath, vector<uint8_t> input);
static bool PatchBuffer(std::istream &bpsFile, vector<uint8_t> &input, vector<uint8_t> &output);
static bool PatchBuffer(string bpsFilepath, vector<uint8_t> &input, vector<uint8_t> &output);
};

View file

@ -4,6 +4,7 @@
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#include <unordered_set>
#include <algorithm>
#include "FolderUtilities.h"
#include "UTF8Util.h"
@ -120,7 +121,7 @@ vector<string> FolderUtilities::GetFolders(string rootFolder)
return folders;
}
vector<string> FolderUtilities::GetFilesInFolder(string rootFolder, string mask, bool recursive)
vector<string> FolderUtilities::GetFilesInFolder(string rootFolder, std::unordered_set<string> extensions, bool recursive)
{
vector<string> files;
vector<string> folders = { { rootFolder } };
@ -139,7 +140,7 @@ vector<string> 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());
}
}

View file

@ -1,6 +1,7 @@
#pragma once
#include "stdafx.h"
#include <unordered_set>
class FolderUtilities
{
@ -24,7 +25,7 @@ public:
static string GetRecentGamesFolder();
static vector<string> GetFolders(string rootFolder);
static vector<string> GetFilesInFolder(string rootFolder, string mask, bool recursive);
static vector<string> GetFilesInFolder(string rootFolder, std::unordered_set<string> extensions, bool recursive);
static string GetFilename(string filepath, bool includeExtension);
static string GetFolderName(string filepath);

View file

@ -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<uint8_t> IpsPatcher::PatchBuffer(string ipsFilepath, vector<uint8_t> input)
bool IpsPatcher::PatchBuffer(string ipsFilepath, vector<uint8_t> &input, vector<uint8_t> &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<IpsRecord> 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<uint8_t> 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<uint8_t> &input, vector<uint8_t> &output)
{
char header[5];
ipsFile.read((char*)&header, 5);
if(memcmp((char*)&header, "PATCH", 5) != 0) {
//Invalid ips file
return false;
}
vector<IpsRecord> 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<uint8_t> IpsPatcher::CreatePatch(vector<uint8_t> originalData, vector<uint8_t> newData)

View file

@ -5,6 +5,7 @@
class IpsPatcher
{
public:
static vector<uint8_t> PatchBuffer(string ipsFilepath, vector<uint8_t> input);
static bool PatchBuffer(string ipsFilepath, vector<uint8_t> &input, vector<uint8_t> &output);
static bool PatchBuffer(std::istream &ipsFile, vector<uint8_t> &input, vector<uint8_t> &output);
static vector<uint8_t> CreatePatch(vector<uint8_t> originalData, vector<uint8_t> newData);
};

View file

@ -38,11 +38,31 @@ bool PNGHelper::WritePNG(string filename, uint32_t* buffer, uint32_t xSize, uint
return false;
}
bool PNGHelper::ReadPNG(vector<uint8_t> input, vector<uint8_t> &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<uint8_t> &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<uint8_t> &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<uint8_t> fileData(fileSize, 0);
pngFile.read((char*)fileData.data(), fileData.size());
return ReadPNG(fileData, pngData, pngWidth, pngHeight);
}
return false;

View file

@ -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<uint8_t> &pngData, uint32_t &pngWidth, uint32_t &pngHeight);
static bool ReadPNG(vector<uint8_t> input, vector<uint8_t> &output, uint32_t &pngWidth, uint32_t &pngHeight);
};

View file

@ -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<uint8_t> &output)
bool SZReader::ExtractFile(string filename, vector<uint8_t> &output)
{
bool result = false;
if(_initialized) {
char16_t *utf16Filename = (char16_t*)SzAlloc(nullptr, 2000);
@ -50,6 +56,7 @@ void SZReader::ExtractFile(string filename, vector<uint8_t> &output)
if(res == SZ_OK) {
uint8_t* buf = new uint8_t[outSizeProcessed];
output = vector<uint8_t>(outBuffer+offset, outBuffer+offset+outSizeProcessed);
result = true;
}
IAlloc_Free(&_allocImp, outBuffer);
break;
@ -57,6 +64,8 @@ void SZReader::ExtractFile(string filename, vector<uint8_t> &output)
}
SzFree(nullptr, utf16Filename);
}
return result;
}
vector<string> SZReader::InternalGetFileList()

View file

@ -24,5 +24,5 @@ public:
SZReader();
virtual ~SZReader();
void ExtractFile(string filename, vector<uint8_t> &output);
bool ExtractFile(string filename, vector<uint8_t> &output);
};

View file

@ -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<uint8_t> UpsPatcher::PatchBuffer(string upsFilepath, vector<uint8_t> input)
bool UpsPatcher::PatchBuffer(string upsFilepath, vector<uint8_t> &input, vector<uint8_t> &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<uint8_t> 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<uint8_t> &input, vector<uint8_t> &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;
}

View file

@ -5,8 +5,9 @@
class UpsPatcher
{
private:
static uint64_t ReadBase128Number(ifstream &file);
static uint64_t ReadBase128Number(std::istream &file);
public:
static vector<uint8_t> PatchBuffer(string upsFilepath, vector<uint8_t> input);
static bool PatchBuffer(std::istream &upsFile, vector<uint8_t> &input, vector<uint8_t> &output);
static bool PatchBuffer(string upsFilepath, vector<uint8_t> &input, vector<uint8_t> &output);
};

View file

@ -359,6 +359,7 @@
<ClInclude Include="Timer.h" />
<ClInclude Include="UpsPatcher.h" />
<ClInclude Include="UTF8Util.h" />
<ClInclude Include="VirtualFile.h" />
<ClInclude Include="xBRZ\config.h" />
<ClInclude Include="xBRZ\xbrz.h" />
<ClInclude Include="ZipReader.h" />

View file

@ -116,9 +116,6 @@
<ClInclude Include="SZReader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ArchiveReader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PlatformUtilities.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -158,6 +155,12 @@
<ClInclude Include="sha1.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ArchiveReader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="VirtualFile.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">

155
Utilities/VirtualFile.h Normal file
View file

@ -0,0 +1,155 @@
#pragma once
#include "stdafx.h"
#include <algorithm>
#include <iterator>
#include <sstream>
#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<uint8_t> _data;
void FromStream(std::istream &input, vector<uint8_t> &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<ArchiveReader> 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<string> 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<uint8_t> &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<uint8_t> 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;
}
};

View file

@ -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<string> ZipReader::InternalGetFileList()
return fileList;
}
void ZipReader::ExtractFile(string filename, vector<uint8_t> &output)
bool ZipReader::ExtractFile(string filename, vector<uint8_t> &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>((uint8_t*)p, (uint8_t*)p + uncompSize);
// We're done.
mz_free(p);
return true;
}
return false;
}

View file

@ -16,5 +16,5 @@ public:
ZipReader();
virtual ~ZipReader();
void ExtractFile(string filename, vector<uint8_t> &output);
bool ExtractFile(string filename, vector<uint8_t> &output);
};

View file

@ -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;
}