diff --git a/Core/BaseMapper.cpp b/Core/BaseMapper.cpp index b49bbdf4..d495da16 100644 --- a/Core/BaseMapper.cpp +++ b/Core/BaseMapper.cpp @@ -874,7 +874,7 @@ uint32_t BaseMapper::GetMemorySize(DebugMemoryType type) uint8_t BaseMapper::GetMemoryValue(DebugMemoryType memoryType, uint32_t address) { switch(memoryType) { - case DebugMemoryType::ChrRom: return _chrRom[address]; + case DebugMemoryType::ChrRom: return _onlyChrRam ? _chrRam[address] : _chrRom[address]; case DebugMemoryType::ChrRam: return _chrRam[address]; case DebugMemoryType::SaveRam: return _saveRam[address]; case DebugMemoryType::PrgRom: return _prgRom[address]; @@ -928,6 +928,9 @@ int32_t BaseMapper::ToAbsoluteChrAddress(uint16_t addr) if(chrAddr >= _chrRom && chrAddr < _chrRom + _chrRomSize) { return (uint32_t)(chrAddr - _chrRom); } + if(chrAddr >= _chrRam && chrAddr < _chrRam + _chrRamSize) { + return (uint32_t)(chrAddr - _chrRam); + } return -1; } diff --git a/Core/BaseMapper.h b/Core/BaseMapper.h index 1d20ea41..bd94500a 100644 --- a/Core/BaseMapper.h +++ b/Core/BaseMapper.h @@ -132,8 +132,6 @@ protected: void RestoreOriginalPrgRam(); void InitializeChrRam(int32_t chrRamSize = -1); - bool HasChrRam(); - bool HasChrRom(); void AddRegisterRange(uint16_t startAddr, uint16_t endAddr, MemoryOperation operation = MemoryOperation::Any); void RemoveRegisterRange(uint16_t startAddr, uint16_t endAddr, MemoryOperation operation = MemoryOperation::Any); @@ -196,6 +194,9 @@ public: static void InitializeRam(void* data, uint32_t length); //Debugger Helper Functions + bool HasChrRam(); + bool HasChrRom(); + CartridgeState GetState(); uint8_t* GetPrgRom(); uint8_t* GetWorkRam(); diff --git a/Core/BaseRenderer.cpp b/Core/BaseRenderer.cpp index b44fd86c..9adaf790 100644 --- a/Core/BaseRenderer.cpp +++ b/Core/BaseRenderer.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include #include "BaseRenderer.h" +#include "Console.h" #include "EmulationSettings.h" #include "VideoDecoder.h" #include "PPU.h" diff --git a/Core/BaseVideoFilter.cpp b/Core/BaseVideoFilter.cpp index 9d03d85d..2c6147d1 100644 --- a/Core/BaseVideoFilter.cpp +++ b/Core/BaseVideoFilter.cpp @@ -72,15 +72,10 @@ void BaseVideoFilter::TakeScreenshot(string filename, std::stringstream *stream) memcpy(frameBuffer, GetOutputBuffer(), _bufferSize); } - //ARGB -> ABGR - for(uint32_t i = 0; i < _bufferSize / GetFrameInfo().BitsPerPixel; i++) { - frameBuffer[i] = 0xFF000000 | (frameBuffer[i] & 0xFF00) | ((frameBuffer[i] & 0xFF0000) >> 16) | ((frameBuffer[i] & 0xFF) << 16); - } - if(!filename.empty()) { - PNGHelper::WritePNG(filename, (uint8_t*)frameBuffer, GetFrameInfo().Width, GetFrameInfo().Height); + PNGHelper::WritePNG(filename, frameBuffer, GetFrameInfo().Width, GetFrameInfo().Height); } else { - PNGHelper::WritePNG(*stream, (uint8_t*)frameBuffer, GetFrameInfo().Width, GetFrameInfo().Height); + PNGHelper::WritePNG(*stream, frameBuffer, GetFrameInfo().Width, GetFrameInfo().Height); } delete[] frameBuffer; diff --git a/Core/Console.cpp b/Core/Console.cpp index de9f5b11..7d91eace 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include #include "Console.h" +#include "FileLoader.h" #include "CPU.h" #include "PPU.h" #include "APU.h" @@ -14,9 +15,11 @@ #include "MessageManager.h" #include "RomLoader.h" #include "EmulationSettings.h" +#include "../Utilities/sha1.h" #include "../Utilities/Timer.h" #include "../Utilities/FolderUtilities.h" #include "../Utilities/PlatformUtilities.h" +#include "HdBuilderPpu.h" #include "HdPpu.h" #include "NsfPpu.h" #include "SoundMixer.h" @@ -25,6 +28,7 @@ #include "MovieManager.h" #include "RewindManager.h" #include "SaveStateManager.h" +#include "HdPackBuilder.h" shared_ptr Console::Instance(new Console()); @@ -62,73 +66,82 @@ bool Console::Initialize(string romFilename, stringstream *filestream, string pa //Save current game state before loading another one SaveStateManager::SaveRecentGame(_mapper->GetRomName(), _romFilepath, _patchFilename, _archiveFileIndex); } - - shared_ptr mapper = MapperFactory::InitializeFromFile(romFilename, filestream, patchFilename, archiveFileIndex); - - 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; - - _autoSaveManager.reset(new AutoSaveManager()); - VideoDecoder::GetInstance()->StopThread(); - - _mapper = mapper; - _memoryManager.reset(new MemoryManager(_mapper)); - _cpu.reset(new CPU(_memoryManager.get())); - if(HdNesPack::HasHdPack(_romFilepath)) { - _ppu.reset(new HdPpu(_mapper.get())); - } else if(NsfMapper::GetInstance()) { - //Disable most of the PPU for NSFs - _ppu.reset(new NsfPpu(_mapper.get())); - } else { - _ppu.reset(new PPU(_mapper.get())); - } - _apu.reset(new APU(_memoryManager.get())); - - _controlManager.reset(_mapper->GetGameSystem() == GameSystem::VsUniSystem ? new VsControlManager() : new ControlManager()); - _controlManager->UpdateControlDevices(); - - _memoryManager->RegisterIODevice(_ppu.get()); - _memoryManager->RegisterIODevice(_apu.get()); - _memoryManager->RegisterIODevice(_controlManager.get()); - _memoryManager->RegisterIODevice(_mapper.get()); - - _model = NesModel::Auto; - UpdateNesModel(false); - - _initialized = true; - - if(_debugger) { - auto lock = _debuggerLock.AcquireSafe(); - StopDebugger(); - GetDebugger(); - } - - ResetComponents(false); - - _rewindManager.reset(new RewindManager()); - - VideoDecoder::GetInstance()->StartThread(); - FolderUtilities::AddKnownGameFolder(FolderUtilities::GetFolderName(romFilename)); - - string modelName = _model == NesModel::PAL ? "PAL" : (_model == NesModel::Dendy ? "Dendy" : "NTSC"); - string messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")"; - MessageManager::DisplayMessage(messageTitle, FolderUtilities::GetFilename(_mapper->GetRomName(), false)); - if(EmulationSettings::GetOverclockRate() != 100) { - MessageManager::DisplayMessage("ClockRate", std::to_string(EmulationSettings::GetOverclockRate()) + "%"); + vector fileData; + if(FileLoader::LoadFile(romFilename, filestream, archiveFileIndex, fileData)) { + LoadHdPack(romFilename, fileData, patchFilename); + if(!patchFilename.empty()) { + FileLoader::ApplyPatch(patchFilename, fileData); + } + + shared_ptr mapper = MapperFactory::InitializeFromFile(romFilename, 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; + + _autoSaveManager.reset(new AutoSaveManager()); + VideoDecoder::GetInstance()->StopThread(); + + _mapper = mapper; + _memoryManager.reset(new MemoryManager(_mapper)); + _cpu.reset(new CPU(_memoryManager.get())); + + if(_hdData) { + _ppu.reset(new HdPpu(_mapper.get())); + } else if(NsfMapper::GetInstance()) { + //Disable most of the PPU for NSFs + _ppu.reset(new NsfPpu(_mapper.get())); + } else { + _ppu.reset(new PPU(_mapper.get())); + } + + _apu.reset(new APU(_memoryManager.get())); + + _controlManager.reset(_mapper->GetGameSystem() == GameSystem::VsUniSystem ? new VsControlManager() : new ControlManager()); + _controlManager->UpdateControlDevices(); + + _memoryManager->RegisterIODevice(_ppu.get()); + _memoryManager->RegisterIODevice(_apu.get()); + _memoryManager->RegisterIODevice(_controlManager.get()); + _memoryManager->RegisterIODevice(_mapper.get()); + + _model = NesModel::Auto; + UpdateNesModel(false); + + _initialized = true; + + if(_debugger) { + auto lock = _debuggerLock.AcquireSafe(); + StopDebugger(); + GetDebugger(); + } + + ResetComponents(false); + + _rewindManager.reset(new RewindManager()); + + VideoDecoder::GetInstance()->StartThread(); + + FolderUtilities::AddKnownGameFolder(FolderUtilities::GetFolderName(romFilename)); + + string modelName = _model == NesModel::PAL ? "PAL" : (_model == NesModel::Dendy ? "Dendy" : "NTSC"); + string messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")"; + MessageManager::DisplayMessage(messageTitle, FolderUtilities::GetFilename(_mapper->GetRomName(), false)); + if(EmulationSettings::GetOverclockRate() != 100) { + MessageManager::DisplayMessage("ClockRate", std::to_string(EmulationSettings::GetOverclockRate()) + "%"); + } + return true; } - return true; - } else { - MessageManager::DisplayMessage("Error", "CouldNotLoadFile", FolderUtilities::GetFilename(romFilename, true)); - return false; } + + MessageManager::DisplayMessage("Error", "CouldNotLoadFile", FolderUtilities::GetFilename(romFilename, true)); + return false; } bool Console::LoadROM(string filepath, stringstream *filestream, int32_t archiveFileIndex, string patchFilepath) @@ -207,6 +220,15 @@ RomFormat Console::GetRomFormat() } } +bool Console::IsChrRam() +{ + if(Instance->_mapper) { + return Instance->_mapper->HasChrRam(); + } else { + return false; + } +} + uint32_t Console::GetCrc32() { if(Instance->_mapper) { @@ -264,8 +286,9 @@ void Console::ResetComponents(bool softReset) MovieManager::Stop(); if(!softReset) { SoundMixer::StopRecording(); + _hdPackBuilder.reset(); } - + _memoryManager->Reset(softReset); if(!EmulationSettings::CheckFlag(EmulationFlags::DisablePpuReset) || !softReset) { _ppu->Reset(); @@ -460,6 +483,9 @@ void Console::Run() _memoryManager.reset(); _controlManager.reset(); + _hdPackBuilder.reset(); + _hdData.reset(); + _stopLock.Release(); _runLock.Release(); } @@ -608,3 +634,62 @@ void Console::SetNextFrameOverclockStatus(bool disabled) { Instance->_disableOcNextFrame = disabled; } + +HdPackData* Console::GetHdData() +{ + return Instance->_hdData.get(); +} + +void Console::LoadHdPack(string romFilename, vector &fileData, string &patchFilename) +{ + _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())) { + _hdData.reset(); + } else { + string sha1hash = SHA1::GetHash(fileData); + auto result = _hdData->PatchesByHash.find(sha1hash); + if(result != _hdData->PatchesByHash.end()) { + patchFilename = FolderUtilities::CombinePath(hdPackFolder, result->second); + } + } + } +} + +void Console::StartRecordingHdPack(string saveFolder, ScaleFilterType filterType, uint32_t scale, uint32_t flags, uint32_t chrRamBankSize) +{ + Console::Pause(); + std::stringstream saveState; + Instance->SaveState(saveState); + + Instance->_hdPackBuilder.reset(); + Instance->_hdPackBuilder.reset(new HdPackBuilder(saveFolder, filterType, scale, flags, chrRamBankSize, !Instance->_mapper->HasChrRom())); + + Instance->_memoryManager->UnregisterIODevice(Instance->_ppu.get()); + Instance->_ppu.reset(); + Instance->_ppu.reset(new HdBuilderPpu(Instance->_mapper.get(), Instance->_hdPackBuilder.get(), chrRamBankSize)); + Instance->_memoryManager->RegisterIODevice(Instance->_ppu.get()); + + Instance->LoadState(saveState); + Console::Resume(); +} + +void Console::StopRecordingHdPack() +{ + Console::Pause(); + std::stringstream saveState; + Instance->SaveState(saveState); + + Instance->_memoryManager->UnregisterIODevice(Instance->_ppu.get()); + Instance->_ppu.reset(); + Instance->_ppu.reset(new PPU(Instance->_mapper.get())); + Instance->_memoryManager->RegisterIODevice(Instance->_ppu.get()); + + Instance->_hdPackBuilder.reset(); + + Instance->LoadState(saveState); + Console::Resume(); +} \ No newline at end of file diff --git a/Core/Console.h b/Core/Console.h index 659f0d27..b8b5a076 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -4,6 +4,7 @@ #include #include "../Utilities/SimpleLock.h" #include "RomData.h" +#include "HdData.h" class Debugger; class BaseMapper; @@ -14,7 +15,9 @@ class PPU; class MemoryManager; class ControlManager; class AutoSaveManager; +class HdPackBuilder; enum class NesModel; +enum class ScaleFilterType; class Console { @@ -36,6 +39,9 @@ class Console unique_ptr _autoSaveManager; + shared_ptr _hdPackBuilder; + unique_ptr _hdData; + NesModel _model; string _romFilepath; @@ -51,6 +57,8 @@ class Console bool _initialized = false; + void LoadHdPack(string romFilename, vector &fileData, string &patchFilename); + void ResetComponents(bool softReset); bool Initialize(string filename, stringstream *filestream = nullptr, string patchFilename = "", int32_t archiveFileIndex = -1); void UpdateNesModel(bool sendNotification); @@ -85,6 +93,7 @@ class Console static bool LoadROM(string romName, string sha1Hash); static string GetROMPath(); static string GetRomName(); + static bool IsChrRam(); static RomFormat GetRomFormat(); static uint32_t GetCrc32(); static uint32_t GetPrgCrc32(); @@ -99,6 +108,11 @@ class Console static bool IsDebuggerAttached(); + static HdPackData* GetHdData(); + + static void StartRecordingHdPack(string saveFolder, ScaleFilterType filterType, uint32_t scale, uint32_t flags, uint32_t chrRamBankSize); + static void StopRecordingHdPack(); + static shared_ptr GetInstance(); static void Release(); }; diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 254150d4..01f6de57 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -413,6 +413,11 @@ + + + + + @@ -774,6 +779,10 @@ + + + + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index bcc34346..060e1765 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1180,6 +1180,21 @@ VideoDecoder + + VideoDecoder\HD + + + VideoDecoder\HD + + + VideoDecoder\HD + + + VideoDecoder\HD + + + Nes\RomLoader + @@ -1404,5 +1419,17 @@ VideoDecoder + + VideoDecoder\HD + + + VideoDecoder\HD + + + VideoDecoder\HD + + + Misc + \ No newline at end of file diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index 4a3f9a3d..335c287b 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include +#include "../Utilities/FolderUtilities.h" #include "MessageManager.h" #include "Debugger.h" #include "Console.h" diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index a28f59f8..e00ebe5f 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -104,6 +104,17 @@ enum class RamPowerOnState Random = 2 }; +enum class ScaleFilterType +{ + xBRZ = 0, + HQX = 1, + Scale2x = 2, + _2xSai = 3, + Super2xSai = 4, + SuperEagle = 5, + Prescale = 6, +}; + enum class VideoFilterType { None = 0, diff --git a/Core/FileLoader.cpp b/Core/FileLoader.cpp new file mode 100644 index 00000000..6a7dc56c --- /dev/null +++ b/Core/FileLoader.cpp @@ -0,0 +1,112 @@ +#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 new file mode 100644 index 00000000..72bc92b5 --- /dev/null +++ b/Core/FileLoader.h @@ -0,0 +1,15 @@ +#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/HdBuilderPpu.h b/Core/HdBuilderPpu.h new file mode 100644 index 00000000..f50e49dd --- /dev/null +++ b/Core/HdBuilderPpu.h @@ -0,0 +1,109 @@ +#pragma once +#include "stdafx.h" +#include "PPU.h" +#include "HdNesPack.h" +#include "VideoDecoder.h" +#include "RewindManager.h" +#include "HdPackBuilder.h" + +class HdBuilderPpu : public PPU +{ +private: + HdPackBuilder* _hdPackBuilder; + bool _isChrRam; + bool _needChrHash = false; + uint32_t _chrRamBankSize; + uint32_t _chrRamIndexMask; + vector _bankHashes; + +protected: + void DrawPixel() + { + if(IsRenderingEnabled() || ((_state.VideoRamAddr & 0x3F00) != 0x3F00)) { + _lastSprite = nullptr; + uint32_t color = GetPixelColor(); + _currentOutputBuffer[(_scanline << 8) + _cycle - 1] = _paletteRAM[color & 0x03 ? color : 0]; + + if(_needChrHash) { + uint16_t addr = 0; + _bankHashes.clear(); + while(addr < 0x2000) { + uint32_t hash = 0; + for(uint16_t i = 0; i < _chrRamBankSize; i++) { + hash += _mapper->DebugReadVRAM(i + addr); + hash = (hash << 1) | (hash >> 31); + } + _bankHashes.push_back(hash); + addr += _chrRamBankSize; + } + _needChrHash = false; + } + + HdPpuPixelInfo tileInfo; + if(_lastSprite && _flags.SpritesEnabled) { + if(_lastSprite->AbsoluteTileAddr >= 0) { + tileInfo.Sprite.TileIndex = (_isChrRam ? (_lastSprite->TileAddr & _chrRamIndexMask) : _lastSprite->AbsoluteTileAddr) / 16; + tileInfo.Sprite.PaletteColors = ReadPaletteRAM(_lastSprite->PaletteOffset + 3) | (ReadPaletteRAM(_lastSprite->PaletteOffset + 2) << 8) | (ReadPaletteRAM(_lastSprite->PaletteOffset + 1) << 16) | 0xFF000000; + tileInfo.Sprite.IsChrRamTile = _isChrRam; + for(int i = 0; i < 16; i++) { + tileInfo.Sprite.TileData[i] = _mapper->GetMemoryValue(DebugMemoryType::ChrRom, _lastSprite->AbsoluteTileAddr / 16 * 16 + i); + } + + _hdPackBuilder->ProcessTile(_cycle - 1, _scanline, _lastSprite->AbsoluteTileAddr, tileInfo.Sprite, _mapper, false, _bankHashes[_lastSprite->TileAddr / _chrRamBankSize]); + } + } + + if(_flags.BackgroundEnabled) { + TileInfo* lastTile = &((_state.XScroll + ((_cycle - 1) & 0x07) < 8) ? _previousTile : _currentTile); + if(lastTile->AbsoluteTileAddr >= 0) { + tileInfo.Tile.TileIndex = (_isChrRam ? (lastTile->TileAddr & _chrRamIndexMask) : lastTile->AbsoluteTileAddr) / 16; + tileInfo.Tile.PaletteColors = ReadPaletteRAM(lastTile->PaletteOffset + 3) | (ReadPaletteRAM(lastTile->PaletteOffset + 2) << 8) | (ReadPaletteRAM(lastTile->PaletteOffset + 1) << 16) | (ReadPaletteRAM(0) << 24); + tileInfo.Tile.IsChrRamTile = _isChrRam; + for(int i = 0; i < 16; i++) { + tileInfo.Tile.TileData[i] = _mapper->GetMemoryValue(DebugMemoryType::ChrRom, lastTile->AbsoluteTileAddr / 16 * 16 + i); + } + + _hdPackBuilder->ProcessTile(_cycle - 1, _scanline, lastTile->AbsoluteTileAddr, tileInfo.Tile, _mapper, false, _bankHashes[lastTile->TileAddr / _chrRamBankSize]); + } + } + } else { + //"If the current VRAM address points in the range $3F00-$3FFF during forced blanking, the color indicated by this palette location will be shown on screen instead of the backdrop color." + _currentOutputBuffer[(_scanline << 8) + _cycle - 1] = _paletteRAM[_state.VideoRamAddr & 0x1F]; + } + } + + void WriteRAM(uint16_t addr, uint8_t value) + { + switch(GetRegisterID(addr)) { + case PPURegisters::VideoMemoryData: + if(_state.VideoRamAddr < 0x2000) { + _needChrHash = true; + } + break; + } + PPU::WriteRAM(addr, value); + } + + void StreamState(bool saving) + { + PPU::StreamState(saving); + if(!saving) { + _needChrHash = true; + } + } + +public: + HdBuilderPpu(BaseMapper* mapper, HdPackBuilder* hdPackBuilder, uint32_t chrRamBankSize) : PPU(mapper) + { + _hdPackBuilder = hdPackBuilder; + _chrRamBankSize = chrRamBankSize; + _chrRamIndexMask = chrRamBankSize - 1; + _isChrRam = !_mapper->HasChrRom(); + _needChrHash = true; + } + + void SendFrame() + { + PPU::SendFrame(); + } +}; \ No newline at end of file diff --git a/Core/HdData.h b/Core/HdData.h new file mode 100644 index 00000000..f7514ab2 --- /dev/null +++ b/Core/HdData.h @@ -0,0 +1,364 @@ +#pragma once +#include "stdafx.h" +#include "PPU.h" +#include "../Utilities/HexUtilities.h" + +struct HdTileKey +{ + static const uint32_t NoTile = -1; + + uint32_t PaletteColors; + uint8_t TileData[16]; + uint32_t TileIndex; + bool IsChrRamTile = false; + bool ForDefaultKey = false; + + HdTileKey GetKey(bool defaultKey) + { + if(defaultKey) { + HdTileKey copy = *this; + copy.PaletteColors = 0xFFFFFFFF; + return copy; + } else { + return *this; + } + } + + uint32_t GetHashCode() const + { + if(IsChrRamTile) { + return CalculateHash((uint8_t*)&PaletteColors, 20); + } else { + uint64_t key = TileIndex | ((uint64_t)PaletteColors << 32); + return CalculateHash((uint8_t*)&key, sizeof(key)); + } + } + + size_t operator() (const HdTileKey &tile) const { + return tile.GetHashCode(); + } + + bool operator==(const HdTileKey &other) const + { + if(IsChrRamTile) { + return memcmp((uint8_t*)&PaletteColors, (uint8_t*)&other.PaletteColors, 20) == 0; + } else { + uint64_t key = TileIndex | ((uint64_t)PaletteColors << 32); + uint64_t otherKey = other.TileIndex | ((uint64_t)other.PaletteColors << 32); + return key == otherKey; + } + } + + uint32_t CalculateHash(const uint8_t* key, size_t len) const + { + uint32_t result = 0; + for(size_t i = 0; i < len; i += 4) { + result += *((uint32_t*)key); + result = (result << 2) | (result >> 30); + key += 4; + } + return result; + } + + bool IsSpriteTile() + { + return (PaletteColors & 0xFF000000) == 0xFF000000; + } +}; + +namespace std { + template <> struct hash + { + size_t operator()(const HdTileKey& x) const + { + return x.GetHashCode(); + } + }; +} + +struct HdPpuTileInfo : public HdTileKey +{ + uint8_t OffsetX; + uint8_t OffsetY; + bool HorizontalMirroring; + bool VerticalMirroring; + bool BackgroundPriority; + uint8_t BgColorIndex; + uint8_t SpriteColorIndex; + uint8_t BgColor; + uint8_t SpriteColor; + uint8_t NametableValue; +}; + +struct HdPpuPixelInfo +{ + HdPpuTileInfo Tile; + HdPpuTileInfo Sprite; +}; + +enum class HdPackConditionType +{ + TileAtPosition, + SpriteAtPosition, + TileNearby, + SpriteNearby, +}; + +struct HdPackCondition +{ + string Name; + HdPackConditionType Type; + int32_t TileX; + int32_t TileY; + uint32_t PaletteColors; + int32_t TileIndex; + uint8_t TileData[16]; + + bool CheckCondition(HdPpuPixelInfo *screenTiles, int x, int y) + { + switch(Type) { + case HdPackConditionType::TileAtPosition: + case HdPackConditionType::SpriteAtPosition: { + int pixelIndex = (TileY << 8) + TileX; + if(pixelIndex < 0 || pixelIndex > PPU::PixelCount) { + return false; + } + + HdPpuTileInfo &tile = Type == HdPackConditionType::TileAtPosition ? screenTiles[pixelIndex].Tile : screenTiles[pixelIndex].Sprite; + if(TileIndex >= 0) { + return tile.PaletteColors == PaletteColors && tile.TileIndex == TileIndex; + } else { + return tile.PaletteColors == PaletteColors && memcmp(tile.TileData, TileData, sizeof(TileData)) == 0; + } + break; + } + + case HdPackConditionType::TileNearby: + case HdPackConditionType::SpriteNearby: { + int pixelIndex = ((y + TileY) << 8) + TileX + x; + if(pixelIndex < 0 || pixelIndex > PPU::PixelCount) { + return false; + } + + HdPpuTileInfo &tile = Type == HdPackConditionType::TileNearby ? screenTiles[pixelIndex].Tile : screenTiles[pixelIndex].Sprite; + if(TileIndex >= 0) { + return tile.TileIndex == TileIndex; + } else { + return memcmp(tile.TileData, TileData, sizeof(TileData)) == 0; + } + break; + } + } + + return false; + } + + string ToString() + { + stringstream out; + out << "" << Name << ","; + switch(Type) { + case HdPackConditionType::TileAtPosition: out << "tileAtPosition"; break; + case HdPackConditionType::SpriteAtPosition: out << "spriteAtPosition"; break; + case HdPackConditionType::TileNearby: out << "tileNearby"; break; + case HdPackConditionType::SpriteNearby: out << "spriteNearby"; break; + } + out << ","; + out << TileX << ","; + out << TileY << ","; + if(TileIndex >= 0) { + out << TileIndex << ","; + } else { + for(int i = 0; i < 16; i++) { + out << HexUtilities::ToHex(TileData[i]); + } + } + out << HexUtilities::ToHex(PaletteColors, true); + + return out.str(); + } +}; + +struct HdPackTileInfo : public HdTileKey +{ + uint32_t X; + uint32_t Y; + uint32_t BitmapIndex; + uint8_t Brightness; + bool DefaultTile; + bool Blank; + vector HdTileData; + uint32_t ChrBankId; + + vector Conditions; + + bool MatchesCondition(HdPpuPixelInfo *screenTiles, int x, int y) + { + for(HdPackCondition* condition : Conditions) { + if(!condition->CheckCondition(screenTiles, x, y)) { + return false; + } + } + return true; + } + + vector ToRgb() + { + vector rgbBuffer; + uint32_t* palette = EmulationSettings::GetRgbPalette(); + for(uint8_t i = 0; i < 8; i++) { + uint8_t lowByte = TileData[i]; + uint8_t highByte = TileData[i + 8]; + for(uint8_t j = 0; j < 8; j++) { + uint8_t color = ((lowByte >> (7 - j)) & 0x01) | (((highByte >> (7 - j)) & 0x01) << 1); + uint32_t rgbColor; + if(IsSpriteTile()) { + rgbColor = color == 0 ? 0x00FFFFFF : palette[(PaletteColors >> ((3 - color) * 8)) & 0x3F]; + } else { + rgbColor = palette[(PaletteColors >> ((3 - color) * 8)) & 0x3F]; + } + rgbBuffer.push_back(rgbColor); + } + } + + return rgbBuffer; + } + + void UpdateBlankTileFlag() + { + for(size_t i = 0; i < HdTileData.size(); i++) { + if(HdTileData[i] != HdTileData[0]) { + Blank = false; + return; + } + } + Blank = true; + } + + string ToString(int pngIndex) + { + stringstream out; + + if(Conditions.size() > 0) { + out << "["; + for(int i = 0; i < Conditions.size(); i++) { + if(i > 0) { + out << "&"; + } + out << Conditions[i]->Name; + } + out << "]"; + } + + if(IsChrRamTile) { + out << "" << pngIndex << ","; + + for(int i = 0; i < 16; i++) { + out << HexUtilities::ToHex(TileData[i]); + } + out << "," << + HexUtilities::ToHex(PaletteColors, true) << "," << + X << "," << + Y << "," << + (double)Brightness / 255 << "," << + (DefaultTile ? "Y" : "N") << "," << + ChrBankId << "," << + TileIndex; + } else { + out << "" << + pngIndex << "," << + TileIndex << "," << + HexUtilities::ToHex(PaletteColors, true) << "," << + X << "," << + Y << "," << + (double)Brightness / 255 << "," << + (DefaultTile ? "Y" : "N"); + } + + return out.str(); + } +}; + +struct HdPackBitmapInfo +{ + vector PixelData; + uint32_t Width; + uint32_t Height; +}; + +struct HdBackgroundFileData +{ + string PngName; + uint32_t Width; + uint32_t Height; + + vector PixelData; +}; + +struct HdBackgroundInfo +{ + HdBackgroundFileData* Data; + uint16_t Brightness; + vector Conditions; + + uint32_t* data() + { + return (uint32_t*)Data->PixelData.data(); + } + + string ToString() + { + stringstream out; + + if(Conditions.size() > 0) { + out << "["; + for(int i = 0; i < Conditions.size(); i++) { + if(i > 0) { + out << "&"; + } + out << Conditions[i]->Name; + } + out << "]"; + } + + out << Data->PngName << ","; + out << (Brightness / 255.0); + + return out.str(); + } +}; + +struct HdPackData +{ + vector Backgrounds; + vector> BackgroundFileData; + vector> Tiles; + vector> Conditions; + std::unordered_map> TileByKey; + std::unordered_map PatchesByHash; + vector Palette; + vector PaletteBackup; + uint32_t Scale = 1; + uint32_t Version = 0; + uint32_t OptionFlags = 0; + + HdPackData() + { + } + + HdPackData(const HdPackData&) = delete; + HdPackData& operator=(const HdPackData&) = delete; + + ~HdPackData() + { + if(PaletteBackup.size() == 0x40) { + EmulationSettings::SetRgbPalette(PaletteBackup.data()); + } + } +}; + +enum class HdPackOptions +{ + None = 0, + NoSpriteLimit = 1, +}; \ No newline at end of file diff --git a/Core/HdNesPack.cpp b/Core/HdNesPack.cpp new file mode 100644 index 00000000..7ef2619b --- /dev/null +++ b/Core/HdNesPack.cpp @@ -0,0 +1,229 @@ +#include "stdafx.h" +#include +#include +#include "HdNesPack.h" +#include "Console.h" +#include "MessageManager.h" +#include "EmulationSettings.h" +#include "HdPackLoader.h" +#include "../Utilities/FolderUtilities.h" +#include "../Utilities/PNGHelper.h" + +HdNesPack::HdNesPack() +{ +} + +HdNesPack::~HdNesPack() +{ +} + +void HdNesPack::BlendColors(uint8_t output[4], uint8_t input[4]) +{ + uint8_t alpha = input[3] + 1; + uint8_t invertedAlpha = 256 - input[3]; + output[0] = (uint8_t)((alpha * input[0] + invertedAlpha * output[0]) >> 8); + output[1] = (uint8_t)((alpha * input[1] + invertedAlpha * output[1]) >> 8); + output[2] = (uint8_t)((alpha * input[2] + invertedAlpha * output[2]) >> 8); + output[3] = 0xFF; +} + +uint32_t HdNesPack::AdjustBrightness(uint8_t input[4], uint16_t brightness) +{ + uint8_t output[4]; + output[0] = std::min(255, (brightness * input[0]) >> 8); + output[1] = std::min(255, (brightness * input[1]) >> 8); + output[2] = std::min(255, (brightness * input[2]) >> 8); + output[3] = input[3]; + return *((uint32_t*)output); +} + +void HdNesPack::DrawTile(HdPpuTileInfo &tileInfo, HdPackTileInfo &hdPackTileInfo, uint32_t *outputBuffer, uint32_t screenWidth, bool drawBackground) +{ + uint32_t scale = GetScale(); + uint32_t *bitmapData = hdPackTileInfo.HdTileData.data(); + uint32_t tileWidth = 8 * scale; + uint8_t tileOffsetX = tileInfo.HorizontalMirroring ? 7 - tileInfo.OffsetX : tileInfo.OffsetX; + uint32_t bitmapOffset = (tileInfo.OffsetY * scale) * tileWidth + tileOffsetX * scale; + int32_t bitmapSmallInc = 1; + int32_t bitmapLargeInc = tileWidth - scale; + if(tileInfo.HorizontalMirroring) { + bitmapOffset += scale - 1; + bitmapSmallInc = -1; + bitmapLargeInc = tileWidth + scale; + } + if(tileInfo.VerticalMirroring) { + bitmapOffset += tileWidth * (scale - 1); + bitmapLargeInc = (tileInfo.HorizontalMirroring ? (int32_t)scale : -(int32_t)scale) - (int32_t)tileWidth; + } + for(uint32_t y = 0; y < scale; y++) { + for(uint32_t x = 0; x < scale; x++) { + if(drawBackground) { + *outputBuffer = EmulationSettings::GetRgbPalette()[tileInfo.BgColor]; + } + if(!tileInfo.BackgroundPriority || tileInfo.BgColorIndex == 0) { + uint32_t rgbValue = AdjustBrightness((uint8_t*)(bitmapData + bitmapOffset), hdPackTileInfo.Brightness); + if((bitmapData[bitmapOffset] & 0xFF000000) == 0xFF000000) { + *outputBuffer = rgbValue; + } else if((bitmapData[bitmapOffset] & 0xFF000000) != 0) { + BlendColors((uint8_t*)outputBuffer, (uint8_t*)&rgbValue); + } + } + outputBuffer++; + bitmapOffset += bitmapSmallInc; + } + bitmapOffset += bitmapLargeInc; + outputBuffer += screenWidth - scale; + } +} + +uint32_t HdNesPack::GetScale() +{ + return Console::GetHdData()->Scale; +} + +void HdNesPack::OnBeforeApplyFilter(HdPpuPixelInfo *screenTiles) +{ + HdPackData* hdData = Console::GetHdData(); + + if(hdData->OptionFlags & (int)HdPackOptions::NoSpriteLimit) { + EmulationSettings::SetFlags(EmulationFlags::RemoveSpriteLimit | EmulationFlags::AdaptiveSpriteLimit); + } + + if(hdData->Palette.size() == 0x40) { + EmulationSettings::SetRgbPalette(hdData->Palette.data()); + } + + _backgroundIndex = -1; + for(int i = 0; i < hdData->Backgrounds.size(); i++) { + bool isMatch = true; + for(HdPackCondition* condition : hdData->Backgrounds[i].Conditions) { + if(!condition->CheckCondition(screenTiles, 0, 0)) { + isMatch = false; + break; + } + } + + if(isMatch) { + _backgroundIndex = i; + break; + } + } +} + +HdPackTileInfo * HdNesPack::GetMatchingTile(HdPpuPixelInfo *screenTiles, uint32_t x, uint32_t y, HdTileKey &key) +{ + HdPackData *hdData = Console::GetHdData(); + std::unordered_map>::const_iterator hdTile; + hdTile = hdData->TileByKey.find(key); + if(hdTile == hdData->TileByKey.end()) { + hdTile = hdData->TileByKey.find(key.GetKey(true)); + } + + if(hdTile != hdData->TileByKey.end()) { + for(HdPackTileInfo* tile : hdTile->second) { + if(tile->MatchesCondition(screenTiles, x, y)) { + return tile; + } + } + } + + return nullptr; +} + +bool HdNesPack::IsNextToSprite(HdPpuPixelInfo *screenTiles, uint32_t x, uint32_t y) +{ + bool hasNonBackgroundSurrounding = false; + auto processAdjacentTile = [&hasNonBackgroundSurrounding](HdPpuPixelInfo& pixelInfo) { + if(pixelInfo.Sprite.TileIndex == HdPpuTileInfo::NoTile || pixelInfo.Sprite.SpriteColorIndex == 0 || pixelInfo.Sprite.SpriteColor != pixelInfo.Sprite.BgColor) { + hasNonBackgroundSurrounding |= pixelInfo.Sprite.TileIndex != HdPpuTileInfo::NoTile && pixelInfo.Sprite.SpriteColorIndex != 0 || pixelInfo.Tile.BgColorIndex != 0; + } + }; + for(int i = -1; i <= 1; i++) { + for(int j = -1; j <= 1; j++) { + if(!hasNonBackgroundSurrounding) { + processAdjacentTile(screenTiles[(i + y) * 256 + j + x]); + } + } + } + return hasNonBackgroundSurrounding; +} + +uint32_t HdNesPack::GetCustomBackgroundPixel(int x, int y, int offsetX, int offsetY) +{ + HdPackData *hdData = Console::GetHdData(); + return AdjustBrightness((uint8_t*)(hdData->Backgrounds[_backgroundIndex].data() + (y * hdData->Scale + offsetY) * 256 * hdData->Scale + x * hdData->Scale + offsetX), hdData->Backgrounds[_backgroundIndex].Brightness); +} + +void HdNesPack::GetPixels(HdPpuPixelInfo *screenTiles, uint32_t x, uint32_t y, HdPpuPixelInfo &pixelInfo, uint32_t sdPixel, uint32_t *outputBuffer, uint32_t screenWidth) +{ + HdPackTileInfo *hdPackTileInfo = nullptr; + HdPackTileInfo *hdPackSpriteInfo = nullptr; + HdPackData *hdData = Console::GetHdData(); + + if(hdData->Version <= 2) { + pixelInfo.Sprite.PaletteColors &= 0xFFFFFF; + pixelInfo.Tile.PaletteColors &= 0xFFFFFF; + } + + bool hasTile = pixelInfo.Tile.TileIndex != HdPpuTileInfo::NoTile; + bool hasSprite = pixelInfo.Sprite.TileIndex != HdPpuTileInfo::NoTile; + + std::unordered_map::const_iterator hdTile; + if(hasTile) { + hdPackTileInfo = GetMatchingTile(screenTiles, x, y, pixelInfo.Tile); + } + + if(hasSprite) { + hdPackSpriteInfo = GetMatchingTile(screenTiles, x, y, pixelInfo.Sprite); + } + + bool needPixel = true; + if(hdPackSpriteInfo && pixelInfo.Sprite.BackgroundPriority && pixelInfo.Tile.BgColorIndex == 0) { + DrawTile(pixelInfo.Sprite, *hdPackSpriteInfo, outputBuffer, screenWidth, !hdPackTileInfo); + needPixel = false; + } + + bool hasCustomBackground = _backgroundIndex >= 0 && y < hdData->Backgrounds[_backgroundIndex].Data->Height; + bool hasNonBackgroundSurrounding = hasCustomBackground ? IsNextToSprite(screenTiles, x, y) : false; + if(hasCustomBackground) { + uint32_t *buffer = outputBuffer; + for(uint32_t i = 0; i < hdData->Scale; i++) { + for(uint32_t j = 0; j < hdData->Scale; j++) { + *buffer = GetCustomBackgroundPixel(x, y, j, i); + buffer++; + } + buffer += screenWidth - hdData->Scale; + } + } + + if(hdPackTileInfo) { + DrawTile(pixelInfo.Tile, *hdPackTileInfo, outputBuffer, screenWidth, true); + needPixel = false; + } + + if(needPixel || (!hdPackSpriteInfo && hasSprite && pixelInfo.Sprite.SpriteColorIndex != 0 && (!pixelInfo.Sprite.BackgroundPriority || pixelInfo.Tile.BgColorIndex == 0))) { + //Write the standard SD tile if no HD tile is present + uint32_t *buffer = outputBuffer; + + if(hasSprite && hdPackSpriteInfo) { + sdPixel = EmulationSettings::GetRgbPalette()[pixelInfo.Sprite.BgColor]; + } + + bool useCustomBackground = !hasNonBackgroundSurrounding && hasCustomBackground && (!hasSprite || pixelInfo.Sprite.SpriteColorIndex == 0 || pixelInfo.Sprite.SpriteColor == pixelInfo.Sprite.BgColor) && pixelInfo.Tile.BgColorIndex == 0; + + for(uint32_t i = 0; i < hdData->Scale; i++) { + for(uint32_t j = 0; j < hdData->Scale; j++) { + if(useCustomBackground) { + sdPixel = GetCustomBackgroundPixel(x, y, j, i); + } + *buffer = sdPixel; + buffer++; + } + buffer += screenWidth - hdData->Scale; + } + } + + if(hdPackSpriteInfo && (!pixelInfo.Sprite.BackgroundPriority || pixelInfo.Tile.BgColorIndex == 0)) { + DrawTile(pixelInfo.Sprite, *hdPackSpriteInfo, outputBuffer, screenWidth, false); + } +} diff --git a/Core/HdNesPack.h b/Core/HdNesPack.h index eb4db26f..bedb4044 100644 --- a/Core/HdNesPack.h +++ b/Core/HdNesPack.h @@ -1,279 +1,26 @@ #pragma once +#include "stdafx.h" +#include "HdData.h" -#include -#include "Console.h" -#include "MessageManager.h" -#include "EmulationSettings.h" -#include "../Utilities/FolderUtilities.h" -#include "../Utilities/PNGHelper.h" - -struct HdPpuTileInfo -{ - static const uint32_t NoTile = -1; - uint32_t TileIndex; - uint32_t PaletteColors; - uint8_t OffsetX; - uint8_t OffsetY; - bool HorizontalMirroring; - bool VerticalMirroring; - bool BackgroundPriority; - uint8_t BgColorIndex; - uint8_t BgColor; - - uint64_t GetKey(bool defaultKey) - { - if(defaultKey) { - return (uint64_t)TileIndex | 0xFFFFFFFF00000000; - } else { - return (uint64_t)TileIndex | ((uint64_t)PaletteColors << 32); - } - } -}; - -struct HdPpuPixelInfo -{ - HdPpuTileInfo Tile; - HdPpuTileInfo Sprite; -}; - -struct HdPackTileInfo -{ - uint32_t TileIndex; - uint32_t BitmapIndex; - uint32_t PaletteColors; - uint32_t X; - uint32_t Y; - bool DefaultTile; - - uint64_t GetKey(bool defaultKey) - { - if(defaultKey) { - return (uint64_t)TileIndex | 0xFFFFFFFF00000000; - } else { - return (uint64_t)TileIndex | ((uint64_t)PaletteColors << 32); - } - } -}; - -struct HdPackBitmapInfo -{ - vector PixelData; - uint32_t Width; - uint32_t Height; -}; - -class HdNesPack : public INotificationListener +class HdNesPack { private: - vector _hdNesBitmaps; - vector _hdNesTiles; - std::unordered_map _tileInfoByKey; - SimpleLock _loadLock; - uint32_t _hdScale; + int32_t _backgroundIndex = -1; - void LoadHdNesPack() - { - _loadLock.Acquire(); + void BlendColors(uint8_t output[4], uint8_t input[4]); + uint32_t AdjustBrightness(uint8_t input[4], uint16_t brightness); + void DrawTile(HdPpuTileInfo &tileInfo, HdPackTileInfo &hdPackTileInfo, uint32_t* outputBuffer, uint32_t screenWidth, bool drawBackground); - _hdNesBitmaps.clear(); - _hdNesTiles.clear(); - _tileInfoByKey.clear(); - - string hdPackFolder = FolderUtilities::CombinePath(FolderUtilities::GetHdPackFolder(), FolderUtilities::GetFilename(Console::GetRomName(), false)); - string hdPackDefinitionFile = FolderUtilities::CombinePath(hdPackFolder, "hires.txt"); - ifstream packDefinition(hdPackDefinitionFile, ios::in | ios::binary); - while(packDefinition.good()) { - string lineContent; - std::getline(packDefinition, lineContent); - lineContent = lineContent.substr(0, lineContent.length() - 1); - - if(lineContent.substr(0, 7) == "") { - lineContent = lineContent.substr(7); - _hdScale = 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); - } else if(lineContent.substr(0, 6) == "") { - lineContent = lineContent.substr(6); - vector tokens = split(lineContent, ','); - HdPackTileInfo tileInfo; - tileInfo.TileIndex = std::stoi(tokens[0]); - tileInfo.BitmapIndex = std::stoi(tokens[1]); - tileInfo.PaletteColors = std::stoi(tokens[2]) | (std::stoi(tokens[3]) << 8) | (std::stoi(tokens[4]) << 16); - tileInfo.X = std::stoi(tokens[5]); - tileInfo.Y = std::stoi(tokens[6]); - tileInfo.DefaultTile = (tokens[7] == "Y"); - _hdNesTiles.push_back(tileInfo); - } - } - - for(HdPackTileInfo &tileInfo : _hdNesTiles) { - _tileInfoByKey[tileInfo.GetKey(false)] = &tileInfo; - if(tileInfo.DefaultTile) { - _tileInfoByKey[tileInfo.GetKey(true)] = &tileInfo; - } - } - - packDefinition.close(); - - _loadLock.Release(); - } - - vector split(const string &s, char delim) - { - vector tokens; - std::stringstream ss(s); - std::string item; - while(std::getline(ss, item, delim)) { - tokens.push_back(item); - } - return tokens; - } + bool IsNextToSprite(HdPpuPixelInfo *screenTiles, uint32_t x, uint32_t y); + uint32_t GetCustomBackgroundPixel(int x, int y, int offsetX, int offsetY); public: - HdNesPack() - { - _hdScale = 2; + HdNesPack(); + ~HdNesPack(); - LoadHdNesPack(); - MessageManager::RegisterNotificationListener(this); - } + uint32_t GetScale(); - ~HdNesPack() - { - MessageManager::UnregisterNotificationListener(this); - } - - uint32_t GetScale() - { - return _hdScale; - } - - void BlendColors(uint8_t output[4], uint8_t input[4]) - { - uint8_t alpha = input[3] + 1; - uint8_t invertedAlpha = 256 - input[3]; - output[0] = (uint8_t)((alpha * input[0] + invertedAlpha * output[0]) >> 8); - output[1] = (uint8_t)((alpha * input[1] + invertedAlpha * output[1]) >> 8); - output[2] = (uint8_t)((alpha * input[2] + invertedAlpha * output[2]) >> 8); - output[3] = 0xFF; - } - - void DrawTile(HdPpuTileInfo &tileInfo, HdPackTileInfo &hdPackTileInfo, uint32_t* outputBuffer, uint32_t screenWidth, bool drawBackground) - { - HdPackBitmapInfo &bitmapInfo = _hdNesBitmaps[hdPackTileInfo.BitmapIndex]; - uint32_t* bitmapData = (uint32_t*)&bitmapInfo.PixelData[0]; - uint8_t tileOffsetX = tileInfo.HorizontalMirroring ? 7 - tileInfo.OffsetX : tileInfo.OffsetX; - uint32_t bitmapOffset = (hdPackTileInfo.Y + tileInfo.OffsetY * _hdScale) * bitmapInfo.Width + hdPackTileInfo.X + tileOffsetX * _hdScale; - int32_t bitmapSmallInc = 1; - int32_t bitmapLargeInc = bitmapInfo.Width - _hdScale; - if(tileInfo.HorizontalMirroring) { - bitmapOffset += _hdScale - 1; - bitmapSmallInc = -1; - bitmapLargeInc = bitmapInfo.Width + _hdScale; - } - if(tileInfo.VerticalMirroring) { - bitmapOffset += bitmapInfo.Width * (_hdScale - 1); - bitmapLargeInc = tileInfo.HorizontalMirroring ? -(int32_t)bitmapInfo.Width + (int32_t)_hdScale : -(int32_t)bitmapInfo.Width - (int32_t)_hdScale; - } - for(uint32_t y = 0; y < _hdScale; y++) { - for(uint32_t x = 0; x < _hdScale; x++) { - if(drawBackground) { - *outputBuffer = EmulationSettings::GetRgbPalette()[tileInfo.BgColor]; - } - if(!tileInfo.BackgroundPriority || tileInfo.BgColorIndex == 0) { - if((bitmapData[bitmapOffset] & 0xFF000000) == 0xFF000000) { - *outputBuffer = bitmapData[bitmapOffset]; - } else if((bitmapData[bitmapOffset] & 0xFF000000) != 0) { - BlendColors((uint8_t*)outputBuffer, (uint8_t*)&bitmapData[bitmapOffset]); - } - } - outputBuffer++; - bitmapOffset += bitmapSmallInc; - } - bitmapOffset += bitmapLargeInc; - outputBuffer += screenWidth - _hdScale; - } - } - - void GetPixels(HdPpuPixelInfo &pixelInfo, uint32_t sdPixel, uint32_t *outputBuffer, uint32_t screenWidth) - { - _loadLock.Acquire(); - - HdPackTileInfo *hdPackTileInfo = nullptr; - HdPackTileInfo *hdPackSpriteInfo = nullptr; - - std::unordered_map::const_iterator hdTile; - if(pixelInfo.Tile.TileIndex != HdPpuTileInfo::NoTile) { - hdTile = _tileInfoByKey.find(pixelInfo.Tile.GetKey(false)); - if(hdTile != _tileInfoByKey.end()) { - hdPackTileInfo = hdTile->second; - } else { - hdTile = _tileInfoByKey.find(pixelInfo.Tile.GetKey(true)); - if(hdTile != _tileInfoByKey.end()) { - hdPackTileInfo = hdTile->second; - } - } - } - - if(pixelInfo.Sprite.TileIndex != HdPpuTileInfo::NoTile) { - hdTile = _tileInfoByKey.find(pixelInfo.Sprite.GetKey(false)); - if(hdTile != _tileInfoByKey.end()) { - hdPackSpriteInfo = hdTile->second; - } else { - hdTile = _tileInfoByKey.find(pixelInfo.Sprite.GetKey(true)); - if(hdTile != _tileInfoByKey.end()) { - hdPackSpriteInfo = hdTile->second; - } - } - } - - if(hdPackSpriteInfo && pixelInfo.Sprite.BackgroundPriority) { - DrawTile(pixelInfo.Sprite, *hdPackSpriteInfo, outputBuffer, screenWidth, !hdPackTileInfo); - } - - if(!hdPackTileInfo && !hdPackSpriteInfo) { - //Write the standard SD tile if no HD tile is present - uint32_t *buffer = outputBuffer; - for(uint32_t y = 0; y < _hdScale; y++) { - for(uint32_t x = 0; x < _hdScale; x++) { - *buffer = sdPixel; - buffer++; - } - buffer += screenWidth - _hdScale; - } - } - - if(hdPackTileInfo) { - DrawTile(pixelInfo.Tile, *hdPackTileInfo, outputBuffer, screenWidth, true); - } - - if(hdPackSpriteInfo && (!pixelInfo.Sprite.BackgroundPriority || pixelInfo.Tile.BgColorIndex == 0)) { - DrawTile(pixelInfo.Sprite, *hdPackSpriteInfo, outputBuffer, screenWidth, !hdPackTileInfo); - } - - _loadLock.Release(); - } - - void ProcessNotification(ConsoleNotificationType type, void* parameter) override - { - if(type == ConsoleNotificationType::GameLoaded) { - LoadHdNesPack(); - } - } - - static bool HasHdPack(string romFilepath) - { - string hdPackFolder = FolderUtilities::CombinePath(FolderUtilities::GetHdPackFolder(), FolderUtilities::GetFilename(romFilepath, false)); - string hdPackDefinitionFile = FolderUtilities::CombinePath(hdPackFolder, "hires.txt"); - - if(ifstream(hdPackDefinitionFile)) { - return EmulationSettings::CheckFlag(EmulationFlags::UseHdPacks); - } else { - return false; - } - } + void OnBeforeApplyFilter(HdPpuPixelInfo *screenTiles); + HdPackTileInfo* GetMatchingTile(HdPpuPixelInfo *screenTiles, uint32_t x, uint32_t y, HdTileKey& key); + void GetPixels(HdPpuPixelInfo *screenTiles, uint32_t x, uint32_t y, HdPpuPixelInfo &pixelInfo, uint32_t sdPixel, uint32_t *outputBuffer, uint32_t screenWidth); }; diff --git a/Core/HdPackBuilder.cpp b/Core/HdPackBuilder.cpp new file mode 100644 index 00000000..c85c3ad8 --- /dev/null +++ b/Core/HdPackBuilder.cpp @@ -0,0 +1,402 @@ +#include "stdafx.h" +#include "HdPackBuilder.h" + +HdPackBuilder* HdPackBuilder::_instance = nullptr; + +enum HdPackRecordFlags +{ + None = 0, + UseLargeSprites = 1, + SortByUsageFrequency = 2, + GroupBlankTiles = 4, +}; + +HdPackBuilder::HdPackBuilder(string saveFolder, ScaleFilterType filterType, uint32_t scale, uint32_t flags, uint32_t chrRamBankSize, bool isChrRam) +{ + _saveFolder = saveFolder; + _filterType = filterType; + _chrRamBankSize = chrRamBankSize; + _flags = flags; + _isChrRam = isChrRam; + + string existingPackDefinition = FolderUtilities::CombinePath(saveFolder, "hires.txt"); + if(ifstream(existingPackDefinition)) { + HdPackLoader::LoadHdNesPack(existingPackDefinition, _hdData); + for(unique_ptr &tile : _hdData.Tiles) { + //Mark the tiles in the first PNGs as higher usage (preserves order when adding new tiles to an existing set) + AddTile(tile.get(), 0xFFFFFFFF - tile->BitmapIndex); + } + + if(_hdData.Scale != scale) { + _filterType = ScaleFilterType::Prescale; + } + } else { + _hdData.Scale = scale; + } + + _romName = FolderUtilities::GetFilename(Console::GetRomName(), false); + _instance = this; +} + +HdPackBuilder::~HdPackBuilder() +{ + SaveHdPack(); + if(_instance == this) { + _instance = nullptr; + } +} + +void HdPackBuilder::AddTile(HdPackTileInfo *tile, uint32_t usageCount) +{ + bool isTileBlank = (_flags & HdPackRecordFlags::GroupBlankTiles) ? tile->Blank : false; + + int chrBankId = isTileBlank ? 0xFFFFFFFF : tile->ChrBankId; + int palette = isTileBlank ? _blankTilePalette : tile->PaletteColors; + + if(_tilesByChrBankByPalette.find(chrBankId) == _tilesByChrBankByPalette.end()) { + _tilesByChrBankByPalette[chrBankId] = std::map>(); + } + + std::map> &paletteMap = _tilesByChrBankByPalette[chrBankId]; + if(paletteMap.find(palette) == paletteMap.end()) { + paletteMap[palette] = vector(256, nullptr); + } + + if(isTileBlank) { + paletteMap[palette][_blankTileIndex] = tile; + _blankTileIndex++; + if(_blankTileIndex == _chrRamBankSize / 16) { + _blankTileIndex = 0; + _blankTilePalette++; + } + } else { + paletteMap[palette][tile->TileIndex % 256] = tile; + } + + _tileUsageCount[tile->GetKey(false)] = usageCount; +} + +void HdPackBuilder::ProcessTile(int x, int y, uint16_t tileAddr, HdPpuTileInfo &tile, BaseMapper *mapper, bool isSprite, uint32_t chrBankHash) +{ + auto result = _tileUsageCount.find(tile.GetKey(false)); + if(result == _tileUsageCount.end()) { + //Check to see if a default tile matches + result = _tileUsageCount.find(tile.GetKey(true)); + } + + if(result == _tileUsageCount.end()) { + //First time seeing this tile/palette combination, store it + HdPackTileInfo* hdTile = new HdPackTileInfo(); + hdTile->PaletteColors = tile.PaletteColors; + hdTile->TileIndex = tile.TileIndex; + hdTile->DefaultTile = false; + hdTile->IsChrRamTile = _isChrRam; + hdTile->Brightness = 255; + hdTile->ChrBankId = _isChrRam ? chrBankHash : (tileAddr / 16 / 256); + + memcpy(hdTile->TileData, tile.TileData, 16); + GenerateHdTile(hdTile); + hdTile->UpdateBlankTileFlag(); + + _hdData.Tiles.push_back(unique_ptr(hdTile)); + AddTile(hdTile, 1); + } else { + if(result->second < 0x7FFFFFFF) { + //Increase usage count + result->second++; + } + } +} + +void HdPackBuilder::GenerateHdTile(HdPackTileInfo *tile) +{ + uint32_t hdScale = _hdData.Scale; + + vector originalTile = tile->ToRgb(); + vector hdTile(8 * 8 * hdScale*hdScale, 0); + + switch(_filterType) { + case ScaleFilterType::HQX: + hqx(hdScale, originalTile.data(), hdTile.data(), 8, 8); + break; + + case ScaleFilterType::Prescale: + hdTile.clear(); + for(uint8_t i = 0; i < 8 * hdScale; i++) { + for(uint8_t j = 0; j < 8 * hdScale; j++) { + hdTile.push_back(originalTile[i/hdScale*8+j/hdScale]); + } + } + break; + + case ScaleFilterType::Scale2x: + scale(hdScale, hdTile.data(), 8 * sizeof(uint32_t) * hdScale, originalTile.data(), 8 * sizeof(uint32_t), 4, 8, 8); + break; + + case ScaleFilterType::_2xSai: + twoxsai_generic_xrgb8888(8, 8, originalTile.data(), 8, hdTile.data(), 8 * hdScale); + break; + + case ScaleFilterType::Super2xSai: + supertwoxsai_generic_xrgb8888(8, 8, originalTile.data(), 8, hdTile.data(), 8 * hdScale); + break; + + case ScaleFilterType::SuperEagle: + supereagle_generic_xrgb8888(8, 8, originalTile.data(), 8, hdTile.data(), 8 * hdScale); + break; + + case ScaleFilterType::xBRZ: + xbrz::scale(hdScale, originalTile.data(), hdTile.data(), 8, 8, xbrz::ColorFormat::ARGB); + break; + } + + tile->HdTileData = hdTile; +} + +void HdPackBuilder::DrawTile(HdPackTileInfo *tile, int tileNumber, uint32_t *pngBuffer, int pageNumber, bool containsSpritesOnly) +{ + if(containsSpritesOnly && (_flags & HdPackRecordFlags::UseLargeSprites)) { + int row = tileNumber / 16; + int column = tileNumber % 16; + + int newColumn = column / 2 + ((row & 1) ? 8 : 0); + int newRow = (row & 0xFE) + ((column & 1) ? 1 : 0); + + tileNumber = newRow * 16 + newColumn; + } + + tileNumber += pageNumber * (256 / (0x1000 / _chrRamBankSize)); + + int tileDimension = 8 * _hdData.Scale; + int x = tileNumber % 16 * tileDimension; + int y = tileNumber / 16 * tileDimension; + + tile->X = x; + tile->Y = y; + + int pngWidth = 128 * _hdData.Scale; + int pngPos = y * pngWidth + x; + int tilePos = 0; + for(uint8_t i = 0; i < tileDimension; i++) { + for(uint8_t j = 0; j < tileDimension; j++) { + pngBuffer[pngPos] = tile->HdTileData[tilePos++]; + pngPos++; + } + pngPos += pngWidth - tileDimension; + } +} + +void HdPackBuilder::SaveHdPack() +{ + FolderUtilities::CreateFolder(_saveFolder); + + stringstream pngRows; + stringstream tileRows; + stringstream ss; + int pngIndex = 0; + ss << "100" << std::endl; + ss << "" << _hdData.Scale << std::endl; + + int tileDimension = 8 * _hdData.Scale; + int pngDimension = 16 * tileDimension; + int pngBufferSize = pngDimension * pngDimension; + uint32_t* pngBuffer = new uint32_t[pngBufferSize]; + + int maxPageNumber = 0x1000 / _chrRamBankSize; + int pageNumber = 0; + bool pngEmpty = true; + int pngNumber = 0; + + for(int i = 0; i < pngBufferSize; i++) { + pngBuffer[i] = 0xFFFF00FF; + } + + auto savePng = [&tileRows, &pngRows, &ss, &pngBuffer, &pngDimension, &pngIndex, &pngBufferSize, &pngEmpty, &pngNumber, this](uint32_t chrBankId) { + if(!pngEmpty) { + string pngName; + if(_isChrRam) { + pngName = "Chr_" + std::to_string(pngNumber) + ".png"; + } else { + pngName = "Chr_" + HexUtilities::ToHex(chrBankId) + "_" + std::to_string(pngNumber) + ".png"; + } + + tileRows << std::endl << "#" << pngName << std::endl; + tileRows << pngRows.str(); + pngRows = stringstream(); + + ss << "" << pngName << std::endl; + PNGHelper::WritePNG(FolderUtilities::CombinePath(_saveFolder, pngName), pngBuffer, pngDimension, pngDimension, 32); + pngNumber++; + pngIndex++; + + for(int i = 0; i < pngBufferSize; i++) { + pngBuffer[i] = 0xFFFF00FF; + } + pngEmpty = true; + } + }; + + 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; + for(std::pair> &paletteMap : kvp.second) { + if(paletteMap.second[i]) { + tiles.push_back({ _tileUsageCount[paletteMap.second[i]->GetKey(false)], paletteMap.second[i] }); + } + } + std::sort(tiles.begin(), tiles.end(), [=](std::pair &a, std::pair &b) { + return a.first > b.first; + }); + + int j = 0; + for(std::pair> &paletteMap : kvp.second) { + if(j < tiles.size()) { + paletteMap.second[i] = tiles[j].second; + j++; + } else { + paletteMap.second[i] = nullptr; + } + } + } + } + + if(!_isChrRam) { + pngNumber = 0; + } + + for(std::pair> &tileKvp : kvp.second) { + bool pageEmpty = true; + bool spritesOnly = true; + for(HdPackTileInfo* tileInfo : tileKvp.second) { + if(tileInfo && !tileInfo->IsSpriteTile()) { + spritesOnly = false; + } + } + + for(int i = 0; i < 256; i++) { + HdPackTileInfo* tileInfo = tileKvp.second[i]; + if(tileInfo) { + DrawTile(tileInfo, i, pngBuffer, pageNumber, spritesOnly); + + pngRows << tileInfo->ToString(pngIndex) << std::endl; + + pageEmpty = false; + pngEmpty = false; + } + } + + if(!pageEmpty) { + pageNumber++; + + if(pageNumber == maxPageNumber) { + savePng(kvp.first); + pageNumber = 0; + } + } + } + } + savePng(-1); + + for(unique_ptr &condition : _hdData.Conditions) { + ss << condition->ToString() << std::endl; + } + + for(HdBackgroundInfo &bgInfo : _hdData.Backgrounds) { + ss << bgInfo.ToString() << std::endl; + } + + ss << tileRows.str(); + + ofstream hiresFile(FolderUtilities::CombinePath(_saveFolder, "hires.txt"), ios::out); + hiresFile << ss.str(); + hiresFile.close(); + + delete[] pngBuffer; +} + +void HdPackBuilder::GetChrBankList(uint32_t *banks) +{ + Console::Pause(); + for(std::pair>> &kvp : _instance->_tilesByChrBankByPalette) { + *banks = kvp.first; + banks++; + } + *banks = -1; + Console::Resume(); +} + +void HdPackBuilder::GetBankPreview(uint32_t bankNumber, uint32_t pageNumber, uint8_t *rgbBuffer) +{ + Console::Pause(); + + for(uint32_t i = 0; i < 128 * 128 * _instance->_hdData.Scale*_instance->_hdData.Scale; i++) { + ((uint32_t*)rgbBuffer)[i] = 0xFFFF00FF; + } + + auto result = _instance->_tilesByChrBankByPalette.find(bankNumber); + if(result != _instance->_tilesByChrBankByPalette.end()) { + std::map> bankData = result->second; + + if(_instance->_flags & HdPackRecordFlags::SortByUsageFrequency) { + for(int i = 0; i < 256; i++) { + vector> tiles; + for(std::pair> &pageData : bankData) { + if(pageData.second[i]) { + tiles.push_back({ _instance->_tileUsageCount[pageData.second[i]->GetKey(false)], pageData.second[i] }); + } + } + + std::sort(tiles.begin(), tiles.end(), [=](std::pair &a, std::pair &b) { + return a.first > b.first; + }); + + int j = 0; + for(std::pair> &pageData : bankData) { + if(j < tiles.size()) { + pageData.second[i] = tiles[j].second; + j++; + } else { + pageData.second[i] = nullptr; + } + } + } + } + + bool spritesOnly = true; + for(HdPackTileInfo* tileInfo : (*bankData.begin()).second) { + if(tileInfo && !tileInfo->IsSpriteTile()) { + spritesOnly = false; + } + } + + for(int i = 0; i < 256; i++) { + HdPackTileInfo* tileInfo = (*bankData.begin()).second[i]; + if(tileInfo) { + _instance->DrawTile(tileInfo, i, (uint32_t*)rgbBuffer, 0, spritesOnly); + } + } + } + + Console::Resume(); +} diff --git a/Core/HdPackBuilder.h b/Core/HdPackBuilder.h new file mode 100644 index 00000000..adce5e96 --- /dev/null +++ b/Core/HdPackBuilder.h @@ -0,0 +1,49 @@ +#pragma once +#include "stdafx.h" +#include "../Utilities/xBRZ/xbrz.h" +#include "../Utilities/HQX/hqx.h" +#include "../Utilities/Scale2x/scalebit.h" +#include "../Utilities/KreedSaiEagle/SaiEagle.h" +#include "../Utilities/FolderUtilities.h" +#include "../Utilities/PNGHelper.h" +#include "../Utilities/HexUtilities.h" +#include "Console.h" +#include "HdPackLoader.h" +#include "HdNesPack.h" +#include "BaseMapper.h" +#include "Types.h" +#include + +class HdPackBuilder +{ +private: + static HdPackBuilder* _instance; + + HdPackData _hdData; + std::unordered_map _tileUsageCount; + std::map>> _tilesByChrBankByPalette; + bool _isChrRam; + uint32_t _chrRamBankSize; + ScaleFilterType _filterType; + string _saveFolder; + string _romName; + uint32_t _flags; + + //Used to group blank tiles together + int _blankTileIndex = 0; + int _blankTilePalette = 0; + + void AddTile(HdPackTileInfo *tile, uint32_t usageCount); + void GenerateHdTile(HdPackTileInfo *tile); + void DrawTile(HdPackTileInfo *tile, int tileIndex, uint32_t* pngBuffer, int pageNumber, bool containsSpritesOnly); + +public: + HdPackBuilder(string saveFolder, ScaleFilterType filterType, uint32_t scale, uint32_t flags, uint32_t chrRamBankSize, bool isChrRam); + ~HdPackBuilder(); + + void ProcessTile(int x, int y, uint16_t tileAddr, HdPpuTileInfo& tile, BaseMapper* mapper, bool isSprite, uint32_t chrBankHash); + void SaveHdPack(); + + static void GetChrBankList(uint32_t *banks); + static void GetBankPreview(uint32_t bankNumber, uint32_t pageNumber, uint8_t *rgbBuffer); +}; \ No newline at end of file diff --git a/Core/HdPackLoader.cpp b/Core/HdPackLoader.cpp new file mode 100644 index 00000000..1ec9fdaa --- /dev/null +++ b/Core/HdPackLoader.cpp @@ -0,0 +1,337 @@ +#include "stdafx.h" +#include +#include "../Utilities/FolderUtilities.h" +#include "../Utilities/StringUtilities.h" +#include "../Utilities/HexUtilities.h" +#include "../Utilities/PNGHelper.h" +#include "Console.h" +#include "HdPackLoader.h" + +HdPackLoader::HdPackLoader(string hdPackDefinitionFile, HdPackData *data) +{ + _hdPackDefinitionFile = hdPackDefinitionFile; + _hdPackFolder = FolderUtilities::GetFolderName(_hdPackDefinitionFile); + _data = data; +} + +bool HdPackLoader::LoadHdNesPack(string hdPackDefinitionFile, HdPackData &outData) +{ + //outData = HdPackData(); + + HdPackLoader loader(hdPackDefinitionFile, &outData); + return loader.LoadPack(); +} + +bool HdPackLoader::LoadPack() +{ + try { + ifstream packDefinition(_hdPackDefinitionFile, ios::in | ios::binary); + if(!packDefinition.good()) { + return false; + } + + while(packDefinition.good()) { + string lineContent; + std::getline(packDefinition, lineContent); + lineContent = lineContent.substr(0, lineContent.length() - 1); + + vector conditions; + if(lineContent.substr(0, 1) == "[") { + size_t endOfCondition = lineContent.find_first_of(']', 1); + conditions = ParseConditionString(lineContent.substr(1, endOfCondition - 1), _data->Conditions); + lineContent = lineContent.substr(endOfCondition + 1); + } + + vector tokens; + if(lineContent.substr(0, 5) == "") { + _data->Version = stoi(lineContent.substr(5)); + } else if(lineContent.substr(0, 7) == "") { + lineContent = lineContent.substr(7); + _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); + } else if(lineContent.substr(0, 7) == "") { + tokens = StringUtilities::Split(lineContent.substr(7), ','); + ProcessPatchTag(tokens); + } else if(lineContent.substr(0, 12) == "") { + tokens = StringUtilities::Split(lineContent.substr(12), ','); + ProcessBackgroundTag(tokens, conditions); + } else if(lineContent.substr(0, 11) == "") { + tokens = StringUtilities::Split(lineContent.substr(11), ','); + ProcessConditionTag(tokens); + } else if(lineContent.substr(0, 6) == "") { + tokens = StringUtilities::Split(lineContent.substr(6), ','); + ProcessTileTag(tokens, conditions); + } else if(lineContent.substr(0, 9) == "") { + tokens = StringUtilities::Split(lineContent.substr(9), ','); + ProcessOptionTag(tokens); + } + } + + LoadCustomPalette(); + InitializeHdPack(); + + packDefinition.close(); + return true; + } catch(std::exception ex) { + MessageManager::Log(string("[HDPack] Error loading HDPack: ") + ex.what()); + 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]))) { + 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]; +} + +void HdPackLoader::ProcessTileTag(vector &tokens, vector conditions) +{ + HdPackTileInfo *tileInfo = new HdPackTileInfo(); + int index = 0; + if(_data->Version < 100) { + tileInfo->TileIndex = std::stoi(tokens[index++]); + tileInfo->BitmapIndex = std::stoi(tokens[index++]); + tileInfo->PaletteColors = std::stoi(tokens[index + 2]) | (std::stoi(tokens[index + 1]) << 8) | (std::stoi(tokens[index]) << 16); + index += 3; + } else { + tileInfo->BitmapIndex = std::stoi(tokens[index++]); + string tileData = tokens[index++]; + if(tileData.size() >= 32) { + //CHR RAM tile, read the tile data + for(int i = 0; i < 16; i++) { + tileInfo->TileData[i] = HexUtilities::FromHex(tileData.substr(i * 2, 2)); + } + tileInfo->IsChrRamTile = true; + } else { + tileInfo->TileIndex = std::stoi(tokens[index++]); + tileInfo->IsChrRamTile = false; + } + tileInfo->PaletteColors = HexUtilities::FromHex(tokens[index++]); + } + tileInfo->X = std::stoi(tokens[index++]); + tileInfo->Y = std::stoi(tokens[index++]); + tileInfo->Conditions = conditions; + + if(_data->Version > 0) { + tileInfo->Brightness = (uint8_t)(std::stof(tokens[index++]) * 255); + } else { + tileInfo->Brightness = 255; + } + tileInfo->DefaultTile = (tokens[index++] == "Y"); + + //For CHR ROM tiles, the ID is just the bank number in chr rom (4k banks) + tileInfo->ChrBankId = tileInfo->TileIndex / 256; + + if(_data->Version < 100) { + if(tokens.size() >= 24) { + //CHR RAM tile, read the tile data + for(int i = 0; i < 16; i++) { + tileInfo->TileData[i] = std::stoi(tokens[index++]); + } + tileInfo->IsChrRamTile = true; + } else { + tileInfo->IsChrRamTile = false; + } + } else { + if(tileInfo->IsChrRamTile && tokens.size() > index) { + tileInfo->ChrBankId = std::stoul(tokens[index++]); + } + if(tileInfo->IsChrRamTile && tokens.size() > index) { + tileInfo->TileIndex = std::stoi(tokens[index++]); + } + } + + if(tileInfo->BitmapIndex > _hdNesBitmaps.size()) { + MessageManager::Log("[HDPack] Invalid bitmap index: " + std::to_string(tileInfo->BitmapIndex)); + return; + } + + HdPackBitmapInfo &bitmapInfo = _hdNesBitmaps[tileInfo->BitmapIndex]; + uint32_t bitmapOffset = tileInfo->Y * bitmapInfo.Width + tileInfo->X; + uint32_t* pngData = (uint32_t*)bitmapInfo.PixelData.data(); + for(uint32_t y = 0; y < 8 * _data->Scale; y++) { + for(uint32_t x = 0; x < 8 * _data->Scale; x++) { + tileInfo->HdTileData.push_back(pngData[bitmapOffset]); + bitmapOffset++; + } + bitmapOffset += bitmapInfo.Width - 8 * _data->Scale; + } + + tileInfo->UpdateBlankTileFlag(); + + _data->Tiles.push_back(unique_ptr(tileInfo)); +} + +void HdPackLoader::ProcessOptionTag(vector &tokens) +{ + for(string token : tokens) { + if(token == "disableSpriteLimit") { + _data->OptionFlags |= (int)HdPackOptions::NoSpriteLimit; + } + } +} + +void HdPackLoader::ProcessConditionTag(vector &tokens) +{ + HdPackCondition *condition = new HdPackCondition(); + tokens[0].erase(tokens[0].find_last_not_of(" \n\r\t") + 1); + condition->Name = tokens[0]; + + if(tokens[1] == "tileAtPosition") { + condition->Type = HdPackConditionType::TileAtPosition; + } else if(tokens[1] == "tileNearby") { + condition->Type = HdPackConditionType::TileNearby; + } else if(tokens[1] == "spriteAtPosition") { + condition->Type = HdPackConditionType::SpriteAtPosition; + } else if(tokens[1] == "spriteNearby") { + condition->Type = HdPackConditionType::SpriteNearby; + } + + int index = 2; + switch(condition->Type) { + case HdPackConditionType::TileAtPosition: + case HdPackConditionType::SpriteAtPosition: + case HdPackConditionType::TileNearby: + case HdPackConditionType::SpriteNearby: + condition->TileX = std::stoi(tokens[index++]); + condition->TileY = std::stoi(tokens[index++]); + break; + } + + string tileData = tokens[index++]; + if(tileData.size() == 32) { + //CHR RAM tile, read the tile data + for(int i = 0; i < 16; i++) { + condition->TileData[i] = HexUtilities::FromHex(tileData.substr(i * 2, 2)); + } + condition->TileIndex = -1; + } else { + condition->TileIndex = std::stoi(tokens[index++]); + } + + condition->PaletteColors = HexUtilities::FromHex(tokens[index++]); + + _data->Conditions.push_back(unique_ptr(condition)); +} + +void HdPackLoader::ProcessBackgroundTag(vector &tokens, vector conditions) +{ + HdBackgroundFileData* fileData = nullptr; + for(unique_ptr &bgData : _data->BackgroundFileData) { + if(bgData->PngName == tokens[0]) { + fileData = bgData.get(); + } + } + + if(!fileData) { + 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]; + } + } + + HdBackgroundInfo backgroundInfo; + if(fileData) { + backgroundInfo.Data = fileData; + backgroundInfo.Brightness = (uint16_t)(std::stof(tokens[1]) * 255); + backgroundInfo.Conditions = conditions; + + for(HdPackCondition* condition : backgroundInfo.Conditions) { + if(condition->Type == HdPackConditionType::SpriteNearby || condition->Type == HdPackConditionType::TileNearby) { + MessageManager::Log("[HDPack] Invalid condition type for background: " + tokens[0]); + } + } + + _data->Backgrounds.push_back(backgroundInfo); + } else { + MessageManager::Log("[HDPack] Error while loading background: " + tokens[0]); + } +} + +vector HdPackLoader::ParseConditionString(string conditionString, vector> &conditions) +{ + vector conditionNames = StringUtilities::Split(conditionString, '&'); + + vector result; + for(string conditionName : conditionNames) { + conditionName.erase(conditionName.find_last_not_of(" \n\r\t") + 1); + + bool found = false; + for(unique_ptr &condition : conditions) { + if(conditionName == condition->Name) { + result.push_back(condition.get()); + found = true; + break; + } + } + + if(!found) { + MessageManager::Log("[HDPack] Condition not found: " + conditionName); + } + } + + return result; +} + +void HdPackLoader::LoadCustomPalette() +{ + string customPalettePath = FolderUtilities::CombinePath(_hdPackFolder, "palette.dat"); + ifstream file(customPalettePath, ios::binary); + if(file.good()) { + 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]); + } + } + + if(paletteData.size() == 0x40) { + _data->PaletteBackup = vector(0x40, 0); + EmulationSettings::GetRgbPalette(_data->PaletteBackup.data()); + _data->Palette = paletteData; + } + } +} + +void HdPackLoader::InitializeHdPack() +{ + for(unique_ptr &tileInfo : _data->Tiles) { + auto tiles = _data->TileByKey.find(tileInfo->GetKey(false)); + if(tiles == _data->TileByKey.end()) { + _data->TileByKey[tileInfo->GetKey(false)] = vector(); + } + _data->TileByKey[tileInfo->GetKey(false)].push_back(tileInfo.get()); + + if(tileInfo->DefaultTile) { + auto tiles = _data->TileByKey.find(tileInfo->GetKey(true)); + if(tiles == _data->TileByKey.end()) { + _data->TileByKey[tileInfo->GetKey(false)] = vector(); + } + _data->TileByKey[tileInfo->GetKey(true)].push_back(tileInfo.get()); + } + } +} diff --git a/Core/HdPackLoader.h b/Core/HdPackLoader.h new file mode 100644 index 00000000..bf00d36d --- /dev/null +++ b/Core/HdPackLoader.h @@ -0,0 +1,29 @@ +#pragma once +#include "stdafx.h" +#include "HdData.h" + +class HdPackLoader +{ +public: + static bool LoadHdNesPack(string hdPackDefinitionFile, HdPackData &data); + +private: + HdPackData* _data; + string _hdPackDefinitionFile; + string _hdPackFolder; + vector _hdNesBitmaps; + + HdPackLoader(string hdPackDefinitionFile, HdPackData *data); + + bool LoadPack(); + void InitializeHdPack(); + void LoadCustomPalette(); + + void ProcessPatchTag(vector &tokens); + void ProcessConditionTag(vector &tokens); + void ProcessTileTag(vector &tokens, vector conditions); + void ProcessBackgroundTag(vector &tokens, vector conditions); + void ProcessOptionTag(vector& tokens); + + vector ParseConditionString(string conditionString, vector> &conditions); +}; \ No newline at end of file diff --git a/Core/HdPpu.h b/Core/HdPpu.h index 8e9a0a77..3f68f69d 100644 --- a/Core/HdPpu.h +++ b/Core/HdPpu.h @@ -10,6 +10,7 @@ class HdPpu : public PPU private: HdPpuPixelInfo* _screenTileBuffers[2]; HdPpuPixelInfo* _screenTiles; + bool _isChrRam; protected: void DrawPixel() @@ -22,40 +23,69 @@ protected: uint32_t color = GetPixelColor(); pixel = (_paletteRAM[color & 0x03 ? color : 0] & _paletteRamMask) | _intensifyColorBits; + TileInfo* lastTile = &((_state.XScroll + ((_cycle - 1) & 0x07) < 8) ? _previousTile : _currentTile); + uint32_t backgroundColor = 0; + if(_flags.BackgroundEnabled && _cycle > _minimumDrawBgCycle) { + backgroundColor = (((_state.LowBitShift << _state.XScroll) & 0x8000) >> 15) | (((_state.HighBitShift << _state.XScroll) & 0x8000) >> 14); + } + HdPpuPixelInfo &tileInfo = _screenTiles[bufferOffset]; if(_lastSprite && _flags.SpritesEnabled) { tileInfo.Sprite.TileIndex = _mapper->ToAbsoluteChrAddress(_lastSprite->TileAddr) / 16; - tileInfo.Sprite.PaletteColors = ReadPaletteRAM(_lastSprite->PaletteOffset + 1) | (ReadPaletteRAM(_lastSprite->PaletteOffset + 2) << 8) | (ReadPaletteRAM(_lastSprite->PaletteOffset + 3) << 16); + tileInfo.Sprite.PaletteColors = ReadPaletteRAM(_lastSprite->PaletteOffset + 3) | (ReadPaletteRAM(_lastSprite->PaletteOffset + 2) << 8) | (ReadPaletteRAM(_lastSprite->PaletteOffset + 1) << 16); tileInfo.Sprite.OffsetY = _lastSprite->OffsetY; + if(tileInfo.Sprite.OffsetY >= 8) { + tileInfo.Sprite.OffsetY -= 8; + } + tileInfo.Sprite.IsChrRamTile = _isChrRam; + if(_isChrRam) { + for(int i = 0; i < 16; i++) { + tileInfo.Sprite.TileData[i] = _mapper->GetMemoryValue(DebugMemoryType::ChrRom, _lastSprite->AbsoluteTileAddr / 16 * 16 + i); + } + } tileInfo.Sprite.OffsetX = _cycle - _lastSprite->SpriteX - 1; tileInfo.Sprite.HorizontalMirroring = _lastSprite->HorizontalMirror; tileInfo.Sprite.VerticalMirroring = _lastSprite->VerticalMirror; tileInfo.Sprite.BackgroundPriority = _lastSprite->BackgroundPriority; - uint32_t backgroundColor = 0; - if((_cycle > 8 || _flags.BackgroundMask) && _flags.BackgroundEnabled) { - //BackgroundMask = false: Hide background in leftmost 8 pixels of screen - backgroundColor = (((_state.LowBitShift << _state.XScroll) & 0x8000) >> 15) | (((_state.HighBitShift << _state.XScroll) & 0x8000) >> 14); + int32_t shift = (int32_t)_cycle - _lastSprite->SpriteX - 1; + if(_lastSprite->HorizontalMirror) { + tileInfo.Sprite.SpriteColorIndex = ((_lastSprite->LowByte >> shift) & 0x01) | ((_lastSprite->HighByte >> shift) & 0x01) << 1; + } else { + tileInfo.Sprite.SpriteColorIndex = ((_lastSprite->LowByte << shift) & 0x80) >> 7 | ((_lastSprite->HighByte << shift) & 0x80) >> 6; + } + + if(tileInfo.Sprite.SpriteColorIndex == 0) { + tileInfo.Sprite.SpriteColor = ReadPaletteRAM(0); + } else { + tileInfo.Sprite.SpriteColor = ReadPaletteRAM(_lastSprite->PaletteOffset + tileInfo.Sprite.SpriteColorIndex); } tileInfo.Sprite.BgColorIndex = backgroundColor; if(backgroundColor == 0) { tileInfo.Sprite.BgColor = ReadPaletteRAM(0); } else { - tileInfo.Sprite.BgColor = ReadPaletteRAM(_lastSprite->PaletteOffset + backgroundColor); + tileInfo.Sprite.BgColor = ReadPaletteRAM(lastTile->PaletteOffset + backgroundColor); } } else { tileInfo.Sprite.TileIndex = HdPpuTileInfo::NoTile; } - if(_flags.BackgroundEnabled) { - TileInfo* lastTile = &((_state.XScroll + ((_cycle - 1) & 0x07) < 8) ? _previousTile : _currentTile); + if(_flags.BackgroundEnabled && _cycle > _minimumDrawBgCycle) { + tileInfo.Tile.NametableValue = (lastTile->TileAddr / 16) & 0xFF; tileInfo.Tile.TileIndex = _mapper->ToAbsoluteChrAddress(lastTile->TileAddr) / 16; - tileInfo.Tile.PaletteColors = ReadPaletteRAM(lastTile->PaletteOffset + 1) | (ReadPaletteRAM(lastTile->PaletteOffset + 2) << 8) | (ReadPaletteRAM(lastTile->PaletteOffset + 3) << 16); + tileInfo.Tile.PaletteColors = ReadPaletteRAM(lastTile->PaletteOffset + 3) | (ReadPaletteRAM(lastTile->PaletteOffset + 2) << 8) | (ReadPaletteRAM(lastTile->PaletteOffset + 1) << 16) | (ReadPaletteRAM(0) << 24); tileInfo.Tile.OffsetY = lastTile->OffsetY; tileInfo.Tile.BackgroundPriority = false; + tileInfo.Tile.IsChrRamTile = _isChrRam; + if(_isChrRam) { + for(int i = 0; i < 16; i++) { + tileInfo.Tile.TileData[i] = _mapper->GetMemoryValue(DebugMemoryType::ChrRom, lastTile->AbsoluteTileAddr / 16 * 16 + i); + } + } + tileInfo.Tile.BgColorIndex = backgroundColor; tileInfo.Tile.OffsetX = (_state.XScroll + ((_cycle - 1) & 0x07)) & 0x07; tileInfo.Tile.HorizontalMirroring = false; tileInfo.Tile.VerticalMirroring = false; @@ -77,6 +107,7 @@ public: _screenTileBuffers[0] = new HdPpuPixelInfo[256 * 240]; _screenTileBuffers[1] = new HdPpuPixelInfo[256 * 240]; _screenTiles = _screenTileBuffers[0]; + _isChrRam = !_mapper->HasChrRom(); } ~HdPpu() diff --git a/Core/HdVideoFilter.cpp b/Core/HdVideoFilter.cpp index 9ff2c7d5..5ac98c06 100644 --- a/Core/HdVideoFilter.cpp +++ b/Core/HdVideoFilter.cpp @@ -28,11 +28,14 @@ void HdVideoFilter::ApplyFilter(uint16_t *ppuOutputBuffer) uint32_t hdScale = _hdNesPack->GetScale(); uint32_t screenWidth = overscan.GetScreenWidth() * hdScale; + _hdNesPack->OnBeforeApplyFilter(_hdScreenTiles); + for(uint32_t i = overscan.Top, iMax = 240 - overscan.Bottom; i < iMax; i++) { for(uint32_t j = overscan.Left, jMax = 256 - overscan.Right; j < jMax; j++) { uint32_t sdPixel = EmulationSettings::GetRgbPalette()[ppuOutputBuffer[i * 256 + j] & 0x3F]; //ProcessIntensifyBits(inputBuffer[i * 256 + j]); uint32_t bufferIndex = (i - overscan.Top) * screenWidth * hdScale + (j - overscan.Left) * hdScale; - _hdNesPack->GetPixels(_hdScreenTiles[i * 256 + j], sdPixel, (uint32_t*)GetOutputBuffer() + bufferIndex, screenWidth); + + _hdNesPack->GetPixels(_hdScreenTiles, j, i, _hdScreenTiles[i * 256 + j], sdPixel, (uint32_t*)GetOutputBuffer() + bufferIndex, screenWidth); } } } \ No newline at end of file diff --git a/Core/MapperFactory.cpp b/Core/MapperFactory.cpp index a1759758..889628c0 100644 --- a/Core/MapperFactory.cpp +++ b/Core/MapperFactory.cpp @@ -564,11 +564,11 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData) return nullptr; } -shared_ptr MapperFactory::InitializeFromFile(string romFilename, stringstream *filestream, string patchFilename, int32_t archiveFileIndex) +shared_ptr MapperFactory::InitializeFromFile(string romFilename, vector &fileData) { RomLoader loader; - if(loader.LoadFile(romFilename, filestream, patchFilename, archiveFileIndex)) { + if(loader.LoadFile(romFilename, fileData)) { RomData romData = loader.GetRomData(); shared_ptr mapper(GetMapperFromID(romData)); diff --git a/Core/MapperFactory.h b/Core/MapperFactory.h index 522344cf..9354e116 100644 --- a/Core/MapperFactory.h +++ b/Core/MapperFactory.h @@ -12,5 +12,5 @@ class MapperFactory static const uint16_t FdsMapperID = 65535; static const uint16_t NsfMapperID = 65534; - static shared_ptr InitializeFromFile(string romFilename, stringstream *filestream, string patchFilename, int32_t archiveFileIndex); + static shared_ptr InitializeFromFile(string romFilename, vector &fileData); }; diff --git a/Core/MemoryDumper.cpp b/Core/MemoryDumper.cpp index a25ed2bd..1bb4de13 100644 --- a/Core/MemoryDumper.cpp +++ b/Core/MemoryDumper.cpp @@ -239,23 +239,23 @@ void MemoryDumper::GetChrBank(int bankIndex, uint32_t* frameBuffer, uint8_t pale uint8_t chrBuffer[0x1000]; bool chrIsDrawn[0x1000]; bool tileUsed[0x4000]; + bool isChrRam = _mapper->GetMemorySize(DebugMemoryType::ChrRam) > 0; if(bankIndex == 0 || bankIndex == 1) { uint16_t baseAddr = bankIndex == 0 ? 0x0000 : 0x1000; for(int i = 0; i < 0x1000; i++) { chrBuffer[i] = _mapper->DebugReadVRAM(baseAddr + i); - chrIsDrawn[i] = _codeDataLogger->IsDrawn(_mapper->ToAbsoluteChrAddress(baseAddr + i)); + chrIsDrawn[i] = isChrRam ? true : _codeDataLogger->IsDrawn(_mapper->ToAbsoluteChrAddress(baseAddr + i)); } } else { int bank = bankIndex - 2; uint32_t baseAddr = bank * 0x1000; - bool useChrRam = _mapper->GetMemorySize(DebugMemoryType::ChrRam) > 0; - uint32_t chrSize = _mapper->GetMemorySize(useChrRam ? DebugMemoryType::ChrRam : DebugMemoryType::ChrRom); + uint32_t chrSize = _mapper->GetMemorySize(isChrRam ? DebugMemoryType::ChrRam : DebugMemoryType::ChrRom); vector chrData(chrSize, 0); - _mapper->CopyMemory(useChrRam ? DebugMemoryType::ChrRam : DebugMemoryType::ChrRom, chrData.data()); + _mapper->CopyMemory(isChrRam ? DebugMemoryType::ChrRam : DebugMemoryType::ChrRom, chrData.data()); for(int i = 0; i < 0x1000; i++) { chrBuffer[i] = chrData[baseAddr + i]; - chrIsDrawn[i] = useChrRam ? true : _codeDataLogger->IsDrawn(baseAddr + i); + chrIsDrawn[i] = isChrRam ? true : _codeDataLogger->IsDrawn(baseAddr + i); } } diff --git a/Core/PPU.cpp b/Core/PPU.cpp index 7c7a1295..7f4893c6 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -544,6 +544,7 @@ void PPU::LoadTileInfo() case 3: _nextTile.LowByte = _mapper->ReadVRAM(_nextTile.TileAddr); + _nextTile.AbsoluteTileAddr = _mapper->ToAbsoluteChrAddress(_nextTile.TileAddr); break; case 5: @@ -590,6 +591,7 @@ void PPU::LoadSprite(uint8_t spriteY, uint8_t tileIndex, uint8_t attributes, uin info.HighByte = _mapper->ReadVRAM(tileAddr + 8); } info.TileAddr = tileAddr; + info.AbsoluteTileAddr = _mapper->ToAbsoluteChrAddress(tileAddr); info.OffsetY = lineOffset; info.SpriteX = spriteX; diff --git a/Core/RomLoader.cpp b/Core/RomLoader.cpp index c0b045b9..d3289a9e 100644 --- a/Core/RomLoader.cpp +++ b/Core/RomLoader.cpp @@ -1,154 +1,56 @@ #include "stdafx.h" #include #include "../Utilities/FolderUtilities.h" -#include "../Utilities/ArchiveReader.h" #include "../Utilities/CRC32.h" #include "../Utilities/sha1.h" -#include "../Utilities/BpsPatcher.h" -#include "../Utilities/IpsPatcher.h" -#include "../Utilities/UpsPatcher.h" -#include "../Utilities/ZipReader.h" -#include "../Utilities/SZReader.h" #include "RomLoader.h" +#include "FileLoader.h" #include "iNesLoader.h" #include "FdsLoader.h" #include "NsfLoader.h" #include "NsfeLoader.h" #include "UnifLoader.h" -vector RomLoader::GetArchiveRomList(string filename) +bool RomLoader::LoadFile(string filename, int32_t archiveFileIndex) { - 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{}; -} - -bool RomLoader::LoadFromArchive(istream &zipFile, ArchiveReader& reader, int32_t archiveFileIndex) -{ - bool result = false; - - uint32_t fileSize; - uint8_t* buffer = ReadFile(zipFile, fileSize); - - reader.LoadArchive(buffer, fileSize); - - vector fileList = reader.GetFileList({ ".nes", ".fds", ".nsf", ".nsfe", ".unf" }); - int32_t currentIndex = 0; - if(archiveFileIndex > (int32_t)fileList.size()) { + vector fileData; + if(FileLoader::LoadFile(filename, nullptr, archiveFileIndex, fileData)) { + return LoadFile(filename, fileData); + } else { return false; } - - for(string filename : fileList) { - if(archiveFileIndex == -1 || archiveFileIndex == currentIndex) { - uint8_t* fileBuffer = nullptr; - size_t size = 0; - reader.ExtractFile(filename, &fileBuffer, size); - if(fileBuffer) { - result = LoadFromMemory(fileBuffer, size, FolderUtilities::GetFilename(filename, true)); - delete[] fileBuffer; - break; - } - } - currentIndex++; - } - - delete[] buffer; - return result; } -bool RomLoader::LoadFromStream(istream &romFile, string romName) +bool RomLoader::LoadFile(string filename, vector &fileData) { - uint32_t fileSize; - uint8_t* buffer = ReadFile(romFile, fileSize); - bool result = LoadFromMemory(buffer, fileSize, romName); - delete[] buffer; + _filename = filename; - return result; -} + string romName = FolderUtilities::GetFilename(filename, true); -uint32_t RomLoader::GetFileSize(istream &file) -{ - file.seekg(0, ios::end); - uint32_t fileSize = (uint32_t)file.tellg(); - file.seekg(0, ios::beg); - - return fileSize; -} - -uint8_t* RomLoader::ReadFile(istream &file, uint32_t &fileSize) -{ - fileSize = GetFileSize(file); - - uint8_t* buffer = new uint8_t[fileSize]; - file.read((char*)buffer, fileSize); - return buffer; -} - -void RomLoader::ApplyPatch(string patchPath, vector &data) -{ - //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) { - data = IpsPatcher::PatchBuffer(patchPath, data); - } else if(memcmp(buffer, "UPS1", 4) == 0) { - data = UpsPatcher::PatchBuffer(patchPath, data); - } else if(memcmp(buffer, "BPS1", 4) == 0) { - data = BpsPatcher::PatchBuffer(patchPath, data); - } - } -} - -bool RomLoader::LoadFromMemory(uint8_t* buffer, size_t length, string romName) -{ - vector fileData(buffer, buffer + length); - - if(!_patchFilename.empty()) { - ApplyPatch(_patchFilename, fileData); - } - - uint32_t crc = CRC32::GetCRC(buffer, length); + uint32_t crc = CRC32::GetCRC(fileData.data(), fileData.size()); MessageManager::Log(""); MessageManager::Log("Loading rom: " + romName); stringstream crcHex; crcHex << std::hex << std::uppercase << std::setfill('0') << std::setw(8) << crc; MessageManager::Log("File CRC32: 0x" + crcHex.str()); - if(memcmp(buffer, "NES\x1a", 4) == 0) { + if(memcmp(fileData.data(), "NES\x1a", 4) == 0) { iNesLoader loader; _romData = loader.LoadRom(fileData, nullptr); - } else if(memcmp(buffer, "FDS\x1a", 4) == 0 || memcmp(buffer, "\x1*NINTENDO-HVC*", 15) == 0) { + } else if(memcmp(fileData.data(), "FDS\x1a", 4) == 0 || memcmp(fileData.data(), "\x1*NINTENDO-HVC*", 15) == 0) { FdsLoader loader; _romData = loader.LoadRom(fileData, _filename); - } else if(memcmp(buffer, "NESM\x1a", 5) == 0) { + } else if(memcmp(fileData.data(), "NESM\x1a", 5) == 0) { NsfLoader loader; _romData = loader.LoadRom(fileData); - } else if(memcmp(buffer, "NSFE", 4) == 0) { + } else if(memcmp(fileData.data(), "NSFE", 4) == 0) { NsfeLoader loader; _romData = loader.LoadRom(fileData); - } else if(memcmp(buffer, "UNIF", 4) == 0) { + } else if(memcmp(fileData.data(), "UNIF", 4) == 0) { UnifLoader loader; _romData = loader.LoadRom(fileData); } else { - NESHeader header = { }; + NESHeader header = {}; if(GameDatabase::GetiNesHeader(crc, header)) { MessageManager::Log("[DB] Headerless ROM file found - using game database data."); iNesLoader loader; @@ -170,7 +72,7 @@ bool RomLoader::LoadFromMemory(uint8_t* buffer, size_t length, string romName) string name = _romData.Filename; std::transform(name.begin(), name.end(), name.begin(), ::tolower); - if(name.find("(e)") != string::npos || name.find("(australia)") != string::npos || name.find("(europe)") != string::npos || + if(name.find("(e)") != string::npos || name.find("(australia)") != string::npos || name.find("(europe)") != string::npos || name.find("(germany)") != string::npos || name.find("(spain)") != string::npos) { _romData.System = GameSystem::NesPal; } else if(name.find("(vs)") != string::npos) { @@ -181,44 +83,6 @@ bool RomLoader::LoadFromMemory(uint8_t* buffer, size_t length, string romName) return !_romData.Error; } -bool RomLoader::LoadFile(string filename, istream *filestream, string patchFilename, int32_t archiveFileIndex) -{ - _filename = filename; - _patchFilename = patchFilename; - - 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); - } else if(memcmp(header, "7z", 2) == 0) { - SZReader reader; - return LoadFromArchive(*input, reader, archiveFileIndex); - } else { - if(archiveFileIndex > 0) { - return false; - } - - return LoadFromStream(*input, FolderUtilities::GetFilename(filename, true)); - } - } - return false; -} - RomData RomLoader::GetRomData() { return _romData; @@ -226,13 +90,16 @@ RomData RomLoader::GetRomData() int32_t RomLoader::FindMatchingRomInFile(string filename, HashInfo hashInfo) { - RomLoader loader; - int32_t fileIndex = 0; - while(loader.LoadFile(filename, nullptr, "", fileIndex)) { - if(hashInfo.Crc32Hash == loader._romData.Crc32 || hashInfo.Sha1Hash.compare(loader._romData.Sha1) == 0) { - return fileIndex; + 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; + } + fileIndex++; } - fileIndex++; } return -1; } diff --git a/Core/RomLoader.h b/Core/RomLoader.h index 64b87b0f..f0ac8a9d 100644 --- a/Core/RomLoader.h +++ b/Core/RomLoader.h @@ -8,20 +8,13 @@ class RomLoader private: RomData _romData; string _filename; - string _patchFilename; - bool LoadFromArchive(istream &zipFile, ArchiveReader& reader, int32_t archiveFileIndex = -1); - bool LoadFromStream(istream &romFile, string romName); - uint32_t GetFileSize(istream &file); - - uint8_t* ReadFile(istream &file, uint32_t &fileSize); - bool LoadFromMemory(uint8_t* buffer, size_t length, string romName); static int32_t FindMatchingRomInFile(string filename, HashInfo hashInfo); - void ApplyPatch(string patchPath, vector &data); public: - bool LoadFile(string filename, istream *filestream = nullptr, string patchFilename = "", int32_t archiveFileIndex = -1); + bool LoadFile(string filename, int32_t archiveFileIndex); + bool LoadFile(string filename, vector &fileData); + RomData GetRomData(); static string FindMatchingRomInFolder(string folder, string romFilename, HashInfo hashInfo, bool useFastSearch, int32_t &archiveFileIndex); - static vector GetArchiveRomList(string filename); }; diff --git a/Core/ScaleFilter.cpp b/Core/ScaleFilter.cpp index 8f765ac7..58685ed5 100644 --- a/Core/ScaleFilter.cpp +++ b/Core/ScaleFilter.cpp @@ -66,11 +66,7 @@ void ScaleFilter::ApplyFilter(uint16_t *ppuOutputBuffer) if(_scaleFilterType == ScaleFilterType::xBRZ) { xbrz::scale(_filterScale, _decodedPpuBuffer, outputBuffer, width, height, xbrz::ColorFormat::ARGB); } else if(_scaleFilterType == ScaleFilterType::HQX) { - switch(_filterScale) { - case 2: hq2x_32(_decodedPpuBuffer, outputBuffer, width, height); break; - case 3: hq3x_32(_decodedPpuBuffer, outputBuffer, width, height); break; - case 4: hq4x_32(_decodedPpuBuffer, outputBuffer, width, height); break; - } + hqx(_filterScale, _decodedPpuBuffer, outputBuffer, width, height); } else if(_scaleFilterType == ScaleFilterType::Scale2x) { scale(_filterScale, outputBuffer, width*sizeof(uint32_t)*_filterScale, _decodedPpuBuffer, width*sizeof(uint32_t), 4, width, height); } else if(_scaleFilterType == ScaleFilterType::_2xSai) { diff --git a/Core/ScaleFilter.h b/Core/ScaleFilter.h index d3a41a50..b1b09d30 100644 --- a/Core/ScaleFilter.h +++ b/Core/ScaleFilter.h @@ -3,17 +3,6 @@ #include "stdafx.h" #include "DefaultVideoFilter.h" -enum class ScaleFilterType -{ - xBRZ, - HQX, - Scale2x, - _2xSai, - Super2xSai, - SuperEagle, - Prescale, -}; - class ScaleFilter : public DefaultVideoFilter { private: diff --git a/Core/Types.h b/Core/Types.h index 817968cf..a89c33de 100644 --- a/Core/Types.h +++ b/Core/Types.h @@ -124,8 +124,9 @@ struct TileInfo uint8_t LowByte; uint8_t HighByte; uint32_t PaletteOffset; - - uint16_t TileAddr; //used by HD ppu + uint16_t TileAddr; + + int32_t AbsoluteTileAddr; //used by HD ppu uint8_t OffsetY; //used by HD ppu }; diff --git a/Core/iNesLoader.cpp b/Core/iNesLoader.cpp index 24a07070..e99b8386 100644 --- a/Core/iNesLoader.cpp +++ b/Core/iNesLoader.cpp @@ -12,7 +12,7 @@ RomData iNesLoader::LoadRom(vector& romFile, NESHeader *preloadedHeader NESHeader header; uint8_t* buffer = romFile.data(); - uint32_t dataSize = romFile.size(); + uint32_t dataSize = (uint32_t)romFile.size(); if(preloadedHeader) { header = *preloadedHeader; header.SanitizeHeader(romFile.size() + sizeof(NESHeader)); diff --git a/GUI.NET/Config/ConfigManager.cs b/GUI.NET/Config/ConfigManager.cs index 950bc220..b7d6aac7 100644 --- a/GUI.NET/Config/ConfigManager.cs +++ b/GUI.NET/Config/ConfigManager.cs @@ -268,6 +268,18 @@ namespace Mesen.GUI.Config } } + public static string HdPackFolder + { + get + { + string hdPackFolder = Path.Combine(ConfigManager.HomeFolder, "HdPacks"); + if(!Directory.Exists(hdPackFolder)) { + Directory.CreateDirectory(hdPackFolder); + } + return hdPackFolder; + } + } + public static string ConfigFile { get diff --git a/GUI.NET/Debugger/Controls/ctrlChrViewer.Designer.cs b/GUI.NET/Debugger/Controls/ctrlChrViewer.Designer.cs index cc13a8d9..16849465 100644 --- a/GUI.NET/Debugger/Controls/ctrlChrViewer.Designer.cs +++ b/GUI.NET/Debugger/Controls/ctrlChrViewer.Designer.cs @@ -34,6 +34,7 @@ this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.lblPalette = new System.Windows.Forms.Label(); this.cboPalette = new System.Windows.Forms.ComboBox(); + this.picPaletteTooltip = new System.Windows.Forms.PictureBox(); this.chkLargeSprites = new System.Windows.Forms.CheckBox(); this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); this.lblChrSelection = new System.Windows.Forms.Label(); @@ -57,11 +58,13 @@ this.picChrBank1 = new System.Windows.Forms.PictureBox(); this.picChrBank2 = new System.Windows.Forms.PictureBox(); this.toolTip = new System.Windows.Forms.ToolTip(this.components); - this.picPaletteTooltip = new System.Windows.Forms.PictureBox(); + this.ctxMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.mnuCopyHdPack = new System.Windows.Forms.ToolStripMenuItem(); this.tableLayoutPanel3.SuspendLayout(); this.grpDisplayOptions.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picPaletteTooltip)).BeginInit(); this.flowLayoutPanel2.SuspendLayout(); this.flpHighlight.SuspendLayout(); this.grpTileInfo.SuspendLayout(); @@ -73,7 +76,7 @@ this.tableLayoutPanel2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.picChrBank1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.picChrBank2)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.picPaletteTooltip)).BeginInit(); + this.ctxMenu.SuspendLayout(); this.SuspendLayout(); // // tableLayoutPanel3 @@ -166,6 +169,18 @@ this.cboPalette.TabIndex = 1; this.cboPalette.SelectedIndexChanged += new System.EventHandler(this.cboPalette_SelectedIndexChanged); // + // picPaletteTooltip + // + this.picPaletteTooltip.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.picPaletteTooltip.Image = global::Mesen.GUI.Properties.Resources.Help; + this.picPaletteTooltip.Location = new System.Drawing.Point(103, 7); + this.picPaletteTooltip.Margin = new System.Windows.Forms.Padding(3, 4, 3, 3); + this.picPaletteTooltip.Name = "picPaletteTooltip"; + this.picPaletteTooltip.Size = new System.Drawing.Size(14, 14); + this.picPaletteTooltip.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.picPaletteTooltip.TabIndex = 16; + this.picPaletteTooltip.TabStop = false; + // // chkLargeSprites // this.chkLargeSprites.AutoSize = true; @@ -406,6 +421,7 @@ // picChrBank1 // this.picChrBank1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.picChrBank1.ContextMenuStrip = this.ctxMenu; this.picChrBank1.Cursor = System.Windows.Forms.Cursors.Hand; this.picChrBank1.Location = new System.Drawing.Point(1, 1); this.picChrBank1.Margin = new System.Windows.Forms.Padding(1); @@ -420,6 +436,7 @@ // picChrBank2 // this.picChrBank2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.picChrBank2.ContextMenuStrip = this.ctxMenu; this.picChrBank2.Cursor = System.Windows.Forms.Cursors.Hand; this.picChrBank2.Location = new System.Drawing.Point(1, 260); this.picChrBank2.Margin = new System.Windows.Forms.Padding(1); @@ -437,17 +454,20 @@ this.toolTip.InitialDelay = 10; this.toolTip.ReshowDelay = 10; // - // picPaletteTooltip + // ctxMenu // - this.picPaletteTooltip.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.picPaletteTooltip.Image = global::Mesen.GUI.Properties.Resources.Help; - this.picPaletteTooltip.Location = new System.Drawing.Point(103, 7); - this.picPaletteTooltip.Margin = new System.Windows.Forms.Padding(3, 4, 3, 3); - this.picPaletteTooltip.Name = "picPaletteTooltip"; - this.picPaletteTooltip.Size = new System.Drawing.Size(14, 14); - this.picPaletteTooltip.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; - this.picPaletteTooltip.TabIndex = 16; - this.picPaletteTooltip.TabStop = false; + this.ctxMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuCopyHdPack}); + this.ctxMenu.Name = "ctxMenu"; + this.ctxMenu.Size = new System.Drawing.Size(222, 48); + this.ctxMenu.Opening += new System.ComponentModel.CancelEventHandler(this.ctxMenu_Opening); + // + // mnuCopyHdPack + // + this.mnuCopyHdPack.Name = "mnuCopyHdPack"; + this.mnuCopyHdPack.Size = new System.Drawing.Size(221, 22); + this.mnuCopyHdPack.Text = "Copy Tile (HD Pack Format)"; + this.mnuCopyHdPack.Click += new System.EventHandler(this.mnuCopyHdPack_Click); // // ctrlChrViewer // @@ -462,6 +482,7 @@ this.tableLayoutPanel1.PerformLayout(); this.flowLayoutPanel1.ResumeLayout(false); this.flowLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picPaletteTooltip)).EndInit(); this.flowLayoutPanel2.ResumeLayout(false); this.flowLayoutPanel2.PerformLayout(); this.flpHighlight.ResumeLayout(false); @@ -476,7 +497,7 @@ this.tableLayoutPanel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.picChrBank1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.picChrBank2)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.picPaletteTooltip)).EndInit(); + this.ctxMenu.ResumeLayout(false); this.ResumeLayout(false); } @@ -513,5 +534,7 @@ private System.Windows.Forms.PictureBox picTileTooltip; private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.PictureBox picPaletteTooltip; + private System.Windows.Forms.ContextMenuStrip ctxMenu; + private System.Windows.Forms.ToolStripMenuItem mnuCopyHdPack; } } diff --git a/GUI.NET/Debugger/Controls/ctrlChrViewer.cs b/GUI.NET/Debugger/Controls/ctrlChrViewer.cs index f50ef1a0..f4842bf6 100644 --- a/GUI.NET/Debugger/Controls/ctrlChrViewer.cs +++ b/GUI.NET/Debugger/Controls/ctrlChrViewer.cs @@ -415,5 +415,34 @@ namespace Mesen.GUI.Debugger.Controls _selectedColor = color; RefreshPalettePicker(); } + + string _copyData; + private void mnuCopyHdPack_Click(object sender, EventArgs e) + { + Clipboard.SetText(_copyData); + } + + private void ctxMenu_Opening(object sender, CancelEventArgs e) + { + int baseAddress = _bottomBank ? 0x1000 : 0x0000; + bool ppuMemory = this.cboChrSelection.SelectedIndex == 0; + if(this.cboChrSelection.SelectedIndex > 1) { + baseAddress += (this.cboChrSelection.SelectedIndex - 1) * 0x2000; + } + + int tileIndex = GetLargeSpriteIndex(_tileIndex); + + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < 16; i++) { + sb.Append(InteropEmu.DebugGetMemoryValue(ppuMemory ? DebugMemoryType.PpuMemory : DebugMemoryType.ChrRom, (UInt32)(baseAddress + tileIndex * 16 + i)).ToString("X2")); + } + sb.Append(","); + for(int i = 1; i < 4; i++) { + sb.Append(InteropEmu.DebugGetMemoryValue(DebugMemoryType.PaletteMemory, (uint)(this._selectedPalette * 4 + i)).ToString("X2")); + } + + _copyData = sb.ToString(); + _copyData = _copyData.Substring(0, _copyData.Length - 1); + } } } diff --git a/GUI.NET/Debugger/Controls/ctrlChrViewer.resx b/GUI.NET/Debugger/Controls/ctrlChrViewer.resx index 8766f298..018e5a50 100644 --- a/GUI.NET/Debugger/Controls/ctrlChrViewer.resx +++ b/GUI.NET/Debugger/Controls/ctrlChrViewer.resx @@ -117,7 +117,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + 17, 17 + + 116, 17 + \ No newline at end of file diff --git a/GUI.NET/Debugger/Controls/ctrlNametableViewer.Designer.cs b/GUI.NET/Debugger/Controls/ctrlNametableViewer.Designer.cs index 1cc68c02..73c269a3 100644 --- a/GUI.NET/Debugger/Controls/ctrlNametableViewer.Designer.cs +++ b/GUI.NET/Debugger/Controls/ctrlNametableViewer.Designer.cs @@ -27,10 +27,14 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.picNametable = new System.Windows.Forms.PictureBox(); this.grpTileInfo = new System.Windows.Forms.GroupBox(); this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.txtPpuAddress = new System.Windows.Forms.TextBox(); + this.txtLocation = new System.Windows.Forms.TextBox(); + this.txtNametable = new System.Windows.Forms.TextBox(); this.txtPaletteAddress = new System.Windows.Forms.TextBox(); this.txtAttributeAddress = new System.Windows.Forms.TextBox(); this.txtAttributeData = new System.Windows.Forms.TextBox(); @@ -43,22 +47,22 @@ this.lblTile = new System.Windows.Forms.Label(); this.txtTileIndex = new System.Windows.Forms.TextBox(); this.picTile = new System.Windows.Forms.PictureBox(); + this.lblNametableIndex = new System.Windows.Forms.Label(); + this.lblLocation = new System.Windows.Forms.Label(); + this.lblPpuAddress = new System.Windows.Forms.Label(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.chkShowPpuScrollOverlay = new System.Windows.Forms.CheckBox(); this.chkShowTileGrid = new System.Windows.Forms.CheckBox(); this.chkShowAttributeGrid = new System.Windows.Forms.CheckBox(); - this.lblNametableIndex = new System.Windows.Forms.Label(); - this.lblLocation = new System.Windows.Forms.Label(); - this.txtNametable = new System.Windows.Forms.TextBox(); - this.txtLocation = new System.Windows.Forms.TextBox(); - this.lblPpuAddress = new System.Windows.Forms.Label(); - this.txtPpuAddress = new System.Windows.Forms.TextBox(); + this.ctxMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.mnuCopyHdPack = new System.Windows.Forms.ToolStripMenuItem(); this.tableLayoutPanel1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.picNametable)).BeginInit(); this.grpTileInfo.SuspendLayout(); this.tableLayoutPanel2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.picTile)).BeginInit(); this.flowLayoutPanel1.SuspendLayout(); + this.ctxMenu.SuspendLayout(); this.SuspendLayout(); // // tableLayoutPanel1 @@ -82,6 +86,7 @@ // picNametable // this.picNametable.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.picNametable.ContextMenuStrip = this.ctxMenu; this.picNametable.Location = new System.Drawing.Point(1, 1); this.picNametable.Margin = new System.Windows.Forms.Padding(1); this.picNametable.Name = "picNametable"; @@ -138,9 +143,33 @@ this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel2.Size = new System.Drawing.Size(169, 282); + this.tableLayoutPanel2.Size = new System.Drawing.Size(169, 311); this.tableLayoutPanel2.TabIndex = 0; // + // txtPpuAddress + // + this.txtPpuAddress.Location = new System.Drawing.Point(84, 3); + this.txtPpuAddress.Name = "txtPpuAddress"; + this.txtPpuAddress.ReadOnly = true; + this.txtPpuAddress.Size = new System.Drawing.Size(42, 20); + this.txtPpuAddress.TabIndex = 18; + // + // txtLocation + // + this.txtLocation.Location = new System.Drawing.Point(84, 55); + this.txtLocation.Name = "txtLocation"; + this.txtLocation.ReadOnly = true; + this.txtLocation.Size = new System.Drawing.Size(42, 20); + this.txtLocation.TabIndex = 16; + // + // txtNametable + // + this.txtNametable.Location = new System.Drawing.Point(84, 29); + this.txtNametable.Name = "txtNametable"; + this.txtNametable.ReadOnly = true; + this.txtNametable.Size = new System.Drawing.Size(26, 20); + this.txtNametable.TabIndex = 15; + // // txtPaletteAddress // this.txtPaletteAddress.Location = new System.Drawing.Point(84, 185); @@ -250,12 +279,42 @@ this.picTile.TabIndex = 12; this.picTile.TabStop = false; // + // lblNametableIndex + // + this.lblNametableIndex.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblNametableIndex.AutoSize = true; + this.lblNametableIndex.Location = new System.Drawing.Point(3, 32); + this.lblNametableIndex.Name = "lblNametableIndex"; + this.lblNametableIndex.Size = new System.Drawing.Size(61, 13); + this.lblNametableIndex.TabIndex = 13; + this.lblNametableIndex.Text = "Nametable:"; + // + // lblLocation + // + this.lblLocation.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblLocation.AutoSize = true; + this.lblLocation.Location = new System.Drawing.Point(3, 58); + this.lblLocation.Name = "lblLocation"; + this.lblLocation.Size = new System.Drawing.Size(51, 13); + this.lblLocation.TabIndex = 14; + this.lblLocation.Text = "Location:"; + // + // lblPpuAddress + // + this.lblPpuAddress.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblPpuAddress.AutoSize = true; + this.lblPpuAddress.Location = new System.Drawing.Point(3, 6); + this.lblPpuAddress.Name = "lblPpuAddress"; + this.lblPpuAddress.Size = new System.Drawing.Size(57, 13); + this.lblPpuAddress.TabIndex = 17; + this.lblPpuAddress.Text = "PPU Addr:"; + // // flowLayoutPanel1 // this.flowLayoutPanel1.Controls.Add(this.chkShowPpuScrollOverlay); this.flowLayoutPanel1.Controls.Add(this.chkShowTileGrid); this.flowLayoutPanel1.Controls.Add(this.chkShowAttributeGrid); - this.flowLayoutPanel1.Location = new System.Drawing.Point(516, 307); + this.flowLayoutPanel1.Location = new System.Drawing.Point(516, 336); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(181, 100); @@ -294,59 +353,20 @@ this.chkShowAttributeGrid.UseVisualStyleBackColor = true; this.chkShowAttributeGrid.Click += new System.EventHandler(this.chkShowAttributeGrid_Click); // - // lblNametableIndex + // ctxMenu // - this.lblNametableIndex.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.lblNametableIndex.AutoSize = true; - this.lblNametableIndex.Location = new System.Drawing.Point(3, 32); - this.lblNametableIndex.Name = "lblNametableIndex"; - this.lblNametableIndex.Size = new System.Drawing.Size(61, 13); - this.lblNametableIndex.TabIndex = 13; - this.lblNametableIndex.Text = "Nametable:"; + this.ctxMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuCopyHdPack}); + this.ctxMenu.Name = "ctxMenu"; + this.ctxMenu.Size = new System.Drawing.Size(222, 48); + this.ctxMenu.Opening += new System.ComponentModel.CancelEventHandler(this.ctxMenu_Opening); // - // lblLocation + // mnuCopyHdPack // - this.lblLocation.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.lblLocation.AutoSize = true; - this.lblLocation.Location = new System.Drawing.Point(3, 58); - this.lblLocation.Name = "lblLocation"; - this.lblLocation.Size = new System.Drawing.Size(51, 13); - this.lblLocation.TabIndex = 14; - this.lblLocation.Text = "Location:"; - // - // txtNametable - // - this.txtNametable.Location = new System.Drawing.Point(84, 29); - this.txtNametable.Name = "txtNametable"; - this.txtNametable.ReadOnly = true; - this.txtNametable.Size = new System.Drawing.Size(26, 20); - this.txtNametable.TabIndex = 15; - // - // txtLocation - // - this.txtLocation.Location = new System.Drawing.Point(84, 55); - this.txtLocation.Name = "txtLocation"; - this.txtLocation.ReadOnly = true; - this.txtLocation.Size = new System.Drawing.Size(42, 20); - this.txtLocation.TabIndex = 16; - // - // lblPpuAddress - // - this.lblPpuAddress.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.lblPpuAddress.AutoSize = true; - this.lblPpuAddress.Location = new System.Drawing.Point(3, 6); - this.lblPpuAddress.Name = "lblPpuAddress"; - this.lblPpuAddress.Size = new System.Drawing.Size(57, 13); - this.lblPpuAddress.TabIndex = 17; - this.lblPpuAddress.Text = "PPU Addr:"; - // - // txtPpuAddress - // - this.txtPpuAddress.Location = new System.Drawing.Point(84, 3); - this.txtPpuAddress.Name = "txtPpuAddress"; - this.txtPpuAddress.ReadOnly = true; - this.txtPpuAddress.Size = new System.Drawing.Size(42, 20); - this.txtPpuAddress.TabIndex = 18; + this.mnuCopyHdPack.Name = "mnuCopyHdPack"; + this.mnuCopyHdPack.Size = new System.Drawing.Size(221, 22); + this.mnuCopyHdPack.Text = "Copy Tile (HD Pack Format)"; + this.mnuCopyHdPack.Click += new System.EventHandler(this.mnuCopyHdPack_Click); // // ctrlNametableViewer // @@ -363,6 +383,7 @@ ((System.ComponentModel.ISupportInitialize)(this.picTile)).EndInit(); this.flowLayoutPanel1.ResumeLayout(false); this.flowLayoutPanel1.PerformLayout(); + this.ctxMenu.ResumeLayout(false); this.ResumeLayout(false); } @@ -395,5 +416,7 @@ private System.Windows.Forms.Label lblLocation; private System.Windows.Forms.TextBox txtPpuAddress; private System.Windows.Forms.Label lblPpuAddress; + private System.Windows.Forms.ContextMenuStrip ctxMenu; + private System.Windows.Forms.ToolStripMenuItem mnuCopyHdPack; } } diff --git a/GUI.NET/Debugger/Controls/ctrlNametableViewer.cs b/GUI.NET/Debugger/Controls/ctrlNametableViewer.cs index f0dfe753..e548e134 100644 --- a/GUI.NET/Debugger/Controls/ctrlNametableViewer.cs +++ b/GUI.NET/Debugger/Controls/ctrlNametableViewer.cs @@ -21,6 +21,9 @@ namespace Mesen.GUI.Debugger.Controls private Bitmap _gridOverlay; private Bitmap _nametableImage = new Bitmap(512, 480); private int _currentPpuAddress = -1; + private int _tileX = 0; + private int _tileY = 0; + private int _nametableIndex = 0; public ctrlNametableViewer() { @@ -139,28 +142,28 @@ namespace Mesen.GUI.Debugger.Controls private void picNametable_MouseMove(object sender, MouseEventArgs e) { - int nametableIndex = 0; + _nametableIndex = 0; if(e.X >= 256) { - nametableIndex++; + _nametableIndex++; } if(e.Y >= 240) { - nametableIndex+=2; + _nametableIndex+=2; } - int baseAddress = 0x2000 + nametableIndex * 0x400; + int baseAddress = 0x2000 + _nametableIndex * 0x400; - int tileX = Math.Min(e.X / 8, 63); - int tileY = Math.Min(e.Y / 8, 59); - int shift = (tileX & 0x02) | ((tileY & 0x02) << 1); + _tileX = Math.Min(e.X / 8, 63); + _tileY = Math.Min(e.Y / 8, 59); + int shift = (_tileX & 0x02) | ((_tileY & 0x02) << 1); - if(nametableIndex % 2 == 1) { - tileX -= 32; + if(_nametableIndex % 2 == 1) { + _tileX -= 32; } - if(nametableIndex >= 2) { - tileY -= 30; + if(_nametableIndex >= 2) { + _tileY -= 30; } - int ppuAddress = (baseAddress + tileX + tileY * 32); + int ppuAddress = (baseAddress + _tileX + _tileY * 32); if(_currentPpuAddress == ppuAddress) { return; } @@ -170,14 +173,14 @@ namespace Mesen.GUI.Debugger.Controls InteropEmu.DebugGetState(ref state); int bgAddr = state.PPU.ControlFlags.BackgroundPatternAddr; - int tileIndex = _tileData[nametableIndex][tileY*32+tileX]; - int attributeData = _attributeData[nametableIndex][tileY*32+tileX]; - int attributeAddr = baseAddress + 960 + ((tileY & 0xFC) << 1) + (tileX >> 2); + int tileIndex = _tileData[_nametableIndex][_tileY*32+_tileX]; + int attributeData = _attributeData[_nametableIndex][_tileY*32+_tileX]; + int attributeAddr = baseAddress + 960 + ((_tileY & 0xFC) << 1) + (_tileX >> 2); int paletteBaseAddr = ((attributeData >> shift) & 0x03) << 2; this.txtPpuAddress.Text = _currentPpuAddress.ToString("X4"); - this.txtNametable.Text = nametableIndex.ToString(); - this.txtLocation.Text = tileX.ToString() + ", " + tileY.ToString(); + this.txtNametable.Text = _nametableIndex.ToString(); + this.txtLocation.Text = _tileX.ToString() + ", " + _tileY.ToString(); this.txtTileIndex.Text = tileIndex.ToString("X2"); this.txtTileAddress.Text = (bgAddr + tileIndex * 16).ToString("X4"); this.txtAttributeData.Text = attributeData.ToString("X2"); @@ -221,5 +224,36 @@ namespace Mesen.GUI.Debugger.Controls this._gridOverlay = null; this.RefreshViewer(); } + + string _copyData; + private void mnuCopyHdPack_Click(object sender, EventArgs e) + { + Clipboard.SetText(_copyData); + } + + private void ctxMenu_Opening(object sender, CancelEventArgs e) + { + int baseAddress = 0x2000 + _nametableIndex * 0x400; + int tileIndex = _tileData[_nametableIndex][_tileY*32+_tileX]; + int attributeData = _attributeData[_nametableIndex][_tileY*32+_tileX]; + int shift = (_tileX & 0x02) | ((_tileY & 0x02) << 1); + int paletteBaseAddr = ((attributeData >> shift) & 0x03) << 2; + DebugState state = new DebugState(); + InteropEmu.DebugGetState(ref state); + int bgAddr = state.PPU.ControlFlags.BackgroundPatternAddr; + int tileAddr = bgAddr + tileIndex * 16; + + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < 16; i++) { + sb.Append(InteropEmu.DebugGetMemoryValue(DebugMemoryType.PpuMemory, (uint)(tileAddr + i)).ToString("X2")); + } + sb.Append(","); + for(int i = 1; i < 4; i++) { + sb.Append(InteropEmu.DebugGetMemoryValue(DebugMemoryType.PaletteMemory, (uint)(paletteBaseAddr + i)).ToString("X2")); + } + + _copyData = sb.ToString(); + _copyData = _copyData.Substring(0, _copyData.Length - 1); + } } } diff --git a/GUI.NET/Debugger/Controls/ctrlNametableViewer.resx b/GUI.NET/Debugger/Controls/ctrlNametableViewer.resx index 1af7de15..ffc99298 100644 --- a/GUI.NET/Debugger/Controls/ctrlNametableViewer.resx +++ b/GUI.NET/Debugger/Controls/ctrlNametableViewer.resx @@ -117,4 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + \ No newline at end of file diff --git a/GUI.NET/Debugger/Controls/ctrlSpriteViewer.Designer.cs b/GUI.NET/Debugger/Controls/ctrlSpriteViewer.Designer.cs index 80af6d5c..9c462753 100644 --- a/GUI.NET/Debugger/Controls/ctrlSpriteViewer.Designer.cs +++ b/GUI.NET/Debugger/Controls/ctrlSpriteViewer.Designer.cs @@ -27,6 +27,7 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); this.grpSpriteInfo = new System.Windows.Forms.GroupBox(); this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel(); @@ -36,9 +37,6 @@ this.lblScreenPreview = new System.Windows.Forms.Label(); this.lblTile = new System.Windows.Forms.Label(); this.picTile = new System.Windows.Forms.PictureBox(); - this.chkVerticalMirroring = new System.Windows.Forms.CheckBox(); - this.chkHorizontalMirroring = new System.Windows.Forms.CheckBox(); - this.chkBackgroundPriority = new System.Windows.Forms.CheckBox(); this.lblPosition = new System.Windows.Forms.Label(); this.txtPosition = new System.Windows.Forms.TextBox(); this.lblTileIndex = new System.Windows.Forms.Label(); @@ -47,15 +45,21 @@ this.lblTileAddress = new System.Windows.Forms.Label(); this.txtPaletteAddress = new System.Windows.Forms.TextBox(); this.txtTileAddress = new System.Windows.Forms.TextBox(); - this.picSprites = new System.Windows.Forms.PictureBox(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.chkHorizontalMirroring = new System.Windows.Forms.CheckBox(); + this.chkVerticalMirroring = new System.Windows.Forms.CheckBox(); + this.chkBackgroundPriority = new System.Windows.Forms.CheckBox(); + this.picSprites = new System.Windows.Forms.PictureBox(); + this.ctxMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.mnuCopyHdPack = new System.Windows.Forms.ToolStripMenuItem(); this.tableLayoutPanel3.SuspendLayout(); this.grpSpriteInfo.SuspendLayout(); this.tableLayoutPanel4.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.picPreview)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.picTile)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.picSprites)).BeginInit(); this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picSprites)).BeginInit(); + this.ctxMenu.SuspendLayout(); this.SuspendLayout(); // // tableLayoutPanel3 @@ -178,39 +182,6 @@ this.picTile.TabIndex = 12; this.picTile.TabStop = false; // - // chkVerticalMirroring - // - this.chkVerticalMirroring.AutoCheck = false; - this.chkVerticalMirroring.AutoSize = true; - this.chkVerticalMirroring.Location = new System.Drawing.Point(6, 29); - this.chkVerticalMirroring.Name = "chkVerticalMirroring"; - this.chkVerticalMirroring.Size = new System.Drawing.Size(77, 17); - this.chkVerticalMirroring.TabIndex = 14; - this.chkVerticalMirroring.Text = "Vertical flip"; - this.chkVerticalMirroring.UseVisualStyleBackColor = true; - // - // chkHorizontalMirroring - // - this.chkHorizontalMirroring.AutoCheck = false; - this.chkHorizontalMirroring.AutoSize = true; - this.chkHorizontalMirroring.Location = new System.Drawing.Point(6, 6); - this.chkHorizontalMirroring.Name = "chkHorizontalMirroring"; - this.chkHorizontalMirroring.Size = new System.Drawing.Size(89, 17); - this.chkHorizontalMirroring.TabIndex = 13; - this.chkHorizontalMirroring.Text = "Horizontal flip"; - this.chkHorizontalMirroring.UseVisualStyleBackColor = true; - // - // chkBackgroundPriority - // - this.chkBackgroundPriority.AutoCheck = false; - this.chkBackgroundPriority.AutoSize = true; - this.chkBackgroundPriority.Location = new System.Drawing.Point(6, 52); - this.chkBackgroundPriority.Name = "chkBackgroundPriority"; - this.chkBackgroundPriority.Size = new System.Drawing.Size(118, 17); - this.chkBackgroundPriority.TabIndex = 19; - this.chkBackgroundPriority.Text = "Background Priority"; - this.chkBackgroundPriority.UseVisualStyleBackColor = true; - // // lblPosition // this.lblPosition.Anchor = System.Windows.Forms.AnchorStyles.Left; @@ -283,18 +254,6 @@ this.txtTileAddress.Size = new System.Drawing.Size(42, 20); this.txtTileAddress.TabIndex = 8; // - // picSprites - // - this.picSprites.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.picSprites.Location = new System.Drawing.Point(1, 1); - this.picSprites.Margin = new System.Windows.Forms.Padding(1); - this.picSprites.Name = "picSprites"; - this.picSprites.Size = new System.Drawing.Size(258, 514); - this.picSprites.TabIndex = 0; - this.picSprites.TabStop = false; - this.picSprites.MouseLeave += new System.EventHandler(this.picSprites_MouseLeave); - this.picSprites.MouseMove += new System.Windows.Forms.MouseEventHandler(this.picSprites_MouseMove); - // // tableLayoutPanel1 // this.tableLayoutPanel1.ColumnCount = 1; @@ -304,7 +263,7 @@ this.tableLayoutPanel1.Controls.Add(this.chkVerticalMirroring, 0, 1); this.tableLayoutPanel1.Controls.Add(this.chkBackgroundPriority, 0, 2); this.tableLayoutPanel1.Location = new System.Drawing.Point(206, 52); - this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0); + this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(3); this.tableLayoutPanel1.RowCount = 3; @@ -315,6 +274,67 @@ this.tableLayoutPanel1.Size = new System.Drawing.Size(200, 82); this.tableLayoutPanel1.TabIndex = 24; // + // chkHorizontalMirroring + // + this.chkHorizontalMirroring.AutoCheck = false; + this.chkHorizontalMirroring.AutoSize = true; + this.chkHorizontalMirroring.Location = new System.Drawing.Point(6, 6); + this.chkHorizontalMirroring.Name = "chkHorizontalMirroring"; + this.chkHorizontalMirroring.Size = new System.Drawing.Size(89, 17); + this.chkHorizontalMirroring.TabIndex = 13; + this.chkHorizontalMirroring.Text = "Horizontal flip"; + this.chkHorizontalMirroring.UseVisualStyleBackColor = true; + // + // chkVerticalMirroring + // + this.chkVerticalMirroring.AutoCheck = false; + this.chkVerticalMirroring.AutoSize = true; + this.chkVerticalMirroring.Location = new System.Drawing.Point(6, 29); + this.chkVerticalMirroring.Name = "chkVerticalMirroring"; + this.chkVerticalMirroring.Size = new System.Drawing.Size(77, 17); + this.chkVerticalMirroring.TabIndex = 14; + this.chkVerticalMirroring.Text = "Vertical flip"; + this.chkVerticalMirroring.UseVisualStyleBackColor = true; + // + // chkBackgroundPriority + // + this.chkBackgroundPriority.AutoCheck = false; + this.chkBackgroundPriority.AutoSize = true; + this.chkBackgroundPriority.Location = new System.Drawing.Point(6, 52); + this.chkBackgroundPriority.Name = "chkBackgroundPriority"; + this.chkBackgroundPriority.Size = new System.Drawing.Size(118, 17); + this.chkBackgroundPriority.TabIndex = 19; + this.chkBackgroundPriority.Text = "Background Priority"; + this.chkBackgroundPriority.UseVisualStyleBackColor = true; + // + // picSprites + // + this.picSprites.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.picSprites.ContextMenuStrip = this.ctxMenu; + this.picSprites.Location = new System.Drawing.Point(1, 1); + this.picSprites.Margin = new System.Windows.Forms.Padding(1); + this.picSprites.Name = "picSprites"; + this.picSprites.Size = new System.Drawing.Size(258, 514); + this.picSprites.TabIndex = 0; + this.picSprites.TabStop = false; + this.picSprites.MouseLeave += new System.EventHandler(this.picSprites_MouseLeave); + this.picSprites.MouseMove += new System.Windows.Forms.MouseEventHandler(this.picSprites_MouseMove); + // + // ctxMenu + // + this.ctxMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuCopyHdPack}); + this.ctxMenu.Name = "ctxMenu"; + this.ctxMenu.Size = new System.Drawing.Size(222, 26); + this.ctxMenu.Opening += new System.ComponentModel.CancelEventHandler(this.ctxMenu_Opening); + // + // mnuCopyHdPack + // + this.mnuCopyHdPack.Name = "mnuCopyHdPack"; + this.mnuCopyHdPack.Size = new System.Drawing.Size(221, 22); + this.mnuCopyHdPack.Text = "Copy Tile (HD Pack Format)"; + this.mnuCopyHdPack.Click += new System.EventHandler(this.mnuCopyHdPack_Click); + // // ctrlSpriteViewer // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -328,9 +348,10 @@ this.tableLayoutPanel4.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.picPreview)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.picTile)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.picSprites)).EndInit(); this.tableLayoutPanel1.ResumeLayout(false); this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picSprites)).EndInit(); + this.ctxMenu.ResumeLayout(false); this.ResumeLayout(false); } @@ -359,5 +380,7 @@ private System.Windows.Forms.Label lblSpriteIndex; private System.Windows.Forms.TextBox txtSpriteIndex; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.ContextMenuStrip ctxMenu; + private System.Windows.Forms.ToolStripMenuItem mnuCopyHdPack; } } diff --git a/GUI.NET/Debugger/Controls/ctrlSpriteViewer.cs b/GUI.NET/Debugger/Controls/ctrlSpriteViewer.cs index d9c58f9e..07748d17 100644 --- a/GUI.NET/Debugger/Controls/ctrlSpriteViewer.cs +++ b/GUI.NET/Debugger/Controls/ctrlSpriteViewer.cs @@ -173,5 +173,39 @@ namespace Mesen.GUI.Debugger.Controls this._selectedSprite = -1; this.CreateScreenPreview(); } + + string _copyData; + private void mnuCopyHdPack_Click(object sender, EventArgs e) + { + Clipboard.SetText(_copyData); + } + + private void ctxMenu_Opening(object sender, CancelEventArgs e) + { + int ramAddr = _selectedSprite * 4; + int spriteY = _spriteRam[ramAddr]; + int tileIndex = _spriteRam[ramAddr + 1]; + int palette = (_spriteRam[ramAddr + 2] & 0x03) + 4; + int spriteX = _spriteRam[ramAddr + 3]; + + int tileAddr; + if(_largeSprites) { + tileAddr = ((tileIndex & 0x01) == 0x01 ? 0x1000 : 0x0000) + ((tileIndex & 0xFE) << 4); + } else { + tileAddr = _spritePatternAddr + (tileIndex << 4); + } + + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < 16; i++) { + sb.Append(InteropEmu.DebugGetMemoryValue(DebugMemoryType.PpuMemory, (UInt32)(tileAddr + i)).ToString("X2")); + } + sb.Append(","); + for(int i = 1; i < 4; i++) { + sb.Append(InteropEmu.DebugGetMemoryValue(DebugMemoryType.PaletteMemory, (uint)(palette * 4 + i)).ToString("X2")); + } + + _copyData = sb.ToString(); + _copyData = _copyData.Substring(0, _copyData.Length - 1); + } } } diff --git a/GUI.NET/Debugger/Controls/ctrlSpriteViewer.resx b/GUI.NET/Debugger/Controls/ctrlSpriteViewer.resx index 1af7de15..ffc99298 100644 --- a/GUI.NET/Debugger/Controls/ctrlSpriteViewer.resx +++ b/GUI.NET/Debugger/Controls/ctrlSpriteViewer.resx @@ -117,4 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + \ No newline at end of file diff --git a/GUI.NET/Forms/BaseConfigForm.cs b/GUI.NET/Forms/BaseConfigForm.cs index 070da2c8..bbc11350 100644 --- a/GUI.NET/Forms/BaseConfigForm.cs +++ b/GUI.NET/Forms/BaseConfigForm.cs @@ -61,11 +61,14 @@ namespace Mesen.GUI.Forms if(DialogResult == System.Windows.Forms.DialogResult.OK) { if(!ValidateInput()) { e.Cancel = true; - } else { - _validateTimer.Tick -= OnValidateInput; - _validateTimer.Stop(); } } + + if(!e.Cancel) { + _validateTimer.Tick -= OnValidateInput; + _validateTimer.Stop(); + } + base.OnFormClosing(e); } diff --git a/GUI.NET/Forms/BaseForm.cs b/GUI.NET/Forms/BaseForm.cs index dfe9bba8..1761f156 100644 --- a/GUI.NET/Forms/BaseForm.cs +++ b/GUI.NET/Forms/BaseForm.cs @@ -31,11 +31,8 @@ namespace Mesen.GUI.Forms this.Icon = menuItem.Image; } - if(owner != null) { - CenterOnParent(owner); - } - - base.Show(owner); + CenterOnParent(owner); + base.Show(); } private void CenterOnParent(IWin32Window owner) diff --git a/GUI.NET/Forms/HdPackEditor/frmHdPackEditor.Designer.cs b/GUI.NET/Forms/HdPackEditor/frmHdPackEditor.Designer.cs new file mode 100644 index 00000000..83ac6cdd --- /dev/null +++ b/GUI.NET/Forms/HdPackEditor/frmHdPackEditor.Designer.cs @@ -0,0 +1,398 @@ +namespace Mesen.GUI.Forms.HdPackEditor +{ + partial class frmHdPackEditor + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.grpPreview = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.picBankPreview = new System.Windows.Forms.PictureBox(); + this.cboBank = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.cboChrBankSize = new System.Windows.Forms.ComboBox(); + this.lblBankSize = new System.Windows.Forms.Label(); + this.lblScale = new System.Windows.Forms.Label(); + this.cboScale = new System.Windows.Forms.ComboBox(); + this.chkSortByFrequency = new System.Windows.Forms.CheckBox(); + this.chkLargeSprites = new System.Windows.Forms.CheckBox(); + this.chkGroupBlankTiles = new System.Windows.Forms.CheckBox(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.lblFolder = new System.Windows.Forms.Label(); + this.txtSaveFolder = new System.Windows.Forms.TextBox(); + this.btnSelectFolder = new System.Windows.Forms.Button(); + this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); + this.btnStartRecording = new System.Windows.Forms.Button(); + this.btnStopRecording = new System.Windows.Forms.Button(); + this.tmrRefresh = new System.Windows.Forms.Timer(this.components); + this.tableLayoutPanel1.SuspendLayout(); + this.grpPreview.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picBankPreview)).BeginInit(); + this.groupBox1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.flowLayoutPanel1.SuspendLayout(); + this.flowLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.grpPreview, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.groupBox1, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel2, 1, 2); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 3; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(612, 376); + this.tableLayoutPanel1.TabIndex = 0; + // + // grpPreview + // + this.grpPreview.Controls.Add(this.tableLayoutPanel3); + this.grpPreview.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpPreview.Location = new System.Drawing.Point(340, 31); + this.grpPreview.Name = "grpPreview"; + this.grpPreview.Size = new System.Drawing.Size(269, 310); + this.grpPreview.TabIndex = 0; + this.grpPreview.TabStop = false; + this.grpPreview.Text = "CHR Bank Preview"; + // + // tableLayoutPanel3 + // + this.tableLayoutPanel3.ColumnCount = 2; + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel3.Controls.Add(this.picBankPreview, 0, 1); + this.tableLayoutPanel3.Controls.Add(this.cboBank, 1, 0); + this.tableLayoutPanel3.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel3.Location = new System.Drawing.Point(3, 16); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + this.tableLayoutPanel3.RowCount = 2; + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel3.Size = new System.Drawing.Size(263, 291); + this.tableLayoutPanel3.TabIndex = 2; + // + // picBankPreview + // + this.tableLayoutPanel3.SetColumnSpan(this.picBankPreview, 2); + this.picBankPreview.Location = new System.Drawing.Point(3, 30); + this.picBankPreview.Name = "picBankPreview"; + this.picBankPreview.Size = new System.Drawing.Size(257, 258); + this.picBankPreview.TabIndex = 0; + this.picBankPreview.TabStop = false; + // + // cboBank + // + this.cboBank.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboBank.FormattingEnabled = true; + this.cboBank.Location = new System.Drawing.Point(70, 3); + this.cboBank.Name = "cboBank"; + this.cboBank.Size = new System.Drawing.Size(121, 21); + this.cboBank.TabIndex = 1; + // + // label1 + // + this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(3, 7); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(61, 13); + this.label1.TabIndex = 3; + this.label1.Text = "CHR Bank:"; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.tableLayoutPanel2); + this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.groupBox1.Location = new System.Drawing.Point(3, 31); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(331, 310); + this.groupBox1.TabIndex = 4; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Options"; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 2; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Controls.Add(this.cboChrBankSize, 1, 1); + this.tableLayoutPanel2.Controls.Add(this.lblBankSize, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.lblScale, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.cboScale, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.chkSortByFrequency, 0, 2); + this.tableLayoutPanel2.Controls.Add(this.chkLargeSprites, 0, 4); + this.tableLayoutPanel2.Controls.Add(this.chkGroupBlankTiles, 0, 3); + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 16); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 5; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(325, 291); + this.tableLayoutPanel2.TabIndex = 0; + // + // cboChrBankSize + // + this.cboChrBankSize.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboChrBankSize.FormattingEnabled = true; + this.cboChrBankSize.Items.AddRange(new object[] { + "1 KB", + "2 KB", + "4 KB"}); + this.cboChrBankSize.Location = new System.Drawing.Point(93, 30); + this.cboChrBankSize.Name = "cboChrBankSize"; + this.cboChrBankSize.Size = new System.Drawing.Size(121, 21); + this.cboChrBankSize.TabIndex = 10; + // + // lblBankSize + // + this.lblBankSize.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblBankSize.AutoSize = true; + this.lblBankSize.Location = new System.Drawing.Point(3, 34); + this.lblBankSize.Name = "lblBankSize"; + this.lblBankSize.Size = new System.Drawing.Size(84, 13); + this.lblBankSize.TabIndex = 9; + this.lblBankSize.Text = "CHR Bank Size:"; + // + // lblScale + // + this.lblScale.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblScale.AutoSize = true; + this.lblScale.Location = new System.Drawing.Point(3, 7); + this.lblScale.Name = "lblScale"; + this.lblScale.Size = new System.Drawing.Size(64, 13); + this.lblScale.TabIndex = 4; + this.lblScale.Text = "Scale/Filter:"; + // + // cboScale + // + this.cboScale.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboScale.FormattingEnabled = true; + this.cboScale.Location = new System.Drawing.Point(93, 3); + this.cboScale.Name = "cboScale"; + this.cboScale.Size = new System.Drawing.Size(121, 21); + this.cboScale.TabIndex = 5; + // + // chkSortByFrequency + // + this.chkSortByFrequency.AutoSize = true; + this.chkSortByFrequency.Checked = true; + this.chkSortByFrequency.CheckState = System.Windows.Forms.CheckState.Checked; + this.tableLayoutPanel2.SetColumnSpan(this.chkSortByFrequency, 2); + this.chkSortByFrequency.Location = new System.Drawing.Point(3, 57); + this.chkSortByFrequency.Name = "chkSortByFrequency"; + this.chkSortByFrequency.Size = new System.Drawing.Size(173, 17); + this.chkSortByFrequency.TabIndex = 3; + this.chkSortByFrequency.Text = "Sort pages by usage frequency"; + this.chkSortByFrequency.UseVisualStyleBackColor = true; + // + // chkLargeSprites + // + this.chkLargeSprites.AutoSize = true; + this.tableLayoutPanel2.SetColumnSpan(this.chkLargeSprites, 2); + this.chkLargeSprites.Location = new System.Drawing.Point(3, 103); + this.chkLargeSprites.Name = "chkLargeSprites"; + this.chkLargeSprites.Size = new System.Drawing.Size(163, 17); + this.chkLargeSprites.TabIndex = 8; + this.chkLargeSprites.Text = "Use 8x16 sprite display mode"; + this.chkLargeSprites.UseVisualStyleBackColor = true; + // + // chkGroupBlankTiles + // + this.chkGroupBlankTiles.AutoSize = true; + this.chkGroupBlankTiles.Checked = true; + this.chkGroupBlankTiles.CheckState = System.Windows.Forms.CheckState.Checked; + this.tableLayoutPanel2.SetColumnSpan(this.chkGroupBlankTiles, 2); + this.chkGroupBlankTiles.Location = new System.Drawing.Point(3, 80); + this.chkGroupBlankTiles.Name = "chkGroupBlankTiles"; + this.chkGroupBlankTiles.Size = new System.Drawing.Size(105, 17); + this.chkGroupBlankTiles.TabIndex = 11; + this.chkGroupBlankTiles.Text = "Group blank tiles"; + this.chkGroupBlankTiles.UseVisualStyleBackColor = true; + // + // flowLayoutPanel1 + // + this.tableLayoutPanel1.SetColumnSpan(this.flowLayoutPanel1, 2); + this.flowLayoutPanel1.Controls.Add(this.lblFolder); + this.flowLayoutPanel1.Controls.Add(this.txtSaveFolder); + this.flowLayoutPanel1.Controls.Add(this.btnSelectFolder); + this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(612, 28); + this.flowLayoutPanel1.TabIndex = 5; + // + // lblFolder + // + this.lblFolder.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblFolder.AutoSize = true; + this.lblFolder.Location = new System.Drawing.Point(3, 8); + this.lblFolder.Name = "lblFolder"; + this.lblFolder.Size = new System.Drawing.Size(67, 13); + this.lblFolder.TabIndex = 0; + this.lblFolder.Text = "Save Folder:"; + // + // txtSaveFolder + // + this.txtSaveFolder.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.txtSaveFolder.Location = new System.Drawing.Point(76, 4); + this.txtSaveFolder.Name = "txtSaveFolder"; + this.txtSaveFolder.ReadOnly = true; + this.txtSaveFolder.Size = new System.Drawing.Size(443, 20); + this.txtSaveFolder.TabIndex = 1; + this.txtSaveFolder.TabStop = false; + // + // btnSelectFolder + // + this.btnSelectFolder.AutoSize = true; + this.btnSelectFolder.Location = new System.Drawing.Point(525, 3); + this.btnSelectFolder.Name = "btnSelectFolder"; + this.btnSelectFolder.Size = new System.Drawing.Size(80, 23); + this.btnSelectFolder.TabIndex = 8; + this.btnSelectFolder.Text = "Select Folder"; + this.btnSelectFolder.UseVisualStyleBackColor = true; + this.btnSelectFolder.Click += new System.EventHandler(this.btnSelectFolder_Click); + // + // flowLayoutPanel2 + // + this.flowLayoutPanel2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.flowLayoutPanel2.Controls.Add(this.btnStartRecording); + this.flowLayoutPanel2.Controls.Add(this.btnStopRecording); + this.flowLayoutPanel2.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; + this.flowLayoutPanel2.Location = new System.Drawing.Point(348, 350); + this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0); + this.flowLayoutPanel2.Name = "flowLayoutPanel2"; + this.flowLayoutPanel2.Size = new System.Drawing.Size(264, 26); + this.flowLayoutPanel2.TabIndex = 7; + // + // btnStartRecording + // + this.btnStartRecording.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnStartRecording.AutoSize = true; + this.btnStartRecording.Image = global::Mesen.GUI.Properties.Resources.Record; + this.btnStartRecording.Location = new System.Drawing.Point(154, 3); + this.btnStartRecording.Name = "btnStartRecording"; + this.btnStartRecording.Size = new System.Drawing.Size(107, 23); + this.btnStartRecording.TabIndex = 6; + this.btnStartRecording.Text = "Start Recording"; + this.btnStartRecording.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; + this.btnStartRecording.UseVisualStyleBackColor = true; + this.btnStartRecording.Click += new System.EventHandler(this.btnStartRecording_Click); + // + // btnStopRecording + // + this.btnStopRecording.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnStopRecording.AutoSize = true; + this.btnStopRecording.Image = global::Mesen.GUI.Properties.Resources.Stop; + this.btnStopRecording.Location = new System.Drawing.Point(41, 3); + this.btnStopRecording.Name = "btnStopRecording"; + this.btnStopRecording.Size = new System.Drawing.Size(107, 23); + this.btnStopRecording.TabIndex = 7; + this.btnStopRecording.Text = "Stop Recording"; + this.btnStopRecording.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; + this.btnStopRecording.UseVisualStyleBackColor = true; + this.btnStopRecording.Visible = false; + this.btnStopRecording.Click += new System.EventHandler(this.btnStopRecording_Click); + // + // tmrRefresh + // + this.tmrRefresh.Interval = 200; + this.tmrRefresh.Tick += new System.EventHandler(this.tmrRefresh_Tick); + // + // frmHdPackEditor + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(612, 376); + this.Controls.Add(this.tableLayoutPanel1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.Name = "frmHdPackEditor"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "HD Pack Builder"; + this.tableLayoutPanel1.ResumeLayout(false); + this.grpPreview.ResumeLayout(false); + this.tableLayoutPanel3.ResumeLayout(false); + this.tableLayoutPanel3.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picBankPreview)).EndInit(); + this.groupBox1.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.flowLayoutPanel1.ResumeLayout(false); + this.flowLayoutPanel1.PerformLayout(); + this.flowLayoutPanel2.ResumeLayout(false); + this.flowLayoutPanel2.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.GroupBox grpPreview; + private System.Windows.Forms.PictureBox picBankPreview; + private System.Windows.Forms.Timer tmrRefresh; + private System.Windows.Forms.ComboBox cboBank; + private System.Windows.Forms.CheckBox chkSortByFrequency; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Label lblScale; + private System.Windows.Forms.ComboBox cboScale; + private System.Windows.Forms.Button btnStartRecording; + private System.Windows.Forms.CheckBox chkLargeSprites; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.Label lblFolder; + private System.Windows.Forms.TextBox txtSaveFolder; + private System.Windows.Forms.Button btnSelectFolder; + private System.Windows.Forms.ComboBox cboChrBankSize; + private System.Windows.Forms.Label lblBankSize; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; + private System.Windows.Forms.Button btnStopRecording; + private System.Windows.Forms.CheckBox chkGroupBlankTiles; + } +} \ No newline at end of file diff --git a/GUI.NET/Forms/HdPackEditor/frmHdPackEditor.cs b/GUI.NET/Forms/HdPackEditor/frmHdPackEditor.cs new file mode 100644 index 00000000..c0b90c35 --- /dev/null +++ b/GUI.NET/Forms/HdPackEditor/frmHdPackEditor.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Forms; +using Mesen.GUI.Config; +using Mesen.GUI.Controls; + +namespace Mesen.GUI.Forms.HdPackEditor +{ + public partial class frmHdPackEditor : BaseForm + { + private bool _isRecording = false; + + public frmHdPackEditor() + { + InitializeComponent(); + + if(!InteropEmu.GetRomInfo().IsChrRam) { + cboChrBankSize.Visible = false; + lblBankSize.Visible = false; + } + + txtSaveFolder.Text = Path.Combine(ConfigManager.HdPackFolder, InteropEmu.GetRomInfo().GetRomName()); + picBankPreview.Image = new Bitmap(256, 256); + + UpdateFilterDropdown(); + + cboChrBankSize.SelectedIndex = 2; + + UpdateUI(false); + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + base.OnFormClosing(e); + if(_isRecording) { + StopRecording(); + } + } + + private void UpdateFilterDropdown() + { + int scaleFilter = -1; + string hdDefFile = Path.Combine(txtSaveFolder.Text, "hires.txt"); + if(File.Exists(hdDefFile)) { + string fileContent = File.ReadAllText(hdDefFile); + Match match = Regex.Match(fileContent, "(\\d*)"); + if(match.Success) { + if(Int32.TryParse(match.Groups[1].ToString(), out int scale)) { + scaleFilter = scale; + } + } + } + + cboScale.Items.Clear(); + foreach(FilterInfo info in _filters) { + if(scaleFilter == -1 || info.Scale == scaleFilter) { + cboScale.Items.Add(info); + } + } + cboScale.SelectedIndex = 0; + } + + private void tmrRefresh_Tick(object sender, EventArgs e) + { + UInt32[] bankList = InteropEmu.HdBuilderGetChrBankList(); + object selectedItem = cboBank.SelectedItem; + if(bankList.Length != cboBank.Items.Count) { + cboBank.Items.Clear(); + foreach(UInt32 bankNumber in bankList) { + cboBank.Items.Add(bankNumber); + } + } + cboBank.SelectedItem = selectedItem; + if(cboBank.SelectedIndex < 0) { + cboBank.SelectedIndex = 0; + } + + int scale = (int)((FilterInfo)cboScale.SelectedItem).Scale; + + using(Graphics g = Graphics.FromImage(picBankPreview.Image)) { + Byte[] rgbBuffer = InteropEmu.HdBuilderGetBankPreview((uint)cboBank.SelectedItem, scale, 0); + GCHandle handle = GCHandle.Alloc(rgbBuffer, GCHandleType.Pinned); + Bitmap source = new Bitmap(128*scale, 128*scale, 4*128*scale, System.Drawing.Imaging.PixelFormat.Format32bppArgb, handle.AddrOfPinnedObject()); + try { + g.Clear(Color.Black); + g.DrawImage(source, 0, 0, 256, 256); + } finally { + handle.Free(); + } + } + picBankPreview.Refresh(); + } + + private void UpdateUI(bool isRecording) + { + btnStartRecording.Visible = !isRecording; + btnStopRecording.Visible = isRecording; + + cboBank.Enabled = isRecording; + + chkLargeSprites.Enabled = !isRecording; + chkSortByFrequency.Enabled = !isRecording; + chkGroupBlankTiles.Enabled = !isRecording; + + cboChrBankSize.Enabled = !isRecording; + cboScale.Enabled = !isRecording; + btnSelectFolder.Enabled = !isRecording; + + _isRecording = isRecording; + } + + private void btnStartRecording_Click(object sender, EventArgs e) + { + StartRecording(); + } + + private void btnStopRecording_Click(object sender, EventArgs e) + { + StopRecording(); + } + + private void StartRecording() + { + HdPackRecordFlags flags = HdPackRecordFlags.None; + if(chkLargeSprites.Checked) { + flags |= HdPackRecordFlags.UseLargeSprites; + } + if(chkSortByFrequency.Checked) { + flags |= HdPackRecordFlags.SortByUsageFrequency; + } + if(chkGroupBlankTiles.Checked) { + flags |= HdPackRecordFlags.GroupBlankTiles; + } + + InteropEmu.HdBuilderStartRecording(txtSaveFolder.Text, ((FilterInfo)cboScale.SelectedItem).FilterType, ((FilterInfo)cboScale.SelectedItem).Scale, flags, (UInt32)Math.Pow(2, cboChrBankSize.SelectedIndex) * 0x400); + tmrRefresh.Start(); + + UpdateUI(true); + } + + private void StopRecording() + { + tmrRefresh.Stop(); + InteropEmu.HdBuilderStopRecording(); + + UpdateFilterDropdown(); + + UpdateUI(false); + } + + private void btnSelectFolder_Click(object sender, EventArgs e) + { + using(FolderBrowserDialog fbd = new FolderBrowserDialog()) { + fbd.SelectedPath = ConfigManager.HdPackFolder; + if(fbd.ShowDialog() == DialogResult.OK) { + txtSaveFolder.Text = Path.Combine(fbd.SelectedPath, InteropEmu.GetRomInfo().GetRomName()); + UpdateFilterDropdown(); + } + } + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if(keyData == Keys.Escape) { + this.Close(); + } + return base.ProcessCmdKey(ref msg, keyData); + } + + private class FilterInfo + { + public string Name { get; set; } + public ScaleFilterType FilterType { get; set; } + public UInt32 Scale { get; set; } + + public override string ToString() + { + return Name; + } + } + + private FilterInfo[] _filters = { + new FilterInfo() { Name = "None (1x)", FilterType = ScaleFilterType.Prescale, Scale = 1 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.Prescale2x), FilterType = ScaleFilterType.Prescale, Scale = 2 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.Prescale3x), FilterType = ScaleFilterType.Prescale, Scale = 3 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.Prescale4x), FilterType = ScaleFilterType.Prescale, Scale = 4 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.Prescale6x), FilterType = ScaleFilterType.Prescale, Scale = 6 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.Prescale8x), FilterType = ScaleFilterType.Prescale, Scale = 8 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.Prescale10x), FilterType = ScaleFilterType.Prescale, Scale = 10 }, + + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.HQ2x), FilterType = ScaleFilterType.HQX, Scale = 2 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.HQ3x), FilterType = ScaleFilterType.HQX, Scale = 3 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.HQ4x), FilterType = ScaleFilterType.HQX, Scale = 4 }, + + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.Scale2x), FilterType = ScaleFilterType.Scale2x, Scale = 2 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.Scale3x), FilterType = ScaleFilterType.Scale2x, Scale = 3 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.Scale4x), FilterType = ScaleFilterType.Scale2x, Scale = 4 }, + + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.Super2xSai), FilterType = ScaleFilterType.Super2xSai, Scale = 2 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.SuperEagle), FilterType = ScaleFilterType.SuperEagle, Scale = 2 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType._2xSai), FilterType = ScaleFilterType._2xSai, Scale = 2 }, + + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.xBRZ2x), FilterType = ScaleFilterType.xBRZ, Scale = 2 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.xBRZ3x), FilterType = ScaleFilterType.xBRZ, Scale = 3 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.xBRZ4x), FilterType = ScaleFilterType.xBRZ, Scale = 4 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.xBRZ5x), FilterType = ScaleFilterType.xBRZ, Scale = 5 }, + new FilterInfo() { Name = ResourceHelper.GetEnumText(VideoFilterType.xBRZ6x), FilterType = ScaleFilterType.xBRZ, Scale = 6 }, + }; + } +} \ No newline at end of file diff --git a/GUI.NET/Forms/HdPackEditor/frmHdPackEditor.resx b/GUI.NET/Forms/HdPackEditor/frmHdPackEditor.resx new file mode 100644 index 00000000..b8422f0d --- /dev/null +++ b/GUI.NET/Forms/HdPackEditor/frmHdPackEditor.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 107, 17 + + \ No newline at end of file diff --git a/GUI.NET/Forms/frmMain.Designer.cs b/GUI.NET/Forms/frmMain.Designer.cs index 383589bc..8fdaf929 100644 --- a/GUI.NET/Forms/frmMain.Designer.cs +++ b/GUI.NET/Forms/frmMain.Designer.cs @@ -176,6 +176,7 @@ namespace Mesen.GUI.Forms this.mnuRunAutomaticTest = new System.Windows.Forms.ToolStripMenuItem(); this.mnuDebugger = new System.Windows.Forms.ToolStripMenuItem(); this.mnuLogWindow = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHdPackEditor = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); this.mnuRandomGame = new System.Windows.Forms.ToolStripMenuItem(); this.mnuTakeScreenshot = new System.Windows.Forms.ToolStripMenuItem(); @@ -533,7 +534,7 @@ namespace Mesen.GUI.Forms this.mnuShowFPS}); this.mnuEmulationSpeed.Image = global::Mesen.GUI.Properties.Resources.Speed; this.mnuEmulationSpeed.Name = "mnuEmulationSpeed"; - this.mnuEmulationSpeed.Size = new System.Drawing.Size(135, 22); + this.mnuEmulationSpeed.Size = new System.Drawing.Size(152, 22); this.mnuEmulationSpeed.Text = "Speed"; this.mnuEmulationSpeed.DropDownOpening += new System.EventHandler(this.mnuEmulationSpeed_DropDownOpening); // @@ -635,7 +636,7 @@ namespace Mesen.GUI.Forms this.mnuFullscreen}); this.mnuVideoScale.Image = global::Mesen.GUI.Properties.Resources.Fullscreen; this.mnuVideoScale.Name = "mnuVideoScale"; - this.mnuVideoScale.Size = new System.Drawing.Size(135, 22); + this.mnuVideoScale.Size = new System.Drawing.Size(152, 22); this.mnuVideoScale.Text = "Video Size"; // // mnuScale1x @@ -751,7 +752,7 @@ namespace Mesen.GUI.Forms this.mnuBilinearInterpolation}); this.mnuVideoFilter.Image = global::Mesen.GUI.Properties.Resources.VideoFilter; this.mnuVideoFilter.Name = "mnuVideoFilter"; - this.mnuVideoFilter.Size = new System.Drawing.Size(135, 22); + this.mnuVideoFilter.Size = new System.Drawing.Size(152, 22); this.mnuVideoFilter.Text = "Video Filter"; // // mnuNoneFilter @@ -981,7 +982,7 @@ namespace Mesen.GUI.Forms this.mnuRegionDendy}); this.mnuRegion.Image = global::Mesen.GUI.Properties.Resources.Globe; this.mnuRegion.Name = "mnuRegion"; - this.mnuRegion.Size = new System.Drawing.Size(135, 22); + this.mnuRegion.Size = new System.Drawing.Size(152, 22); this.mnuRegion.Text = "Region"; // // mnuRegionAuto @@ -1015,13 +1016,13 @@ namespace Mesen.GUI.Forms // toolStripMenuItem10 // this.toolStripMenuItem10.Name = "toolStripMenuItem10"; - this.toolStripMenuItem10.Size = new System.Drawing.Size(132, 6); + this.toolStripMenuItem10.Size = new System.Drawing.Size(149, 6); // // mnuAudioConfig // this.mnuAudioConfig.Image = global::Mesen.GUI.Properties.Resources.Audio; this.mnuAudioConfig.Name = "mnuAudioConfig"; - this.mnuAudioConfig.Size = new System.Drawing.Size(135, 22); + this.mnuAudioConfig.Size = new System.Drawing.Size(152, 22); this.mnuAudioConfig.Text = "Audio"; this.mnuAudioConfig.Click += new System.EventHandler(this.mnuAudioConfig_Click); // @@ -1029,7 +1030,7 @@ namespace Mesen.GUI.Forms // this.mnuInput.Image = global::Mesen.GUI.Properties.Resources.Controller; this.mnuInput.Name = "mnuInput"; - this.mnuInput.Size = new System.Drawing.Size(135, 22); + this.mnuInput.Size = new System.Drawing.Size(152, 22); this.mnuInput.Text = "Input"; this.mnuInput.Click += new System.EventHandler(this.mnuInput_Click); // @@ -1037,7 +1038,7 @@ namespace Mesen.GUI.Forms // this.mnuVideoConfig.Image = global::Mesen.GUI.Properties.Resources.Video; this.mnuVideoConfig.Name = "mnuVideoConfig"; - this.mnuVideoConfig.Size = new System.Drawing.Size(135, 22); + this.mnuVideoConfig.Size = new System.Drawing.Size(152, 22); this.mnuVideoConfig.Text = "Video"; this.mnuVideoConfig.Click += new System.EventHandler(this.mnuVideoConfig_Click); // @@ -1045,20 +1046,20 @@ namespace Mesen.GUI.Forms // this.mnuEmulationConfig.Image = global::Mesen.GUI.Properties.Resources.DipSwitches; this.mnuEmulationConfig.Name = "mnuEmulationConfig"; - this.mnuEmulationConfig.Size = new System.Drawing.Size(135, 22); + this.mnuEmulationConfig.Size = new System.Drawing.Size(152, 22); this.mnuEmulationConfig.Text = "Emulation"; this.mnuEmulationConfig.Click += new System.EventHandler(this.mnuEmulationConfig_Click); // // toolStripMenuItem11 // this.toolStripMenuItem11.Name = "toolStripMenuItem11"; - this.toolStripMenuItem11.Size = new System.Drawing.Size(132, 6); + this.toolStripMenuItem11.Size = new System.Drawing.Size(149, 6); // // mnuPreferences // this.mnuPreferences.Image = global::Mesen.GUI.Properties.Resources.Cog; this.mnuPreferences.Name = "mnuPreferences"; - this.mnuPreferences.Size = new System.Drawing.Size(135, 22); + this.mnuPreferences.Size = new System.Drawing.Size(152, 22); this.mnuPreferences.Text = "Preferences"; this.mnuPreferences.Click += new System.EventHandler(this.mnuPreferences_Click); // @@ -1075,6 +1076,7 @@ namespace Mesen.GUI.Forms this.mnuTests, this.mnuDebugger, this.mnuLogWindow, + this.mnuHdPackEditor, this.toolStripMenuItem1, this.mnuRandomGame, this.mnuTakeScreenshot}); @@ -1410,6 +1412,14 @@ namespace Mesen.GUI.Forms this.mnuLogWindow.Text = "Log Window"; this.mnuLogWindow.Click += new System.EventHandler(this.mnuLogWindow_Click); // + // mnuHdPackEditor + // + this.mnuHdPackEditor.Image = global::Mesen.GUI.Properties.Resources.HdPack; + this.mnuHdPackEditor.Name = "mnuHdPackEditor"; + this.mnuHdPackEditor.Size = new System.Drawing.Size(231, 22); + this.mnuHdPackEditor.Text = "HD Pack Builder"; + this.mnuHdPackEditor.Click += new System.EventHandler(this.mnuHdPackEditor_Click); + // // toolStripMenuItem1 // this.toolStripMenuItem1.Name = "toolStripMenuItem1"; @@ -1671,6 +1681,7 @@ namespace Mesen.GUI.Forms private Controls.ctrlRecentGames ctrlRecentGames; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem24; private System.Windows.Forms.ToolStripMenuItem mnuPowerOff; + private System.Windows.Forms.ToolStripMenuItem mnuHdPackEditor; } } diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index 8be7f3b5..abf185ab 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -16,6 +16,7 @@ using Mesen.GUI.Config; using Mesen.GUI.Debugger; using Mesen.GUI.Forms.Cheats; using Mesen.GUI.Forms.Config; +using Mesen.GUI.Forms.HdPackEditor; using Mesen.GUI.Forms.NetPlay; using Mesen.GUI.GoogleDriveIntegration; @@ -28,6 +29,7 @@ namespace Mesen.GUI.Forms private frmDebugger _debugger; private frmLogWindow _logWindow; private frmCheatList _cheatListWindow; + private frmHdPackEditor _hdPackEditorWindow; private string _currentRomPath = null; private int _currentRomArchiveIndex = -1; private string _currentGame = null; @@ -497,6 +499,9 @@ namespace Mesen.GUI.Forms this._currentGame = null; CheatInfo.ClearCheats(); this.BeginInvoke((MethodInvoker)(() => { + if(_hdPackEditorWindow != null) { + _hdPackEditorWindow.Close(); + } ctrlRecentGames.Initialize(); })); break; @@ -544,13 +549,14 @@ namespace Mesen.GUI.Forms private void mnuOpen_Click(object sender, EventArgs e) { - OpenFileDialog ofd = new OpenFileDialog(); - ofd.SetFilter(ResourceHelper.GetMessage("FilterRomIps")); - if(ConfigManager.Config.RecentFiles.Count > 0) { - ofd.InitialDirectory = Path.GetDirectoryName(ConfigManager.Config.RecentFiles[0].Path); - } - if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { - LoadFile(ofd.FileName); + 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); + } + if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + LoadFile(ofd.FileName); + } } } @@ -585,15 +591,16 @@ namespace Mesen.GUI.Forms { if(_emuThread == null) { if(MesenMsgBox.Show("SelectRomIps", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.OK) { - OpenFileDialog ofd = new OpenFileDialog(); - ofd.SetFilter(ResourceHelper.GetMessage("FilterRom")); - if(ConfigManager.Config.RecentFiles.Count > 0) { - ofd.InitialDirectory = Path.GetDirectoryName(ConfigManager.Config.RecentFiles[0].Path); - } + 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); + } - if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { - LoadROM(ofd.FileName, true, -1, patchFile); - } + if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + LoadROM(ofd.FileName, true, -1, patchFile); + } + } } } else if(MesenMsgBox.Show("PatchAndReset", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.OK) { LoadROM(_currentRomPath, true, _currentRomArchiveIndex, patchFile); @@ -763,6 +770,7 @@ namespace Mesen.GUI.Forms mnuTestRecordFrom.Enabled = (mnuTestRecordStart.Enabled || mnuTestRecordNow.Enabled || mnuTestRecordMovie.Enabled || mnuTestRecordTest.Enabled); mnuDebugger.Enabled = !netPlay && _emuThread != null; + mnuHdPackEditor.Enabled = !netPlay && _emuThread != null; mnuTakeScreenshot.Enabled = _emuThread != null && !InteropEmu.IsNsf(); mnuNetPlay.Enabled = !InteropEmu.IsNsf(); @@ -988,9 +996,10 @@ namespace Mesen.GUI.Forms if(InteropEmu.IsServerRunning()) { Task.Run(() => InteropEmu.StopServer()); } else { - frmServerConfig frm = new frmServerConfig(); - if(frm.ShowDialog(sender) == System.Windows.Forms.DialogResult.OK) { - InteropEmu.StartServer(ConfigManager.Config.ServerInfo.Port, ConfigManager.Config.Profile.PlayerName); + using(frmServerConfig frm = new frmServerConfig()) { + if(frm.ShowDialog(sender) == System.Windows.Forms.DialogResult.OK) { + InteropEmu.StartServer(ConfigManager.Config.ServerInfo.Port, ConfigManager.Config.Profile.PlayerName); + } } } } @@ -1000,18 +1009,21 @@ namespace Mesen.GUI.Forms if(InteropEmu.IsConnected()) { Task.Run(() => InteropEmu.Disconnect()); } else { - frmClientConfig frm = new frmClientConfig(); - if(frm.ShowDialog(sender) == System.Windows.Forms.DialogResult.OK) { - Task.Run(() => { - InteropEmu.Connect(ConfigManager.Config.ClientConnectionInfo.Host, ConfigManager.Config.ClientConnectionInfo.Port, ConfigManager.Config.Profile.PlayerName, ConfigManager.Config.ClientConnectionInfo.Spectator); - }); + using(frmClientConfig frm = new frmClientConfig()) { + if(frm.ShowDialog(sender) == System.Windows.Forms.DialogResult.OK) { + Task.Run(() => { + InteropEmu.Connect(ConfigManager.Config.ClientConnectionInfo.Host, ConfigManager.Config.ClientConnectionInfo.Port, ConfigManager.Config.Profile.PlayerName, ConfigManager.Config.ClientConnectionInfo.Spectator); + }); + } } } } private void mnuProfile_Click(object sender, EventArgs e) { - new frmPlayerProfile().ShowDialog(sender); + using(frmPlayerProfile frm = new frmPlayerProfile()) { + frm.ShowDialog(sender); + } } private void mnuExit_Click(object sender, EventArgs e) @@ -1021,7 +1033,9 @@ namespace Mesen.GUI.Forms private void mnuVideoConfig_Click(object sender, EventArgs e) { - new frmVideoConfig().ShowDialog(sender); + using(frmVideoConfig frm = new frmVideoConfig()) { + frm.ShowDialog(sender); + } UpdateVideoSettings(); } @@ -1057,22 +1071,24 @@ namespace Mesen.GUI.Forms private void RecordMovie(bool resetEmu) { - SaveFileDialog sfd = new SaveFileDialog(); - sfd.SetFilter(ResourceHelper.GetMessage("FilterMovie")); - sfd.InitialDirectory = ConfigManager.MovieFolder; - sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + ".mmo"; - if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { - InteropEmu.MovieRecord(sfd.FileName, resetEmu); + using(SaveFileDialog sfd = new SaveFileDialog()) { + sfd.SetFilter(ResourceHelper.GetMessage("FilterMovie")); + sfd.InitialDirectory = ConfigManager.MovieFolder; + sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + ".mmo"; + if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + InteropEmu.MovieRecord(sfd.FileName, resetEmu); + } } } private void mnuPlayMovie_Click(object sender, EventArgs e) { - OpenFileDialog ofd = new OpenFileDialog(); - ofd.SetFilter(ResourceHelper.GetMessage("FilterMovie")); - ofd.InitialDirectory = ConfigManager.MovieFolder; - if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { - InteropEmu.MoviePlay(ofd.FileName); + using(OpenFileDialog ofd = new OpenFileDialog()) { + ofd.SetFilter(ResourceHelper.GetMessage("FilterMovie")); + ofd.InitialDirectory = ConfigManager.MovieFolder; + if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + InteropEmu.MoviePlay(ofd.FileName); + } } } @@ -1093,12 +1109,13 @@ namespace Mesen.GUI.Forms private void mnuWaveRecord_Click(object sender, EventArgs e) { - SaveFileDialog sfd = new SaveFileDialog(); - sfd.SetFilter(ResourceHelper.GetMessage("FilterWave")); - sfd.InitialDirectory = ConfigManager.WaveFolder; - sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + ".wav"; - if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { - InteropEmu.WaveRecord(sfd.FileName); + using(SaveFileDialog sfd = new SaveFileDialog()) { + sfd.SetFilter(ResourceHelper.GetMessage("FilterWave")); + sfd.InitialDirectory = ConfigManager.WaveFolder; + sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + ".wav"; + if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + InteropEmu.WaveRecord(sfd.FileName); + } } } @@ -1123,51 +1140,52 @@ namespace Mesen.GUI.Forms private void mnuTestRun_Click(object sender, EventArgs e) { - OpenFileDialog ofd = new OpenFileDialog(); - ofd.SetFilter(ResourceHelper.GetMessage("FilterTest")); - ofd.InitialDirectory = ConfigManager.TestFolder; - ofd.Multiselect = true; - if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { - List passedTests = new List(); - List failedTests = new List(); - List failedFrameCount = new List(); + using(OpenFileDialog ofd = new OpenFileDialog()) { + ofd.SetFilter(ResourceHelper.GetMessage("FilterTest")); + ofd.InitialDirectory = ConfigManager.TestFolder; + ofd.Multiselect = true; + if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + List passedTests = new List(); + List failedTests = new List(); + List failedFrameCount = new List(); - this.menuStrip.Enabled = false; + this.menuStrip.Enabled = false; - Task.Run(() => { - foreach(string filename in ofd.FileNames) { - int result = InteropEmu.RunRecordedTest(filename); + Task.Run(() => { + foreach(string filename in ofd.FileNames) { + int result = InteropEmu.RunRecordedTest(filename); - if(result == 0) { - passedTests.Add(Path.GetFileNameWithoutExtension(filename)); - } else { - failedTests.Add(Path.GetFileNameWithoutExtension(filename)); - failedFrameCount.Add(result); + if(result == 0) { + passedTests.Add(Path.GetFileNameWithoutExtension(filename)); + } else { + failedTests.Add(Path.GetFileNameWithoutExtension(filename)); + failedFrameCount.Add(result); + } } - } - this.BeginInvoke((MethodInvoker)(() => { - if(failedTests.Count == 0) { - MessageBox.Show("All tests passed.", "", MessageBoxButtons.OK, MessageBoxIcon.Information); - } else { - StringBuilder message = new StringBuilder(); - if(passedTests.Count > 0) { - message.AppendLine("Passed tests:"); - foreach(string test in passedTests) { - message.AppendLine(" -" + test); + this.BeginInvoke((MethodInvoker)(() => { + if(failedTests.Count == 0) { + MessageBox.Show("All tests passed.", "", MessageBoxButtons.OK, MessageBoxIcon.Information); + } else { + StringBuilder message = new StringBuilder(); + if(passedTests.Count > 0) { + message.AppendLine("Passed tests:"); + foreach(string test in passedTests) { + message.AppendLine(" -" + test); + } + message.AppendLine(""); } - message.AppendLine(""); + message.AppendLine("Failed tests:"); + for(int i = 0, len = failedTests.Count; i < len; i++) { + message.AppendLine(" -" + failedTests[i] + " (" + failedFrameCount[i] + ")"); + } + MessageBox.Show(message.ToString(), "", MessageBoxButtons.OK, MessageBoxIcon.Error); } - message.AppendLine("Failed tests:"); - for(int i = 0, len = failedTests.Count; i < len; i++) { - message.AppendLine(" -" + failedTests[i] + " (" + failedFrameCount[i] + ")"); - } - MessageBox.Show(message.ToString(), "", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - this.menuStrip.Enabled = true; - })); - }); + this.menuStrip.Enabled = true; + })); + }); + } } } @@ -1183,44 +1201,47 @@ namespace Mesen.GUI.Forms private void RecordTest(bool resetEmu) { - SaveFileDialog sfd = new SaveFileDialog(); - sfd.SetFilter(ResourceHelper.GetMessage("FilterTest")); - sfd.InitialDirectory = ConfigManager.TestFolder; - sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + ".mtp"; - if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { - InteropEmu.RomTestRecord(sfd.FileName, resetEmu); + using(SaveFileDialog sfd = new SaveFileDialog()) { + sfd.SetFilter(ResourceHelper.GetMessage("FilterTest")); + sfd.InitialDirectory = ConfigManager.TestFolder; + sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + ".mtp"; + if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + InteropEmu.RomTestRecord(sfd.FileName, resetEmu); + } } } private void mnuTestRecordMovie_Click(object sender, EventArgs e) { - OpenFileDialog ofd = new OpenFileDialog(); - ofd.SetFilter(ResourceHelper.GetMessage("FilterMovie")); - ofd.InitialDirectory = ConfigManager.MovieFolder; - if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { - SaveFileDialog sfd = new SaveFileDialog(); - sfd.SetFilter(ResourceHelper.GetMessage("FilterTest")); - sfd.InitialDirectory = ConfigManager.TestFolder; - sfd.FileName = Path.GetFileNameWithoutExtension(ofd.FileName) + ".mtp"; - if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { - InteropEmu.RomTestRecordFromMovie(sfd.FileName, ofd.FileName); + using(OpenFileDialog ofd = new OpenFileDialog()) { + ofd.SetFilter(ResourceHelper.GetMessage("FilterMovie")); + ofd.InitialDirectory = ConfigManager.MovieFolder; + if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + SaveFileDialog sfd = new SaveFileDialog(); + sfd.SetFilter(ResourceHelper.GetMessage("FilterTest")); + sfd.InitialDirectory = ConfigManager.TestFolder; + sfd.FileName = Path.GetFileNameWithoutExtension(ofd.FileName) + ".mtp"; + if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + InteropEmu.RomTestRecordFromMovie(sfd.FileName, ofd.FileName); + } } } } private void mnuTestRecordTest_Click(object sender, EventArgs e) { - OpenFileDialog ofd = new OpenFileDialog(); - ofd.SetFilter(ResourceHelper.GetMessage("FilterTest")); - ofd.InitialDirectory = ConfigManager.TestFolder; + using(OpenFileDialog ofd = new OpenFileDialog()) { + ofd.SetFilter(ResourceHelper.GetMessage("FilterTest")); + ofd.InitialDirectory = ConfigManager.TestFolder; - if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { - SaveFileDialog sfd = new SaveFileDialog(); - sfd.SetFilter(ResourceHelper.GetMessage("FilterTest")); - sfd.InitialDirectory = ConfigManager.TestFolder; - sfd.FileName = Path.GetFileNameWithoutExtension(ofd.FileName) + ".mtp"; - if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { - InteropEmu.RomTestRecordFromTest(sfd.FileName, ofd.FileName); + if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + SaveFileDialog sfd = new SaveFileDialog(); + sfd.SetFilter(ResourceHelper.GetMessage("FilterTest")); + sfd.InitialDirectory = ConfigManager.TestFolder; + sfd.FileName = Path.GetFileNameWithoutExtension(ofd.FileName) + ".mtp"; + if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + InteropEmu.RomTestRecordFromTest(sfd.FileName, ofd.FileName); + } } } } @@ -1248,30 +1269,36 @@ namespace Mesen.GUI.Forms private void mnuInput_Click(object sender, EventArgs e) { - new frmInputConfig().ShowDialog(sender); + using(frmInputConfig frm = new frmInputConfig()) { + frm.ShowDialog(sender); + } } private void mnuAudioConfig_Click(object sender, EventArgs e) { - new frmAudioConfig().ShowDialog(sender); + using(frmAudioConfig frm = new frmAudioConfig()) { + frm.ShowDialog(sender); + } this.ctrlNsfPlayer.UpdateVolume(); } private void mnuPreferences_Click(object sender, EventArgs e) { - if(new frmPreferences().ShowDialog(sender) == DialogResult.OK) { - ResourceHelper.LoadResources(ConfigManager.Config.PreferenceInfo.DisplayLanguage); - ResourceHelper.UpdateEmuLanguage(); - ResourceHelper.ApplyResources(this); - UpdateMenus(); - InitializeFdsDiskMenu(); - InitializeNsfMode(true); - ctrlRecentGames.UpdateGameInfo(); - } else { - UpdateVideoSettings(); - UpdateMenus(); - UpdateRecentFiles(); - UpdateViewerSize(); + using(frmPreferences frm = new frmPreferences()) { + if(frm.ShowDialog(sender) == DialogResult.OK) { + ResourceHelper.LoadResources(ConfigManager.Config.PreferenceInfo.DisplayLanguage); + ResourceHelper.UpdateEmuLanguage(); + ResourceHelper.ApplyResources(this); + UpdateMenus(); + InitializeFdsDiskMenu(); + InitializeNsfMode(true); + ctrlRecentGames.UpdateGameInfo(); + } else { + UpdateVideoSettings(); + UpdateMenus(); + UpdateRecentFiles(); + UpdateViewerSize(); + } } ResizeRecentGames(sender, e); } @@ -1565,15 +1592,16 @@ namespace Mesen.GUI.Forms private void SelectFdsBiosPrompt() { if(MesenMsgBox.Show("FdsBiosNotFound", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK) { - OpenFileDialog ofd = new OpenFileDialog(); - ofd.SetFilter(ResourceHelper.GetMessage("FilterAll")); - if(ofd.ShowDialog() == DialogResult.OK) { - 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); - } else { - MesenMsgBox.Show("InvalidFdsBios", MessageBoxButtons.OK, MessageBoxIcon.Error); + using(OpenFileDialog ofd = new OpenFileDialog()) { + ofd.SetFilter(ResourceHelper.GetMessage("FilterAll")); + if(ofd.ShowDialog() == DialogResult.OK) { + 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); + } else { + MesenMsgBox.Show("InvalidFdsBios", MessageBoxButtons.OK, MessageBoxIcon.Error); + } } } } @@ -1668,7 +1696,9 @@ namespace Mesen.GUI.Forms private void mnuAbout_Click(object sender, EventArgs e) { - new frmAbout().ShowDialog(); + using(frmAbout frm = new frmAbout()) { + frm.ShowDialog(); + } } private void CheckForUpdates(bool displayResult) @@ -1688,9 +1718,10 @@ namespace Mesen.GUI.Forms if(latestVersion > currentVersion) { this.BeginInvoke((MethodInvoker)(() => { - frmUpdatePrompt frmUpdate = new frmUpdatePrompt(currentVersion, latestVersion, changeLog, fileHash, donateText); - if(frmUpdate.ShowDialog(null, this) == DialogResult.OK) { - Application.Exit(); + using(frmUpdatePrompt frmUpdate = new frmUpdatePrompt(currentVersion, latestVersion, changeLog, fileHash, donateText)) { + if(frmUpdate.ShowDialog(null, this) == DialogResult.OK) { + Application.Exit(); + } } })); } else if(displayResult) { @@ -1740,8 +1771,10 @@ namespace Mesen.GUI.Forms private void ShowVsGameConfig() { VsConfigInfo configInfo = VsConfigInfo.GetCurrentGameConfig(true); - if(new frmVsGameConfig(configInfo).ShowDialog(null, this) == DialogResult.OK) { - VsConfigInfo.ApplyConfig(); + using(frmVsGameConfig frm = new frmVsGameConfig(configInfo)) { + if(frm.ShowDialog(null, this) == DialogResult.OK) { + VsConfigInfo.ApplyConfig(); + } } } @@ -1761,10 +1794,7 @@ namespace Mesen.GUI.Forms { if(_logWindow == null) { _logWindow = new frmLogWindow(); - _logWindow.StartPosition = FormStartPosition.Manual; - _logWindow.Left = this.Left + (this.Width - _logWindow.Width) / 2; - _logWindow.Top = this.Top + (this.Height - _logWindow.Height) / 2; - _logWindow.Show(sender, null); + _logWindow.Show(sender, this); _logWindow.FormClosed += (object a, FormClosedEventArgs b) => { _logWindow = null; }; @@ -1775,7 +1805,9 @@ namespace Mesen.GUI.Forms private void mnuEmulationConfig_Click(object sender, EventArgs e) { - new frmEmulationConfig().ShowDialog(sender); + using(frmEmulationConfig frm = new frmEmulationConfig()) { + frm.ShowDialog(sender); + } } private void InitializeNsfMode(bool updateTextOnly = false, bool gameLoaded = false) @@ -1852,5 +1884,18 @@ namespace Mesen.GUI.Forms { CursorManager.OnMouseLeave(); } + + private void mnuHdPackEditor_Click(object sender, EventArgs e) + { + if(_hdPackEditorWindow == null) { + _hdPackEditorWindow = new frmHdPackEditor(); + _hdPackEditorWindow.Show(sender, this); + _hdPackEditorWindow.FormClosed += (object a, FormClosedEventArgs b) => { + _hdPackEditorWindow = null; + }; + } else { + _hdPackEditorWindow.Focus(); + } + } } } diff --git a/GUI.NET/GUI.NET.csproj b/GUI.NET/GUI.NET.csproj index f6657f02..bf0d0390 100644 --- a/GUI.NET/GUI.NET.csproj +++ b/GUI.NET/GUI.NET.csproj @@ -643,6 +643,12 @@ frmUpdatePrompt.cs + + Form + + + frmHdPackEditor.cs + Form @@ -679,6 +685,7 @@ + @@ -871,6 +878,9 @@ frmUpdatePrompt.cs + + frmHdPackEditor.cs + frmClientConfig.cs diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 00d3d9b3..8c9ce89d 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -502,6 +502,50 @@ namespace Mesen.GUI yScroll = (int)(ppuScroll >> 16) & 0xFFFF; } + [DllImport(DLLPath)] public static extern void HdBuilderStartRecording( + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string saveFolder, + ScaleFilterType filterType, + UInt32 scale, + HdPackRecordFlags flags, + UInt32 chrRamBankSize); + + [DllImport(DLLPath)] public static extern void HdBuilderStopRecording(); + + [DllImport(DLLPath, EntryPoint = "HdBuilderGetBankPreview")] private static extern void HdBuilderGetBankPreviewWrapper(UInt32 bankNumber, UInt32 pageNumber, IntPtr rgbBuffer); + public static byte[] HdBuilderGetBankPreview(UInt32 bankNumber, int scale, UInt32 pageNumber) + { + byte[] frameData = new byte[128*128*4*scale*scale]; + + GCHandle hFrameData = GCHandle.Alloc(frameData, GCHandleType.Pinned); + try { + InteropEmu.HdBuilderGetBankPreviewWrapper(bankNumber, pageNumber, hFrameData.AddrOfPinnedObject()); + } finally { + hFrameData.Free(); + } + + return frameData; + } + + [DllImport(DLLPath, EntryPoint = "HdBuilderGetChrBankList")] private static extern void HdBuilderGetChrBankListWrapper(IntPtr bankList); + public static UInt32[] HdBuilderGetChrBankList() + { + UInt32[] bankList = new UInt32[1024]; + GCHandle hBankList = GCHandle.Alloc(bankList, GCHandleType.Pinned); + try { + InteropEmu.HdBuilderGetChrBankListWrapper(hBankList.AddrOfPinnedObject()); + for(int i = 0; i < bankList.Length; i++) { + if(bankList[i] == UInt32.MaxValue) { + Array.Resize(ref bankList, i); + break; + } + } + } finally { + hBankList.Free(); + } + + return bankList; + } + public static NsfHeader NsfGetHeader() { NsfHeader header = new NsfHeader(); @@ -1027,6 +1071,9 @@ namespace Mesen.GUI public UInt32 Crc32; public UInt32 PrgCrc32; public RomFormat Format; + + [MarshalAs(UnmanagedType.I1)] + public bool IsChrRam; } public enum RomFormat @@ -1043,6 +1090,7 @@ namespace Mesen.GUI public UInt32 Crc32; public UInt32 PrgCrc32; public RomFormat Format; + public bool IsChrRam; public RomInfo(InteropRomInfo romInfo) { @@ -1050,6 +1098,7 @@ namespace Mesen.GUI this.Crc32 = romInfo.Crc32; this.PrgCrc32 = romInfo.PrgCrc32; this.Format = romInfo.Format; + this.IsChrRam = romInfo.IsChrRam; } public string GetRomName() @@ -1292,6 +1341,17 @@ namespace Mesen.GUI CSCD = 2, } + public enum ScaleFilterType + { + xBRZ = 0, + HQX = 1, + Scale2x = 2, + _2xSai = 3, + Super2xSai = 4, + SuperEagle = 5, + Prescale = 6, + } + public enum VideoFilterType { None = 0, @@ -1370,6 +1430,15 @@ namespace Mesen.GUI Exec = 2, } + [Flags] + public enum HdPackRecordFlags + { + None = 0, + UseLargeSprites = 1, + SortByUsageFrequency = 2, + GroupBlankTiles = 4, + } + public struct AddressTypeInfo { public Int32 Address; diff --git a/GUI.NET/Properties/Resources.Designer.cs b/GUI.NET/Properties/Resources.Designer.cs index 97c4e261..e5049600 100644 --- a/GUI.NET/Properties/Resources.Designer.cs +++ b/GUI.NET/Properties/Resources.Designer.cs @@ -360,6 +360,16 @@ namespace Mesen.GUI.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap HdPack { + get { + object obj = ResourceManager.GetObject("HdPack", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/GUI.NET/Properties/Resources.resx b/GUI.NET/Properties/Resources.resx index 3b7ad090..58d333ca 100644 --- a/GUI.NET/Properties/Resources.resx +++ b/GUI.NET/Properties/Resources.resx @@ -304,4 +304,7 @@ ..\Resources\VideoFilter.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\HdPack.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/GUI.NET/Resources/HdPack.png b/GUI.NET/Resources/HdPack.png new file mode 100644 index 00000000..f6caedb3 Binary files /dev/null and b/GUI.NET/Resources/HdPack.png differ diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index 3049d84a..b78d1126 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -15,11 +15,13 @@ #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" #include "../Core/IAudioDevice.h" #include "../Core/MovieManager.h" +#include "../Core/HdPackBuilder.h" #include "../Utilities/AviWriter.h" #ifdef WIN32 @@ -65,6 +67,7 @@ namespace InteropEmu { uint32_t Crc32; uint32_t PrgCrc32; RomFormat Format; + bool IsChrRam; }; extern "C" { @@ -119,7 +122,7 @@ namespace InteropEmu { DllExport const char* __stdcall GetArchiveRomList(char* filename) { std::ostringstream out; - for(string romName : RomLoader::GetArchiveRomList(filename)) { + for(string romName : FileLoader::GetArchiveRomList(filename)) { out << romName << "[!|!]"; } _returnString = out.str(); @@ -184,9 +187,10 @@ namespace InteropEmu { romInfo.Crc32 = Console::GetCrc32(); romInfo.PrgCrc32 = Console::GetPrgCrc32(); romInfo.Format = Console::GetRomFormat(); + romInfo.IsChrRam = Console::IsChrRam(); } else { RomLoader romLoader; - if(romLoader.LoadFile(filename, nullptr, "", archiveFileIndex)) { + if(romLoader.LoadFile(filename, archiveFileIndex)) { RomData romData = romLoader.GetRomData(); _returnString = romData.RomName; @@ -194,12 +198,14 @@ namespace InteropEmu { romInfo.Crc32 = romData.Crc32; romInfo.PrgCrc32 = romData.PrgCrc32; romInfo.Format = RomFormat::Unknown; + romInfo.IsChrRam = romData.ChrRom.size() == 0; } else { _returnString = ""; romInfo.RomName = _returnString.c_str(); romInfo.Crc32 = 0; romInfo.PrgCrc32 = 0; romInfo.Format = RomFormat::Unknown; + romInfo.IsChrRam = false; } } } @@ -462,5 +468,11 @@ namespace InteropEmu { vs->SetInputType(inputType); } } + + DllExport void __stdcall HdBuilderStartRecording(char* saveFolder, ScaleFilterType filterType, uint32_t scale, uint32_t flags, uint32_t chrRamBankSize) { Console::StartRecordingHdPack(saveFolder, filterType, scale, flags, chrRamBankSize); } + DllExport void __stdcall HdBuilderStopRecording() { Console::StopRecordingHdPack(); } + + DllExport void __stdcall HdBuilderGetChrBankList(uint32_t* bankBuffer) { HdPackBuilder::GetChrBankList(bankBuffer); } + DllExport void __stdcall HdBuilderGetBankPreview(uint32_t bankNumber, uint32_t pageNumber, uint8_t *rgbBuffer) { HdPackBuilder::GetBankPreview(bankNumber, pageNumber, rgbBuffer); } } } \ No newline at end of file diff --git a/Utilities/ArchiveReader.cpp b/Utilities/ArchiveReader.cpp index 503dc060..547059f9 100644 --- a/Utilities/ArchiveReader.cpp +++ b/Utilities/ArchiveReader.cpp @@ -17,15 +17,10 @@ std::stringstream ArchiveReader::GetStream(string filename) { std::stringstream ss; if(_initialized) { - uint8_t* buffer = nullptr; - size_t size = 0; - - ExtractFile(filename, &buffer, size); - ss.write((char*)buffer, size); - - delete[] buffer; + vector fileData; + ExtractFile(filename, fileData); + ss.write((char*)fileData.data(), fileData.size()); } - return ss; } diff --git a/Utilities/ArchiveReader.h b/Utilities/ArchiveReader.h index 54deb3e7..6def557f 100644 --- a/Utilities/ArchiveReader.h +++ b/Utilities/ArchiveReader.h @@ -19,5 +19,5 @@ public: vector GetFileList(std::initializer_list extensions); - virtual void ExtractFile(string filename, uint8_t **fileBuffer, size_t &fileSize) = 0; + virtual void ExtractFile(string filename, vector &output) = 0; }; \ No newline at end of file diff --git a/Utilities/HQX/hqx.h b/Utilities/HQX/hqx.h index 1b9a7b6c..e20d6719 100644 --- a/Utilities/HQX/hqx.h +++ b/Utilities/HQX/hqx.h @@ -44,6 +44,8 @@ #endif void HQX_CALLCONV hqxInit(void); +void HQX_CALLCONV hqx(uint32_t scale, uint32_t * src, uint32_t * dest, int width, int height); + void HQX_CALLCONV hq2x_32( uint32_t * src, uint32_t * dest, int width, int height ); void HQX_CALLCONV hq3x_32( uint32_t * src, uint32_t * dest, int width, int height ); void HQX_CALLCONV hq4x_32( uint32_t * src, uint32_t * dest, int width, int height ); diff --git a/Utilities/HQX/init.cpp b/Utilities/HQX/init.cpp index 567274b9..be9de482 100644 --- a/Utilities/HQX/init.cpp +++ b/Utilities/HQX/init.cpp @@ -37,3 +37,12 @@ void HQX_CALLCONV hqxInit(void) RGBtoYUV[c] = (y << 16) + (u << 8) + v; } } + +void HQX_CALLCONV hqx(uint32_t scale, uint32_t * src, uint32_t * dest, int width, int height) +{ + switch(scale) { + case 2: hq2x_32(src, dest, width, height); break; + case 3: hq3x_32(src, dest, width, height); break; + case 4: hq4x_32(src, dest, width, height); break; + } +} \ No newline at end of file diff --git a/Utilities/HexUtilities.cpp b/Utilities/HexUtilities.cpp index e0df04f9..5aacdfc5 100644 --- a/Utilities/HexUtilities.cpp +++ b/Utilities/HexUtilities.cpp @@ -46,16 +46,16 @@ string HexUtilities::ToHex(uint16_t value) return _hexCache[value >> 8] + _hexCache[value & 0xFF]; } -string HexUtilities::ToHex(uint32_t value) +string HexUtilities::ToHex(uint32_t value, bool fullSize) { - if(value <= 0xFF) { + if(fullSize || value > 0xFFFFFF) { + return _hexCache[value >> 24] + _hexCache[(value >> 16) & 0xFF] + _hexCache[(value >> 8) & 0xFF] + _hexCache[value & 0xFF]; + } else if(value <= 0xFF) { return ToHex((uint8_t)value); } else if(value <= 0xFFFF) { return ToHex((uint16_t)value); - } else if(value <= 0xFFFFFF) { - return _hexCache[(value >> 16) & 0xFF] + _hexCache[(value >> 8) & 0xFF] + _hexCache[value & 0xFF]; } else { - return _hexCache[value >> 24] + _hexCache[(value >> 16) & 0xFF] + _hexCache[(value >> 8) & 0xFF] + _hexCache[value & 0xFF]; + return _hexCache[(value >> 16) & 0xFF] + _hexCache[(value >> 8) & 0xFF] + _hexCache[value & 0xFF]; } } diff --git a/Utilities/HexUtilities.h b/Utilities/HexUtilities.h index e51aaa31..b191801e 100644 --- a/Utilities/HexUtilities.h +++ b/Utilities/HexUtilities.h @@ -9,7 +9,7 @@ private: public: static string ToHex(uint8_t addr); static string ToHex(uint16_t addr); - static string ToHex(uint32_t addr); + static string ToHex(uint32_t addr, bool fullSize = false); static string ToHex(vector &data); static int FromHex(string hex); diff --git a/Utilities/PNGHelper.cpp b/Utilities/PNGHelper.cpp index e0d29e0e..4d69f785 100644 --- a/Utilities/PNGHelper.cpp +++ b/Utilities/PNGHelper.cpp @@ -3,9 +3,16 @@ #include "PNGHelper.h" #include "miniz.h" -bool PNGHelper::WritePNG(std::stringstream &stream, uint8_t* buffer, uint32_t xSize, uint32_t ySize, uint32_t bitsPerPixel) +bool PNGHelper::WritePNG(std::stringstream &stream, uint32_t* buffer, uint32_t xSize, uint32_t ySize, uint32_t bitsPerPixel) { size_t pngSize = 0; + + //ARGB -> ABGR + uint32_t size = xSize * ySize * bitsPerPixel / 8 / 4; + for(uint32_t i = 0; i < size; i++) { + buffer[i] = (buffer[i] & 0xFF00FF00) | ((buffer[i] & 0xFF0000) >> 16) | ((buffer[i] & 0xFF) << 16); + } + void *pngData = tdefl_write_image_to_png_file_in_memory_ex(buffer, xSize, ySize, bitsPerPixel / 8, &pngSize, MZ_DEFAULT_LEVEL, MZ_FALSE); if(!pngData) { std::cout << "tdefl_write_image_to_png_file_in_memory_ex() failed!" << std::endl; @@ -17,7 +24,7 @@ bool PNGHelper::WritePNG(std::stringstream &stream, uint8_t* buffer, uint32_t xS } } -bool PNGHelper::WritePNG(string filename, uint8_t* buffer, uint32_t xSize, uint32_t ySize, uint32_t bitsPerPixel) +bool PNGHelper::WritePNG(string filename, uint32_t* buffer, uint32_t xSize, uint32_t ySize, uint32_t bitsPerPixel) { std::stringstream stream; if(WritePNG(stream, buffer, xSize, ySize, bitsPerPixel)) { @@ -31,7 +38,7 @@ bool PNGHelper::WritePNG(string filename, uint8_t* buffer, uint32_t xSize, uint3 return false; } -void PNGHelper::ReadPNG(string filename, vector &pngData, uint32_t &pngWidth, uint32_t &pngHeight) +bool PNGHelper::ReadPNG(string filename, vector &pngData, uint32_t &pngWidth, uint32_t &pngHeight) { unsigned long width; unsigned long height; @@ -56,7 +63,11 @@ void PNGHelper::ReadPNG(string filename, vector &pngData, uint32_t &png pngWidth = width; pngHeight = height; delete[] buffer; + + return true; } + + return false; } /* diff --git a/Utilities/PNGHelper.h b/Utilities/PNGHelper.h index 6faffef8..f6c49e75 100644 --- a/Utilities/PNGHelper.h +++ b/Utilities/PNGHelper.h @@ -7,7 +7,7 @@ private: static int DecodePNG(vector& out_image, unsigned long& image_width, unsigned long& image_height, const unsigned char* in_png, size_t in_size, bool convert_to_rgba32 = true); public: - static bool WritePNG(std::stringstream &stream, uint8_t* buffer, uint32_t xSize, uint32_t ySize, uint32_t bitsPerPixel = 32); - static bool WritePNG(string filename, uint8_t* buffer, uint32_t xSize, uint32_t ySize, uint32_t bitsPerPixel = 32); - static void ReadPNG(string filename, vector &pngData, uint32_t &pngWidth, uint32_t &pngHeight); + 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); }; \ No newline at end of file diff --git a/Utilities/SZReader.cpp b/Utilities/SZReader.cpp index 44a7978f..89cd0605 100644 --- a/Utilities/SZReader.cpp +++ b/Utilities/SZReader.cpp @@ -26,7 +26,7 @@ bool SZReader::InternalLoadArchive(void* buffer, size_t size) return !SzArEx_Open(&_archive, &_lookStream.s, &allocImp, &allocTempImp); } -void SZReader::ExtractFile(string filename, uint8_t **fileBuffer, size_t &fileSize) +void SZReader::ExtractFile(string filename, vector &output) { if(_initialized) { char16_t *utf16Filename = (char16_t*)SzAlloc(nullptr, 2000); @@ -49,9 +49,7 @@ void SZReader::ExtractFile(string filename, uint8_t **fileBuffer, size_t &fileSi WRes res = SzArEx_Extract(&_archive, &_lookStream.s, i, &blockIndex, &outBuffer, &outBufferSize, &offset, &outSizeProcessed, &_allocImp, &_allocTempImp); if(res == SZ_OK) { uint8_t* buf = new uint8_t[outSizeProcessed]; - memcpy(buf, outBuffer+offset, outSizeProcessed); - *fileBuffer = buf; - fileSize = outSizeProcessed; + output = vector(outBuffer+offset, outBuffer+offset+outSizeProcessed); } IAlloc_Free(&_allocImp, outBuffer); break; diff --git a/Utilities/SZReader.h b/Utilities/SZReader.h index 756be942..7d41684d 100644 --- a/Utilities/SZReader.h +++ b/Utilities/SZReader.h @@ -24,5 +24,5 @@ public: SZReader(); virtual ~SZReader(); - void ExtractFile(string filename, uint8_t **fileBuffer, size_t &fileSize); + void ExtractFile(string filename, vector &output); }; \ No newline at end of file diff --git a/Utilities/ZipReader.cpp b/Utilities/ZipReader.cpp index cdcbf59a..49bbe8a7 100644 --- a/Utilities/ZipReader.cpp +++ b/Utilities/ZipReader.cpp @@ -36,7 +36,7 @@ vector ZipReader::InternalGetFileList() return fileList; } -void ZipReader::ExtractFile(string filename, uint8_t **fileBuffer, size_t &fileSize) +void ZipReader::ExtractFile(string filename, vector &output) { if(_initialized) { size_t uncompSize; @@ -45,12 +45,9 @@ void ZipReader::ExtractFile(string filename, uint8_t **fileBuffer, size_t &fileS std::cout << "mz_zip_reader_extract_file_to_heap() failed!" << std::endl; } - *fileBuffer = new uint8_t[uncompSize]; - memcpy(*fileBuffer, p, uncompSize); + output = vector((uint8_t*)p, (uint8_t*)p + uncompSize); // We're done. mz_free(p); - - fileSize = uncompSize; } } \ No newline at end of file diff --git a/Utilities/ZipReader.h b/Utilities/ZipReader.h index 640b615e..797a17df 100644 --- a/Utilities/ZipReader.h +++ b/Utilities/ZipReader.h @@ -16,5 +16,5 @@ public: ZipReader(); virtual ~ZipReader(); - void ExtractFile(string filename, uint8_t **fileBuffer, size_t &fileSize); + void ExtractFile(string filename, vector &output); }; \ No newline at end of file