HD Packs: Improvements, new features, bug fixes

This commit is contained in:
Souryo 2017-06-28 19:00:08 -04:00
parent f1ba279de6
commit e82371e2f8
67 changed files with 3407 additions and 883 deletions

View file

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

View file

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

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include <cmath>
#include "BaseRenderer.h"
#include "Console.h"
#include "EmulationSettings.h"
#include "VideoDecoder.h"
#include "PPU.h"

View file

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

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include <thread>
#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> 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<BaseMapper> 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<uint8_t> fileData;
if(FileLoader::LoadFile(romFilename, filestream, archiveFileIndex, fileData)) {
LoadHdPack(romFilename, fileData, patchFilename);
if(!patchFilename.empty()) {
FileLoader::ApplyPatch(patchFilename, fileData);
}
shared_ptr<BaseMapper> 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<uint8_t> &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();
}

View file

@ -4,6 +4,7 @@
#include <atomic>
#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> _autoSaveManager;
shared_ptr<HdPackBuilder> _hdPackBuilder;
unique_ptr<HdPackData> _hdData;
NesModel _model;
string _romFilepath;
@ -51,6 +57,8 @@ class Console
bool _initialized = false;
void LoadHdPack(string romFilename, vector<uint8_t> &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<Console> GetInstance();
static void Release();
};

View file

@ -413,6 +413,11 @@
<ClInclude Include="AutomaticRomTest.h" />
<ClInclude Include="BaseRenderer.h" />
<ClInclude Include="FceuxMovie.h" />
<ClInclude Include="FileLoader.h" />
<ClInclude Include="HdBuilderPpu.h" />
<ClInclude Include="HdData.h" />
<ClInclude Include="HdPackBuilder.h" />
<ClInclude Include="HdPackLoader.h" />
<ClInclude Include="Mapper174.h" />
<ClInclude Include="Rambo1_158.h" />
<ClInclude Include="RecordedRomTest.h" />
@ -774,6 +779,10 @@
<ClCompile Include="AutomaticRomTest.cpp" />
<ClCompile Include="BaseRenderer.cpp" />
<ClCompile Include="FceuxMovie.cpp" />
<ClCompile Include="FileLoader.cpp" />
<ClCompile Include="HdNesPack.cpp" />
<ClCompile Include="HdPackBuilder.cpp" />
<ClCompile Include="HdPackLoader.cpp" />
<ClCompile Include="RecordedRomTest.cpp" />
<ClCompile Include="AutoSaveManager.cpp" />
<ClCompile Include="AviRecorder.cpp" />

View file

@ -1180,6 +1180,21 @@
<ClInclude Include="BaseRenderer.h">
<Filter>VideoDecoder</Filter>
</ClInclude>
<ClInclude Include="HdPackBuilder.h">
<Filter>VideoDecoder\HD</Filter>
</ClInclude>
<ClInclude Include="HdBuilderPpu.h">
<Filter>VideoDecoder\HD</Filter>
</ClInclude>
<ClInclude Include="HdData.h">
<Filter>VideoDecoder\HD</Filter>
</ClInclude>
<ClInclude Include="HdPackLoader.h">
<Filter>VideoDecoder\HD</Filter>
</ClInclude>
<ClInclude Include="FileLoader.h">
<Filter>Nes\RomLoader</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@ -1404,5 +1419,17 @@
<ClCompile Include="BaseRenderer.cpp">
<Filter>VideoDecoder</Filter>
</ClCompile>
<ClCompile Include="HdPackBuilder.cpp">
<Filter>VideoDecoder\HD</Filter>
</ClCompile>
<ClCompile Include="HdPackLoader.cpp">
<Filter>VideoDecoder\HD</Filter>
</ClCompile>
<ClCompile Include="HdNesPack.cpp">
<Filter>VideoDecoder\HD</Filter>
</ClCompile>
<ClCompile Include="FileLoader.cpp">
<Filter>Misc</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -1,5 +1,6 @@
#include "stdafx.h"
#include <thread>
#include "../Utilities/FolderUtilities.h"
#include "MessageManager.h"
#include "Debugger.h"
#include "Console.h"

View file

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

112
Core/FileLoader.cpp Normal file
View file

@ -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<uint8_t> &fileData)
{
file.seekg(0, ios::end);
uint32_t fileSize = (uint32_t)file.tellg();
file.seekg(0, ios::beg);
fileData = vector<uint8_t>(fileSize, 0);
file.read((char*)fileData.data(), fileSize);
}
bool FileLoader::LoadFromArchive(istream &zipFile, ArchiveReader &reader, int32_t archiveFileIndex, vector<uint8_t> &fileData)
{
ReadFile(zipFile, fileData);
reader.LoadArchive(fileData.data(), fileData.size());
vector<string> fileList = reader.GetFileList({ ".nes", ".fds", ".nsf", ".nsfe", ".unf" });
if(fileList.empty() || archiveFileIndex > (int32_t)fileList.size()) {
return false;
}
if(archiveFileIndex == -1) {
archiveFileIndex = 0;
}
reader.ExtractFile(fileList[archiveFileIndex], fileData);
return true;
}
bool FileLoader::LoadFile(string filename, istream *filestream, int32_t archiveFileIndex, vector<uint8_t> &fileData)
{
ifstream file;
istream* input = nullptr;
if(!filestream) {
file.open(filename, ios::in | ios::binary);
if(file) {
input = &file;
}
} else {
input = filestream;
}
if(input) {
char header[15];
input->seekg(0, ios::beg);
input->read(header, 15);
input->seekg(0, ios::beg);
if(memcmp(header, "PK", 2) == 0) {
ZipReader reader;
return LoadFromArchive(*input, reader, archiveFileIndex, fileData);
} else if(memcmp(header, "7z", 2) == 0) {
SZReader reader;
return LoadFromArchive(*input, reader, archiveFileIndex, fileData);
} else {
if(archiveFileIndex > 0) {
return false;
}
ReadFile(*input, fileData);
return true;
}
}
return false;
}
void FileLoader::ApplyPatch(string patchPath, vector<uint8_t> &fileData)
{
//Apply patch file
MessageManager::DisplayMessage("Patch", "ApplyingPatch", FolderUtilities::GetFilename(patchPath, true));
ifstream patchFile(patchPath, ios::binary | ios::in);
if(patchFile.good()) {
char buffer[5] = {};
patchFile.read(buffer, 5);
patchFile.close();
if(memcmp(buffer, "PATCH", 5) == 0) {
fileData = IpsPatcher::PatchBuffer(patchPath, fileData);
} else if(memcmp(buffer, "UPS1", 4) == 0) {
fileData = UpsPatcher::PatchBuffer(patchPath, fileData);
} else if(memcmp(buffer, "BPS1", 4) == 0) {
fileData = BpsPatcher::PatchBuffer(patchPath, fileData);
}
}
}
vector<string> FileLoader::GetArchiveRomList(string filename)
{
ifstream in(filename, ios::in | ios::binary);
if(in) {
uint8_t header[2];
in.read((char*)header, 2);
in.close();
if(memcmp(header, "PK", 2) == 0) {
ZipReader reader;
reader.LoadArchive(filename);
return reader.GetFileList({ ".nes", ".fds", ".nsf", ".nsfe", "*.unf" });
} else if(memcmp(header, "7z", 2) == 0) {
SZReader reader;
reader.LoadArchive(filename);
return reader.GetFileList({ ".nes", ".fds", ".nsf", ".nsfe", "*.unf" });
}
}
return{};
}

15
Core/FileLoader.h Normal file
View file

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

109
Core/HdBuilderPpu.h Normal file
View file

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

364
Core/HdData.h Normal file
View file

@ -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<HdTileKey>
{
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 << "<condition>" << 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<uint32_t> HdTileData;
uint32_t ChrBankId;
vector<HdPackCondition*> Conditions;
bool MatchesCondition(HdPpuPixelInfo *screenTiles, int x, int y)
{
for(HdPackCondition* condition : Conditions) {
if(!condition->CheckCondition(screenTiles, x, y)) {
return false;
}
}
return true;
}
vector<uint32_t> ToRgb()
{
vector<uint32_t> 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 << "<tile>" << 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 << "<tile>" <<
pngIndex << "," <<
TileIndex << "," <<
HexUtilities::ToHex(PaletteColors, true) << "," <<
X << "," <<
Y << "," <<
(double)Brightness / 255 << "," <<
(DefaultTile ? "Y" : "N");
}
return out.str();
}
};
struct HdPackBitmapInfo
{
vector<uint8_t> PixelData;
uint32_t Width;
uint32_t Height;
};
struct HdBackgroundFileData
{
string PngName;
uint32_t Width;
uint32_t Height;
vector<uint8_t> PixelData;
};
struct HdBackgroundInfo
{
HdBackgroundFileData* Data;
uint16_t Brightness;
vector<HdPackCondition*> 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<HdBackgroundInfo> Backgrounds;
vector<unique_ptr<HdBackgroundFileData>> BackgroundFileData;
vector<unique_ptr<HdPackTileInfo>> Tiles;
vector<unique_ptr<HdPackCondition>> Conditions;
std::unordered_map<HdTileKey, vector<HdPackTileInfo*>> TileByKey;
std::unordered_map<string, string> PatchesByHash;
vector<uint32_t> Palette;
vector<uint32_t> 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,
};

229
Core/HdNesPack.cpp Normal file
View file

@ -0,0 +1,229 @@
#include "stdafx.h"
#include <algorithm>
#include <unordered_map>
#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<HdTileKey, vector<HdPackTileInfo*>>::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<HdTileKey, HdPackTileInfo*>::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);
}
}

View file

@ -1,279 +1,26 @@
#pragma once
#include "stdafx.h"
#include "HdData.h"
#include <unordered_map>
#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<uint8_t> PixelData;
uint32_t Width;
uint32_t Height;
};
class HdNesPack : public INotificationListener
class HdNesPack
{
private:
vector<HdPackBitmapInfo> _hdNesBitmaps;
vector<HdPackTileInfo> _hdNesTiles;
std::unordered_map<uint64_t, HdPackTileInfo*> _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) == "<scale>") {
lineContent = lineContent.substr(7);
_hdScale = std::stoi(lineContent);
} else if(lineContent.substr(0, 5) == "<img>") {
lineContent = lineContent.substr(5);
HdPackBitmapInfo bitmapInfo;
string imageFile = FolderUtilities::CombinePath(hdPackFolder, lineContent);
PNGHelper::ReadPNG(imageFile, bitmapInfo.PixelData, bitmapInfo.Width, bitmapInfo.Height);
_hdNesBitmaps.push_back(bitmapInfo);
} else if(lineContent.substr(0, 6) == "<tile>") {
lineContent = lineContent.substr(6);
vector<string> 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<string> split(const string &s, char delim)
{
vector<string> 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<uint64_t, HdPackTileInfo*>::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);
};

402
Core/HdPackBuilder.cpp Normal file
View file

@ -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<HdPackTileInfo> &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<uint32_t, vector<HdPackTileInfo*>>();
}
std::map<uint32_t, vector<HdPackTileInfo*>> &paletteMap = _tilesByChrBankByPalette[chrBankId];
if(paletteMap.find(palette) == paletteMap.end()) {
paletteMap[palette] = vector<HdPackTileInfo*>(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<HdPackTileInfo>(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<uint32_t> originalTile = tile->ToRgb();
vector<uint32_t> 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 << "<ver>100" << std::endl;
ss << "<scale>" << _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 << "<img>" << 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<const uint32_t, std::map<uint32_t, vector<HdPackTileInfo*>>> &kvp : _tilesByChrBankByPalette) {
/*if(true) { //flatten palette
for(int i = 0; i < 256; i++) {
auto readItt = kvp.second.begin();
auto writeItt = kvp.second.begin();
while(writeItt != kvp.second.end() && writeItt->second[i]) {
readItt++;
writeItt++;
}
for(; readItt != kvp.second.end() && writeItt != kvp.second.end(); readItt++) {
if(writeItt->second[i] == nullptr && readItt->second[i] != nullptr) {
writeItt->second[i] = readItt->second[i];
readItt->second[i] = nullptr;
while(writeItt != kvp.second.end() && writeItt->second[i]) {
writeItt++;
}
}
}
}
}*/
if(_flags & HdPackRecordFlags::SortByUsageFrequency) {
for(int i = 0; i < 256; i++) {
vector<std::pair<uint32_t, HdPackTileInfo*>> tiles;
for(std::pair<const uint32_t, vector<HdPackTileInfo*>> &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<uint32_t, HdPackTileInfo*> &a, std::pair<uint32_t, HdPackTileInfo*> &b) {
return a.first > b.first;
});
int j = 0;
for(std::pair<const uint32_t, vector<HdPackTileInfo*>> &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<const uint32_t, vector<HdPackTileInfo*>> &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<HdPackCondition> &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<const uint32_t, std::map<uint32_t, vector<HdPackTileInfo*>>> &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<uint32_t, vector<HdPackTileInfo*>> bankData = result->second;
if(_instance->_flags & HdPackRecordFlags::SortByUsageFrequency) {
for(int i = 0; i < 256; i++) {
vector<std::pair<uint32_t, HdPackTileInfo*>> tiles;
for(std::pair<const uint32_t, vector<HdPackTileInfo*>> &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<uint32_t, HdPackTileInfo*> &a, std::pair<uint32_t, HdPackTileInfo*> &b) {
return a.first > b.first;
});
int j = 0;
for(std::pair<const uint32_t, vector<HdPackTileInfo*>> &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();
}

49
Core/HdPackBuilder.h Normal file
View file

@ -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 <map>
class HdPackBuilder
{
private:
static HdPackBuilder* _instance;
HdPackData _hdData;
std::unordered_map<HdTileKey, uint32_t> _tileUsageCount;
std::map<uint32_t, std::map<uint32_t, vector<HdPackTileInfo*>>> _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);
};

337
Core/HdPackLoader.cpp Normal file
View file

@ -0,0 +1,337 @@
#include "stdafx.h"
#include <unordered_map>
#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<HdPackCondition*> 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<string> tokens;
if(lineContent.substr(0, 5) == "<ver>") {
_data->Version = stoi(lineContent.substr(5));
} else if(lineContent.substr(0, 7) == "<scale>") {
lineContent = lineContent.substr(7);
_data->Scale = std::stoi(lineContent);
} else if(lineContent.substr(0, 5) == "<img>") {
lineContent = lineContent.substr(5);
HdPackBitmapInfo bitmapInfo;
string imageFile = FolderUtilities::CombinePath(_hdPackFolder, lineContent);
PNGHelper::ReadPNG(imageFile, bitmapInfo.PixelData, bitmapInfo.Width, bitmapInfo.Height);
_hdNesBitmaps.push_back(bitmapInfo);
} else if(lineContent.substr(0, 7) == "<patch>") {
tokens = StringUtilities::Split(lineContent.substr(7), ',');
ProcessPatchTag(tokens);
} else if(lineContent.substr(0, 12) == "<background>") {
tokens = StringUtilities::Split(lineContent.substr(12), ',');
ProcessBackgroundTag(tokens, conditions);
} else if(lineContent.substr(0, 11) == "<condition>") {
tokens = StringUtilities::Split(lineContent.substr(11), ',');
ProcessConditionTag(tokens);
} else if(lineContent.substr(0, 6) == "<tile>") {
tokens = StringUtilities::Split(lineContent.substr(6), ',');
ProcessTileTag(tokens, conditions);
} else if(lineContent.substr(0, 9) == "<options>") {
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<string> &tokens)
{
if(tokens[1].size() != 40) {
MessageManager::Log(string("[HDPack] Invalid SHA1 hash for patch (" + tokens[0] + "): " + tokens[1]));
return;
}
if(!ifstream(FolderUtilities::CombinePath(_hdPackFolder, tokens[0]))) {
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<string> &tokens, vector<HdPackCondition*> 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<HdPackTileInfo>(tileInfo));
}
void HdPackLoader::ProcessOptionTag(vector<string> &tokens)
{
for(string token : tokens) {
if(token == "disableSpriteLimit") {
_data->OptionFlags |= (int)HdPackOptions::NoSpriteLimit;
}
}
}
void HdPackLoader::ProcessConditionTag(vector<string> &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<HdPackCondition>(condition));
}
void HdPackLoader::ProcessBackgroundTag(vector<string> &tokens, vector<HdPackCondition*> conditions)
{
HdBackgroundFileData* fileData = nullptr;
for(unique_ptr<HdBackgroundFileData> &bgData : _data->BackgroundFileData) {
if(bgData->PngName == tokens[0]) {
fileData = bgData.get();
}
}
if(!fileData) {
vector<uint8_t> pixelData;
uint32_t width, height;
string imageFile = FolderUtilities::CombinePath(_hdPackFolder, tokens[0]);
if(PNGHelper::ReadPNG(imageFile, pixelData, width, height)) {
_data->BackgroundFileData.push_back(unique_ptr<HdBackgroundFileData>(new HdBackgroundFileData()));
fileData = _data->BackgroundFileData.back().get();
fileData->PixelData = pixelData;
fileData->Width = width;
fileData->Height = height;
fileData->PngName = tokens[0];
}
}
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<HdPackCondition*> HdPackLoader::ParseConditionString(string conditionString, vector<unique_ptr<HdPackCondition>> &conditions)
{
vector<string> conditionNames = StringUtilities::Split(conditionString, '&');
vector<HdPackCondition*> result;
for(string conditionName : conditionNames) {
conditionName.erase(conditionName.find_last_not_of(" \n\r\t") + 1);
bool found = false;
for(unique_ptr<HdPackCondition> &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<uint32_t> paletteData;
uint8_t rgb[3];
while(!file.eof()) {
file.read((char*)rgb, 3);
if(!file.eof()) {
paletteData.push_back(0xFF000000 | (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]);
}
}
if(paletteData.size() == 0x40) {
_data->PaletteBackup = vector<uint32_t>(0x40, 0);
EmulationSettings::GetRgbPalette(_data->PaletteBackup.data());
_data->Palette = paletteData;
}
}
}
void HdPackLoader::InitializeHdPack()
{
for(unique_ptr<HdPackTileInfo> &tileInfo : _data->Tiles) {
auto tiles = _data->TileByKey.find(tileInfo->GetKey(false));
if(tiles == _data->TileByKey.end()) {
_data->TileByKey[tileInfo->GetKey(false)] = vector<HdPackTileInfo*>();
}
_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<HdPackTileInfo*>();
}
_data->TileByKey[tileInfo->GetKey(true)].push_back(tileInfo.get());
}
}
}

29
Core/HdPackLoader.h Normal file
View file

@ -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<HdPackBitmapInfo> _hdNesBitmaps;
HdPackLoader(string hdPackDefinitionFile, HdPackData *data);
bool LoadPack();
void InitializeHdPack();
void LoadCustomPalette();
void ProcessPatchTag(vector<string> &tokens);
void ProcessConditionTag(vector<string> &tokens);
void ProcessTileTag(vector<string> &tokens, vector<HdPackCondition*> conditions);
void ProcessBackgroundTag(vector<string> &tokens, vector<HdPackCondition*> conditions);
void ProcessOptionTag(vector<string>& tokens);
vector<HdPackCondition*> ParseConditionString(string conditionString, vector<unique_ptr<HdPackCondition>> &conditions);
};

View file

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

View file

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

View file

@ -564,11 +564,11 @@ BaseMapper* MapperFactory::GetMapperFromID(RomData &romData)
return nullptr;
}
shared_ptr<BaseMapper> MapperFactory::InitializeFromFile(string romFilename, stringstream *filestream, string patchFilename, int32_t archiveFileIndex)
shared_ptr<BaseMapper> MapperFactory::InitializeFromFile(string romFilename, vector<uint8_t> &fileData)
{
RomLoader loader;
if(loader.LoadFile(romFilename, filestream, patchFilename, archiveFileIndex)) {
if(loader.LoadFile(romFilename, fileData)) {
RomData romData = loader.GetRomData();
shared_ptr<BaseMapper> mapper(GetMapperFromID(romData));

View file

@ -12,5 +12,5 @@ class MapperFactory
static const uint16_t FdsMapperID = 65535;
static const uint16_t NsfMapperID = 65534;
static shared_ptr<BaseMapper> InitializeFromFile(string romFilename, stringstream *filestream, string patchFilename, int32_t archiveFileIndex);
static shared_ptr<BaseMapper> InitializeFromFile(string romFilename, vector<uint8_t> &fileData);
};

View file

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

View file

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

View file

@ -1,154 +1,56 @@
#include "stdafx.h"
#include <algorithm>
#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<string> 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<string> fileList = reader.GetFileList({ ".nes", ".fds", ".nsf", ".nsfe", ".unf" });
int32_t currentIndex = 0;
if(archiveFileIndex > (int32_t)fileList.size()) {
vector<uint8_t> 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<uint8_t> &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<uint8_t> &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<uint8_t> 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<uint8_t> fileData;
int fileIndex = 0;
while(FileLoader::LoadFile(filename, nullptr, fileIndex, fileData)) {
RomLoader loader;
if(loader.LoadFile(filename, fileData)) {
if(hashInfo.Crc32Hash == loader._romData.Crc32 || hashInfo.Sha1Hash.compare(loader._romData.Sha1) == 0) {
return fileIndex;
}
fileIndex++;
}
fileIndex++;
}
return -1;
}

View file

@ -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<uint8_t> &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<uint8_t> &fileData);
RomData GetRomData();
static string FindMatchingRomInFolder(string folder, string romFilename, HashInfo hashInfo, bool useFastSearch, int32_t &archiveFileIndex);
static vector<string> GetArchiveRomList(string filename);
};

View file

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

View file

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

View file

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

View file

@ -12,7 +12,7 @@ RomData iNesLoader::LoadRom(vector<uint8_t>& 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));

View file

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

View file

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

View file

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

View file

@ -117,7 +117,10 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<metadata name="ctxMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>116, 17</value>
</metadata>
</root>

View file

@ -27,10 +27,14 @@
/// </summary>
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;
}
}

View file

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

View file

@ -117,4 +117,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="ctxMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -27,6 +27,7 @@
/// </summary>
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;
}
}

View file

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

View file

@ -117,4 +117,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="ctxMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

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

View file

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

View file

@ -0,0 +1,398 @@
namespace Mesen.GUI.Forms.HdPackEditor
{
partial class frmHdPackEditor
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View file

@ -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, "<scale>(\\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 },
};
}
}

View file

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="tmrRefresh.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>107, 17</value>
</metadata>
</root>

View file

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

View file

@ -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<string> passedTests = new List<string>();
List<string> failedTests = new List<string>();
List<int> failedFrameCount = new List<int>();
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<string> passedTests = new List<string>();
List<string> failedTests = new List<string>();
List<int> failedFrameCount = new List<int>();
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();
}
}
}
}

View file

@ -643,6 +643,12 @@
<Compile Include="Forms\frmUpdatePrompt.Designer.cs">
<DependentUpon>frmUpdatePrompt.cs</DependentUpon>
</Compile>
<Compile Include="Forms\HdPackEditor\frmHdPackEditor.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\HdPackEditor\frmHdPackEditor.Designer.cs">
<DependentUpon>frmHdPackEditor.cs</DependentUpon>
</Compile>
<Compile Include="Forms\MesenMsgBox.cs" />
<Compile Include="Forms\NetPlay\frmClientConfig.cs">
<SubType>Form</SubType>
@ -679,6 +685,7 @@
<Compile Include="ResourceManager.cs" />
<Compile Include="RuntimeChecker.cs" />
<Compile Include="SingleInstance.cs" />
<None Include="Resources\HdPack.png" />
<None Include="Resources\VideoFilter.png" />
<None Include="Resources\map.png" />
<None Include="Resources\PowerCycle.png" />
@ -871,6 +878,9 @@
<EmbeddedResource Include="Forms\frmUpdatePrompt.resx">
<DependentUpon>frmUpdatePrompt.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\HdPackEditor\frmHdPackEditor.resx">
<DependentUpon>frmHdPackEditor.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\NetPlay\frmClientConfig.resx">
<DependentUpon>frmClientConfig.cs</DependentUpon>
</EmbeddedResource>

View file

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

View file

@ -360,6 +360,16 @@ namespace Mesen.GUI.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap HdPack {
get {
object obj = ResourceManager.GetObject("HdPack", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>

View file

@ -304,4 +304,7 @@
<data name="VideoFilter" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\VideoFilter.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="HdPack" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\HdPack.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

View file

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

View file

@ -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<uint8_t> fileData;
ExtractFile(filename, fileData);
ss.write((char*)fileData.data(), fileData.size());
}
return ss;
}

View file

@ -19,5 +19,5 @@ public:
vector<string> GetFileList(std::initializer_list<string> extensions);
virtual void ExtractFile(string filename, uint8_t **fileBuffer, size_t &fileSize) = 0;
virtual void ExtractFile(string filename, vector<uint8_t> &output) = 0;
};

View file

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

View file

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

View file

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

View file

@ -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<uint8_t> &data);
static int FromHex(string hex);

View file

@ -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<uint8_t> &pngData, uint32_t &pngWidth, uint32_t &pngHeight)
bool PNGHelper::ReadPNG(string filename, vector<uint8_t> &pngData, uint32_t &pngWidth, uint32_t &pngHeight)
{
unsigned long width;
unsigned long height;
@ -56,7 +63,11 @@ void PNGHelper::ReadPNG(string filename, vector<uint8_t> &pngData, uint32_t &png
pngWidth = width;
pngHeight = height;
delete[] buffer;
return true;
}
return false;
}
/*

View file

@ -7,7 +7,7 @@ private:
static int DecodePNG(vector<unsigned char>& 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<uint8_t> &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<uint8_t> &pngData, uint32_t &pngWidth, uint32_t &pngHeight);
};

View file

@ -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<uint8_t> &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<uint8_t>(outBuffer+offset, outBuffer+offset+outSizeProcessed);
}
IAlloc_Free(&_allocImp, outBuffer);
break;

View file

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

View file

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

View file

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