mirror of
https://github.com/SourMesen/Mesen.git
synced 2025-04-02 10:52:48 -04:00
HD packs - Fixed blending, vertical mirroring & added missing file
This commit is contained in:
parent
8b4104a4ca
commit
21220052d9
4 changed files with 280 additions and 0 deletions
275
Core/HdNesPack.h
Normal file
275
Core/HdNesPack.h
Normal file
|
@ -0,0 +1,275 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include "Console.h"
|
||||
#include "MessageManager.h"
|
||||
#include "EmulationSettings.h"
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
#include "../Core/IVideoDevice.h"
|
||||
#include "../Utilities/PNGHelper.h"
|
||||
|
||||
extern const uint32_t PPU_PALETTE_ARGB[];
|
||||
|
||||
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 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
|
||||
{
|
||||
private:
|
||||
vector<HdPackBitmapInfo> _hdNesBitmaps;
|
||||
vector<HdPackTileInfo> _hdNesTiles;
|
||||
std::unordered_map<uint64_t, HdPackTileInfo*> _tileInfoByKey;
|
||||
SimpleLock _loadLock;
|
||||
uint32_t _hdScale;
|
||||
|
||||
void LoadHdNesPack()
|
||||
{
|
||||
_loadLock.Acquire();
|
||||
|
||||
_hdNesBitmaps.clear();
|
||||
_hdNesTiles.clear();
|
||||
_tileInfoByKey.clear();
|
||||
|
||||
string hdPackFolder = FolderUtilities::CombinePath(FolderUtilities::GetHdPackFolder(), FolderUtilities::GetFilename(Console::GetROMPath(), 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;
|
||||
}
|
||||
|
||||
public:
|
||||
HdNesPack()
|
||||
{
|
||||
_hdScale = 2;
|
||||
|
||||
LoadHdNesPack();
|
||||
MessageManager::RegisterNotificationListener(this);
|
||||
}
|
||||
|
||||
~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 = PPU_PALETTE_ARGB[tileInfo.BgColor];
|
||||
}
|
||||
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;
|
||||
auto 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) {
|
||||
auto 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) {
|
||||
DrawTile(pixelInfo.Sprite, *hdPackSpriteInfo, outputBuffer, screenWidth, !hdPackTileInfo);
|
||||
}
|
||||
|
||||
_loadLock.Release();
|
||||
}
|
||||
|
||||
void ProcessNotification(ConsoleNotificationType type)
|
||||
{
|
||||
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 true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -32,6 +32,7 @@ protected:
|
|||
|
||||
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;
|
||||
|
@ -56,6 +57,7 @@ protected:
|
|||
|
||||
tileInfo.Tile.OffsetX = (_state.XScroll + ((_cycle - 1) & 0x07)) & 0x07;
|
||||
tileInfo.Tile.HorizontalMirroring = false;
|
||||
tileInfo.Tile.VerticalMirroring = false;
|
||||
tileInfo.Tile.BgColor = ReadPaletteRAM(0);
|
||||
} 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."
|
||||
|
|
|
@ -342,6 +342,7 @@ void PPU::LoadSpriteTileInfo(uint8_t spriteIndex)
|
|||
if(spriteIndex < _spriteCount && spriteY < 240) {
|
||||
_spriteTiles[spriteIndex].BackgroundPriority = backgroundPriority;
|
||||
_spriteTiles[spriteIndex].HorizontalMirror = horizontalMirror;
|
||||
_spriteTiles[spriteIndex].VerticalMirror = verticalMirror;
|
||||
_spriteTiles[spriteIndex].PaletteOffset = ((attributes & 0x03) << 2) | 0x10;
|
||||
_spriteTiles[spriteIndex].LowByte = _memoryManager->ReadVRAM(tileAddr);
|
||||
_spriteTiles[spriteIndex].HighByte = _memoryManager->ReadVRAM(tileAddr + 8);
|
||||
|
|
|
@ -75,6 +75,8 @@ struct SpriteInfo : TileInfo
|
|||
bool HorizontalMirror;
|
||||
bool BackgroundPriority;
|
||||
uint8_t SpriteX;
|
||||
|
||||
bool VerticalMirror; //used by HD ppu
|
||||
};
|
||||
|
||||
struct PPUDebugState
|
||||
|
|
Loading…
Add table
Reference in a new issue