2d0468eff5
Added option in HD pack builder to save screen information when tiles are first shown. User can look up which screen the tiles are added in the tileIndex.csv and open the screen_XXX.csv and screen_XXX.png to see the actual usage of the tiles. Added playback option and volume option to <bgm> tag. Use 1 for looping playback, 0 for single playback, -1 for no change. Use 0 to 128 for volume control and -1 for no volume change.
401 lines
8 KiB
C++
401 lines
8 KiB
C++
#pragma once
|
|
#include "stdafx.h"
|
|
#include <unordered_set>
|
|
#include "PPU.h"
|
|
#include "../Utilities/HexUtilities.h"
|
|
|
|
struct HdTileKey
|
|
{
|
|
static constexpr int32_t NoTile = -1;
|
|
|
|
uint32_t PaletteColors;
|
|
uint8_t TileData[16];
|
|
int32_t TileIndex;
|
|
bool IsChrRamTile = 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 {
|
|
return TileIndex == other.TileIndex && PaletteColors == other.PaletteColors;
|
|
}
|
|
}
|
|
|
|
uint32_t CalculateHash(const uint8_t* key, size_t len) const
|
|
{
|
|
uint32_t result = 0;
|
|
for(size_t i = 0; i < len; i += 4) {
|
|
uint32_t chunk;
|
|
memcpy(&chunk, key, sizeof(uint32_t));
|
|
|
|
result += chunk;
|
|
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 PpuBackgroundColor;
|
|
};
|
|
|
|
struct HdPpuPixelInfo
|
|
{
|
|
HdPpuTileInfo Tile;
|
|
vector<HdPpuTileInfo> Sprite;
|
|
int SpriteCount;
|
|
|
|
uint16_t TmpVideoRamAddr;
|
|
uint8_t XScroll;
|
|
uint8_t EmphasisBits;
|
|
bool Grayscale;
|
|
|
|
HdPpuPixelInfo()
|
|
{
|
|
for(int i = 0; i < 4; i++) {
|
|
Sprite.push_back(HdPpuTileInfo());
|
|
}
|
|
}
|
|
};
|
|
|
|
struct HdScreenInfo
|
|
{
|
|
HdPpuPixelInfo* ScreenTiles;
|
|
std::unordered_map<uint32_t, uint8_t> WatchedAddressValues;
|
|
uint32_t FrameNumber;
|
|
|
|
HdScreenInfo(const HdScreenInfo& that) = delete;
|
|
|
|
HdScreenInfo(bool isChrRamGame)
|
|
{
|
|
ScreenTiles = new HdPpuPixelInfo[PPU::PixelCount];
|
|
|
|
for(int i = 0; i < PPU::PixelCount; i++) {
|
|
ScreenTiles[i].Tile.BackgroundPriority = false;
|
|
ScreenTiles[i].Tile.IsChrRamTile = isChrRamGame;
|
|
ScreenTiles[i].Tile.HorizontalMirroring = false;
|
|
ScreenTiles[i].Tile.VerticalMirroring = false;
|
|
|
|
for(int j = 0; j < 4; j++) {
|
|
ScreenTiles[i].Sprite[j].IsChrRamTile = isChrRamGame;
|
|
}
|
|
}
|
|
}
|
|
|
|
~HdScreenInfo()
|
|
{
|
|
delete[] ScreenTiles;
|
|
}
|
|
};
|
|
|
|
struct HdPackCondition
|
|
{
|
|
string Name;
|
|
|
|
virtual string GetConditionName() = 0;
|
|
virtual bool IsExcludedFromFile() { return Name.size() > 0 && Name[0] == '!'; }
|
|
virtual string ToString() = 0;
|
|
|
|
virtual ~HdPackCondition() { }
|
|
|
|
void ClearCache()
|
|
{
|
|
_resultCache = -1;
|
|
}
|
|
|
|
bool CheckCondition(HdScreenInfo *screenInfo, int x, int y, HdPpuTileInfo* tile)
|
|
{
|
|
if(_resultCache == -1) {
|
|
bool result = InternalCheckCondition(screenInfo, x, y, tile);
|
|
if(Name[0] == '!') {
|
|
result = !result;
|
|
}
|
|
|
|
if(_useCache) {
|
|
_resultCache = result ? 1 : 0;
|
|
}
|
|
return result;
|
|
} else {
|
|
return (bool)_resultCache;
|
|
}
|
|
}
|
|
|
|
protected:
|
|
int8_t _resultCache = -1;
|
|
bool _useCache = false;
|
|
|
|
virtual bool InternalCheckCondition(HdScreenInfo *screenInfo, int x, int y, HdPpuTileInfo* tile) = 0;
|
|
};
|
|
|
|
struct HdPackTileInfo : public HdTileKey
|
|
{
|
|
uint32_t X;
|
|
uint32_t Y;
|
|
uint32_t BitmapIndex;
|
|
int Brightness;
|
|
bool DefaultTile;
|
|
bool Blank;
|
|
bool HasTransparentPixels;
|
|
bool TransparencyRequired;
|
|
bool IsFullyTransparent;
|
|
vector<uint32_t> HdTileData;
|
|
uint32_t ChrBankId;
|
|
|
|
vector<HdPackCondition*> Conditions;
|
|
bool ForceDisableCache;
|
|
|
|
bool MatchesCondition(HdScreenInfo *hdScreenInfo, int x, int y, HdPpuTileInfo* tile)
|
|
{
|
|
for(HdPackCondition* condition : Conditions) {
|
|
if(!condition->CheckCondition(hdScreenInfo, x, y, tile)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
vector<uint32_t> ToRgb(uint32_t* palette)
|
|
{
|
|
vector<uint32_t> rgbBuffer;
|
|
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() || TransparencyRequired) {
|
|
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 UpdateFlags()
|
|
{
|
|
Blank = true;
|
|
HasTransparentPixels = false;
|
|
IsFullyTransparent = true;
|
|
for(size_t i = 0; i < HdTileData.size(); i++) {
|
|
if(HdTileData[i] != HdTileData[0]) {
|
|
Blank = false;
|
|
}
|
|
if((HdTileData[i] & 0xFF000000) != 0xFF000000) {
|
|
HasTransparentPixels = true;
|
|
}
|
|
if(HdTileData[i] & 0xFF000000) {
|
|
IsFullyTransparent = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
string ToString(int pngIndex)
|
|
{
|
|
stringstream out;
|
|
|
|
if(Conditions.size() > 0) {
|
|
out << "[";
|
|
for(size_t 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 << "," <<
|
|
HexUtilities::ToHex(TileIndex) << "," <<
|
|
HexUtilities::ToHex(PaletteColors, true) << "," <<
|
|
X << "," <<
|
|
Y << "," <<
|
|
(double)Brightness / 255 << "," <<
|
|
(DefaultTile ? "Y" : "N");
|
|
}
|
|
|
|
return out.str();
|
|
}
|
|
};
|
|
|
|
struct HdPackBitmapInfo
|
|
{
|
|
vector<uint32_t> PixelData;
|
|
uint32_t Width;
|
|
uint32_t Height;
|
|
};
|
|
|
|
struct HdBackgroundFileData
|
|
{
|
|
string PngName;
|
|
uint32_t Width;
|
|
uint32_t Height;
|
|
|
|
vector<uint32_t> PixelData;
|
|
};
|
|
|
|
struct HdBackgroundInfo
|
|
{
|
|
HdBackgroundFileData* Data;
|
|
int Brightness;
|
|
vector<HdPackCondition*> Conditions;
|
|
float HorizontalScrollRatio;
|
|
float VerticalScrollRatio;
|
|
uint8_t Priority;
|
|
|
|
uint32_t Left;
|
|
uint32_t Top;
|
|
|
|
uint32_t* data()
|
|
{
|
|
return Data->PixelData.data();
|
|
}
|
|
|
|
string ToString()
|
|
{
|
|
stringstream out;
|
|
|
|
if(Conditions.size() > 0) {
|
|
out << "[";
|
|
for(size_t i = 0; i < Conditions.size(); i++) {
|
|
if(i > 0) {
|
|
out << "&";
|
|
}
|
|
out << Conditions[i]->Name;
|
|
}
|
|
out << "]";
|
|
}
|
|
|
|
out << "<background>";
|
|
out << Data->PngName << ",";
|
|
out << (Brightness / 255.0);
|
|
|
|
return out.str();
|
|
}
|
|
};
|
|
|
|
|
|
struct HdBGMInfo {
|
|
string File;
|
|
uint32_t LoopPoint;
|
|
int16_t Volume;
|
|
int16_t PlaybackOptions;
|
|
};
|
|
|
|
struct HdPackData
|
|
{
|
|
vector<HdBackgroundInfo> Backgrounds;
|
|
vector<unique_ptr<HdBackgroundFileData>> BackgroundFileData;
|
|
vector<unique_ptr<HdPackTileInfo>> Tiles;
|
|
vector<unique_ptr<HdPackCondition>> Conditions;
|
|
std::unordered_set<uint32_t> WatchedMemoryAddresses;
|
|
std::unordered_map<HdTileKey, vector<HdPackTileInfo*>> TileByKey;
|
|
std::unordered_map<string, string> PatchesByHash;
|
|
std::unordered_map<int, HdBGMInfo> BgmFilesById;
|
|
std::unordered_map<int, string> SfxFilesById;
|
|
vector<uint32_t> Palette;
|
|
|
|
bool HasOverscanConfig = false;
|
|
OverscanDimensions Overscan;
|
|
|
|
uint32_t Scale = 1;
|
|
uint32_t Version = 0;
|
|
uint32_t OptionFlags = 0;
|
|
|
|
HdPackData() { }
|
|
~HdPackData() { }
|
|
|
|
HdPackData(const HdPackData&) = delete;
|
|
HdPackData& operator=(const HdPackData&) = delete;
|
|
};
|
|
|
|
enum class HdPackOptions
|
|
{
|
|
None = 0,
|
|
NoSpriteLimit = 1,
|
|
AlternateRegisterRange = 2,
|
|
DisableCache = 8,
|
|
DontRenderOriginalTiles = 16
|
|
};
|
|
|
|
struct HdScreenTileInfo : public HdTileKey
|
|
{
|
|
int16_t ScreenX;
|
|
int16_t ScreenY;
|
|
bool HorizontalMirroring;
|
|
bool VerticalMirroring;
|
|
bool BackgroundPriority;
|
|
bool IsNew;
|
|
};
|