mirror of
https://github.com/libretro/Mesen.git
synced 2024-12-18 14:56:38 +00:00
278 lines
7.9 KiB
C++
278 lines
7.9 KiB
C++
#pragma once
|
|
|
|
#include <unordered_map>
|
|
#include "Console.h"
|
|
#include "MessageManager.h"
|
|
#include "EmulationSettings.h"
|
|
#include "../Utilities/FolderUtilities.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 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
|
|
{
|
|
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(!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;
|
|
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, void* parameter)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
};
|