mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
Debugger: Added tile editor
This commit is contained in:
parent
1547e70b2f
commit
35a54505b4
36 changed files with 903 additions and 141 deletions
|
@ -213,11 +213,12 @@ enum class TileLayout
|
|||
|
||||
enum class TileBackground
|
||||
{
|
||||
Default = 0,
|
||||
PaletteColor = 1,
|
||||
Black = 2,
|
||||
White = 3,
|
||||
Magenta = 4
|
||||
Default,
|
||||
Transparent,
|
||||
PaletteColor,
|
||||
Black,
|
||||
White,
|
||||
Magenta,
|
||||
};
|
||||
|
||||
enum class TileFilter
|
||||
|
@ -246,6 +247,11 @@ struct GetSpritePreviewOptions
|
|||
int32_t SelectedSprite;
|
||||
};
|
||||
|
||||
struct GetPaletteInfoOptions
|
||||
{
|
||||
TileFormat Format;
|
||||
};
|
||||
|
||||
enum class StackFrameFlags
|
||||
{
|
||||
None = 0,
|
||||
|
|
|
@ -164,6 +164,7 @@ void PpuTools::GetTileView(GetTileViewOptions options, uint8_t *source, uint32_t
|
|||
case TileBackground::Black: bgColor = 0xFF000000; break;
|
||||
case TileBackground::White: bgColor = 0xFFFFFFFF; break;
|
||||
case TileBackground::Magenta: bgColor = 0xFFFF00FF; break;
|
||||
case TileBackground::Transparent: bgColor = 0; break;
|
||||
}
|
||||
|
||||
uint32_t outputSize = tileCount * tileWidth * tileHeight;
|
||||
|
@ -244,6 +245,96 @@ void PpuTools::RemoveViewer(uint32_t viewerId)
|
|||
_updateTimings.erase(viewerId);
|
||||
}
|
||||
|
||||
void PpuTools::SetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t color)
|
||||
{
|
||||
ConsoleMemoryInfo memInfo = _emu->GetMemory(tileAddress.Type);
|
||||
if(!memInfo.Memory || memInfo.Size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int rowOffset;
|
||||
switch(format) {
|
||||
default: rowOffset = 2; break;
|
||||
case TileFormat::Mode7:
|
||||
case TileFormat::Mode7DirectColor:
|
||||
case TileFormat::Mode7ExtBg:
|
||||
rowOffset = 16;
|
||||
break;
|
||||
|
||||
case TileFormat::NesBpp2: rowOffset = 1; break;
|
||||
case TileFormat::PceSpriteBpp4: rowOffset = 2; break;
|
||||
}
|
||||
|
||||
uint8_t* ram = (uint8_t*)memInfo.Memory;
|
||||
int rowStart = tileAddress.Address + (y * rowOffset);
|
||||
int ramMask = (memInfo.Size - 1);
|
||||
|
||||
uint8_t shift = (7 - x);
|
||||
|
||||
auto setBit = [&](uint32_t addr, uint8_t bitNumber, uint8_t bitValue) {
|
||||
ram[addr & ramMask] &= ~(1 << bitNumber);
|
||||
ram[addr & ramMask] |= (bitValue & 0x01) << bitNumber;
|
||||
};
|
||||
|
||||
switch(format) {
|
||||
case TileFormat::PceSpriteBpp4:
|
||||
{
|
||||
shift = 15 - x;
|
||||
if(shift >= 8) {
|
||||
shift -= 8;
|
||||
rowStart++;
|
||||
}
|
||||
|
||||
setBit(rowStart, shift, color & 0x01);
|
||||
setBit(rowStart + 32, shift, (color & 0x02) >> 1);
|
||||
setBit(rowStart + 64, shift, (color & 0x04) >> 2);
|
||||
setBit(rowStart + 96, shift, (color & 0x08) >> 3);
|
||||
break;
|
||||
}
|
||||
|
||||
case TileFormat::Bpp2:
|
||||
setBit(rowStart, shift, color & 0x01);
|
||||
setBit(rowStart + 1, shift, (color & 0x02) >> 1);
|
||||
break;
|
||||
|
||||
case TileFormat::NesBpp2:
|
||||
setBit(rowStart, shift, color & 0x01);
|
||||
setBit(rowStart + 8, shift, (color & 0x02) >> 1);
|
||||
break;
|
||||
|
||||
case TileFormat::Bpp4:
|
||||
setBit(rowStart, shift, color & 0x01);
|
||||
setBit(rowStart + 1, shift, (color & 0x02) >> 1);
|
||||
setBit(rowStart + 16, shift, (color & 0x04) >> 2);
|
||||
setBit(rowStart + 17, shift, (color & 0x08) >> 3);
|
||||
break;
|
||||
|
||||
case TileFormat::Bpp8:
|
||||
case TileFormat::DirectColor:
|
||||
setBit(rowStart, shift, color & 0x01);
|
||||
setBit(rowStart + 1, shift, (color & 0x02) >> 1);
|
||||
setBit(rowStart + 16, shift, (color & 0x04) >> 2);
|
||||
setBit(rowStart + 17, shift, (color & 0x08) >> 3);
|
||||
setBit(rowStart + 32, shift, (color & 0x10) >> 4);
|
||||
setBit(rowStart + 33, shift, (color & 0x20) >> 5);
|
||||
setBit(rowStart + 48, shift, (color & 0x40) >> 6);
|
||||
setBit(rowStart + 49, shift, (color & 0x80) >> 7);
|
||||
break;
|
||||
|
||||
case TileFormat::Mode7:
|
||||
case TileFormat::Mode7DirectColor:
|
||||
ram[(rowStart + x * 2 + 1) & ramMask] = color;
|
||||
break;
|
||||
|
||||
case TileFormat::Mode7ExtBg:
|
||||
ram[(rowStart + x * 2 + 1) & ramMask] = color;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("unsupported format");
|
||||
}
|
||||
}
|
||||
|
||||
void PpuTools::UpdateViewers(uint16_t scanline, uint16_t cycle)
|
||||
{
|
||||
for(auto updateTiming : _updateTimings) {
|
||||
|
|
|
@ -57,6 +57,8 @@ struct DebugSpriteInfo
|
|||
bool UseExtendedVram;
|
||||
NullableBoolean UseSecondTable;
|
||||
|
||||
uint32_t TileCount;
|
||||
uint32_t TileAddresses[8 * 8];
|
||||
uint32_t SpritePreview[64 * 64];
|
||||
|
||||
public:
|
||||
|
@ -65,6 +67,7 @@ public:
|
|||
TileIndex = -1;
|
||||
TileAddress = -1;
|
||||
PaletteAddress = -1;
|
||||
Format = {};
|
||||
SpriteIndex = -1;
|
||||
X = -1;
|
||||
Y = -1;
|
||||
|
@ -78,7 +81,9 @@ public:
|
|||
HorizontalMirror = false;
|
||||
VerticalMirror = false;
|
||||
Visible = false;
|
||||
UseExtendedVram = false;
|
||||
UseSecondTable = NullableBoolean::Undefined;
|
||||
TileCount = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -180,7 +185,7 @@ protected:
|
|||
public:
|
||||
PpuTools(Debugger* debugger, Emulator *emu);
|
||||
|
||||
virtual DebugPaletteInfo GetPaletteInfo() = 0;
|
||||
virtual DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) = 0;
|
||||
|
||||
void GetTileView(GetTileViewOptions options, uint8_t *source, uint32_t srcSize, const uint32_t* palette, uint32_t *outBuffer);
|
||||
|
||||
|
@ -192,6 +197,8 @@ public:
|
|||
virtual void GetSpritePreview(GetSpritePreviewOptions options, BaseState& state, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, uint32_t* outBuffer) = 0;
|
||||
virtual void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[]) = 0;
|
||||
|
||||
void SetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t color);
|
||||
|
||||
virtual void SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle);
|
||||
void RemoveViewer(uint32_t viewerId);
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ FrameInfo GbPpuTools::GetTilemapSize(GetTilemapOptions options, BaseState& state
|
|||
|
||||
DebugTilemapTileInfo GbPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState)
|
||||
{
|
||||
DebugTilemapTileInfo result;
|
||||
DebugTilemapTileInfo result = {};
|
||||
|
||||
FrameInfo size = GetTilemapSize(options, baseState);
|
||||
|
||||
|
@ -192,11 +192,18 @@ void GbPpuTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint16_t i, GetSpritePre
|
|||
|
||||
uint8_t tileIndex = (uint8_t)sprite.TileIndex;
|
||||
uint16_t tileBank = useSecondTable ? 0x2000 : 0x0000;
|
||||
uint16_t tileStart;
|
||||
if(state.LargeSprites) {
|
||||
tileIndex &= 0xFE;
|
||||
tileStart = (tileIndex & 0xFE) * 16;
|
||||
sprite.TileAddresses[0] = tileStart;
|
||||
sprite.TileAddresses[1] = tileStart + 16;
|
||||
sprite.TileCount = 2;
|
||||
} else {
|
||||
tileStart = tileIndex * 16;
|
||||
sprite.TileAddresses[0] = tileStart;
|
||||
sprite.TileCount = 1;
|
||||
}
|
||||
|
||||
uint16_t tileStart = tileIndex * 16;
|
||||
tileStart |= tileBank;
|
||||
|
||||
sprite.TileAddress = tileStart;
|
||||
|
@ -231,7 +238,7 @@ void GbPpuTools::GetSpriteList(GetSpritePreviewOptions options, BaseState& baseS
|
|||
}
|
||||
}
|
||||
|
||||
DebugPaletteInfo GbPpuTools::GetPaletteInfo()
|
||||
DebugPaletteInfo GbPpuTools::GetPaletteInfo(GetPaletteInfoOptions options)
|
||||
{
|
||||
DebugPaletteInfo info = {};
|
||||
GbPpuState state;
|
||||
|
|
|
@ -21,5 +21,5 @@ public:
|
|||
DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) override;
|
||||
void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[]) override;
|
||||
|
||||
DebugPaletteInfo GetPaletteInfo() override;
|
||||
DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) override;
|
||||
};
|
|
@ -227,7 +227,7 @@ FrameInfo NesPpuTools::GetTilemapSize(GetTilemapOptions options, BaseState& stat
|
|||
|
||||
DebugTilemapTileInfo NesPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState)
|
||||
{
|
||||
DebugTilemapTileInfo result;
|
||||
DebugTilemapTileInfo result = {};
|
||||
|
||||
FrameInfo size = GetTilemapSize(options, baseState);
|
||||
if(x >= size.Width || y >= size.Height) {
|
||||
|
@ -308,8 +308,12 @@ void NesPpuTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint32_t i, GetSpritePr
|
|||
} else {
|
||||
tileStart = 0x0000 | (sprite.TileIndex * 16);
|
||||
}
|
||||
sprite.TileAddresses[0] = tileStart;
|
||||
sprite.TileAddresses[1] = tileStart + 16;
|
||||
sprite.TileCount = 2;
|
||||
} else {
|
||||
tileStart = (sprite.TileIndex * 16) | sprAddr;
|
||||
sprite.TileCount = 1;
|
||||
}
|
||||
sprite.TileAddress = tileStart;
|
||||
|
||||
|
@ -354,7 +358,7 @@ DebugSpritePreviewInfo NesPpuTools::GetSpritePreviewInfo(GetSpritePreviewOptions
|
|||
return info;
|
||||
}
|
||||
|
||||
DebugPaletteInfo NesPpuTools::GetPaletteInfo()
|
||||
DebugPaletteInfo NesPpuTools::GetPaletteInfo(GetPaletteInfoOptions options)
|
||||
{
|
||||
DebugPaletteInfo info = {};
|
||||
info.RawFormat = RawPaletteFormat::Indexed;
|
||||
|
|
|
@ -25,5 +25,5 @@ public:
|
|||
void GetSpritePreview(GetSpritePreviewOptions options, BaseState& state, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, uint32_t *outBuffer) override;
|
||||
void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[]) override;
|
||||
|
||||
DebugPaletteInfo GetPaletteInfo() override;
|
||||
DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) override;
|
||||
};
|
|
@ -31,7 +31,7 @@ FrameInfo PceVdcTools::GetTilemapSize(GetTilemapOptions options, BaseState& base
|
|||
|
||||
DebugTilemapTileInfo PceVdcTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState)
|
||||
{
|
||||
DebugTilemapTileInfo result;
|
||||
DebugTilemapTileInfo result = {};
|
||||
|
||||
FrameInfo size = GetTilemapSize(options, baseState);
|
||||
if(x >= size.Width || y >= size.Height) {
|
||||
|
@ -196,8 +196,15 @@ void PceVdcTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint16_t spriteIndex, G
|
|||
} else if(height == 64) {
|
||||
tileIndex &= ~0x06;
|
||||
}
|
||||
|
||||
|
||||
sprite.TileAddress = tileIndex * 64 * 2;
|
||||
sprite.TileCount = 0;
|
||||
for(int i = 0, rowCount = height / 16; i < rowCount; i++) {
|
||||
for(int j = 0, columnCount = width / 16; j < columnCount; j++) {
|
||||
sprite.TileAddresses[sprite.TileCount] = sprite.TileAddress + (i * columnCount + j) * 128;
|
||||
sprite.TileCount++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t yOffset;
|
||||
int rowOffset;
|
||||
|
@ -267,7 +274,7 @@ DebugSpritePreviewInfo PceVdcTools::GetSpritePreviewInfo(GetSpritePreviewOptions
|
|||
return info;
|
||||
}
|
||||
|
||||
DebugPaletteInfo PceVdcTools::GetPaletteInfo()
|
||||
DebugPaletteInfo PceVdcTools::GetPaletteInfo(GetPaletteInfoOptions options)
|
||||
{
|
||||
DebugPaletteInfo info = {};
|
||||
info.RawFormat = RawPaletteFormat::Rgb333;
|
||||
|
|
|
@ -27,5 +27,5 @@ public:
|
|||
void GetSpritePreview(GetSpritePreviewOptions options, BaseState& state, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, uint32_t *outBuffer) override;
|
||||
void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[]) override;
|
||||
|
||||
DebugPaletteInfo GetPaletteInfo() override;
|
||||
DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) override;
|
||||
};
|
|
@ -253,6 +253,17 @@ void SnesPpuTools::GetSpriteInfo(DebugSpriteInfo& sprite, uint16_t spriteIndex,
|
|||
|
||||
int tileRow = (sprite.TileIndex & 0xF0) >> 4;
|
||||
int tileColumn = sprite.TileIndex & 0x0F;
|
||||
|
||||
sprite.TileCount = 0;
|
||||
for(int i = 0, rowCount = height / 8; i < rowCount; i++) {
|
||||
int row = (i + tileRow) & 0x0F;
|
||||
for(int j = 0, columnCount = width / 8; j < columnCount; j++) {
|
||||
int col = (j + tileColumn) & 0x0F;
|
||||
sprite.TileAddresses[sprite.TileCount] = ((state.OamBaseAddress + (row * 16 + col) * 16 + (useSecondTable ? state.OamAddressOffset : 0)) & 0x7FFF) << 1;
|
||||
sprite.TileCount++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t yOffset;
|
||||
int rowOffset;
|
||||
|
||||
|
@ -341,7 +352,7 @@ FrameInfo SnesPpuTools::GetTilemapSize(GetTilemapOptions options, BaseState& bas
|
|||
|
||||
DebugTilemapTileInfo SnesPpuTools::GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState)
|
||||
{
|
||||
DebugTilemapTileInfo result;
|
||||
DebugTilemapTileInfo result = {};
|
||||
|
||||
FrameInfo size = GetTilemapSize(options, baseState);
|
||||
if(x >= size.Width || y >= size.Height) {
|
||||
|
@ -417,7 +428,7 @@ DebugSpritePreviewInfo SnesPpuTools::GetSpritePreviewInfo(GetSpritePreviewOption
|
|||
return info;
|
||||
}
|
||||
|
||||
DebugPaletteInfo SnesPpuTools::GetPaletteInfo()
|
||||
DebugPaletteInfo SnesPpuTools::GetPaletteInfo(GetPaletteInfoOptions options)
|
||||
{
|
||||
DebugPaletteInfo info = {};
|
||||
info.RawFormat = RawPaletteFormat::Rgb555;
|
||||
|
@ -426,11 +437,23 @@ DebugPaletteInfo SnesPpuTools::GetPaletteInfo()
|
|||
info.SpriteColorCount = 16 * 8;
|
||||
info.ColorCount = info.BgColorCount + info.SpriteColorCount;
|
||||
|
||||
uint8_t* cgram= _debugger->GetMemoryDumper()->GetMemoryBuffer(MemoryType::SnesCgRam);
|
||||
for(int i = 0; i < 256; i++) {
|
||||
info.RawPalette[i] = cgram[i*2] | (cgram[i*2+1] << 8);
|
||||
info.RgbPalette[i] = SnesDefaultVideoFilter::ToArgb(info.RawPalette[i]);
|
||||
}
|
||||
switch(options.Format) {
|
||||
case TileFormat::DirectColor:
|
||||
case TileFormat::Mode7DirectColor:
|
||||
for(int i = 0; i < 256; i++) {
|
||||
info.RawPalette[i] = ((i & 0x07) << 2) | ((i & 0x38) << 4) | ((i & 0xC0) << 7);
|
||||
info.RgbPalette[i] = SnesDefaultVideoFilter::ToArgb(info.RawPalette[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
uint8_t mask = options.Format == TileFormat::Mode7ExtBg ? 0x7F : 0xFF;
|
||||
uint8_t* cgram = _debugger->GetMemoryDumper()->GetMemoryBuffer(MemoryType::SnesCgRam);
|
||||
for(int i = 0; i < 256; i++) {
|
||||
info.RawPalette[i] = cgram[(i & mask) * 2] | (cgram[(i & mask) * 2 + 1] << 8);
|
||||
info.RgbPalette[i] = SnesDefaultVideoFilter::ToArgb(info.RawPalette[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
|
|
@ -22,5 +22,5 @@ public:
|
|||
void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[]) override;
|
||||
DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state) override;
|
||||
|
||||
DebugPaletteInfo GetPaletteInfo() override;
|
||||
DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) override;
|
||||
};
|
|
@ -152,7 +152,8 @@ extern "C"
|
|||
DllExport void __stdcall GetSpritePreview(CpuType cpuType, GetSpritePreviewOptions options, BaseState& state, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, uint32_t* buffer) { WithToolVoid(GetPpuTools(cpuType), GetSpritePreview(options, state, vram, oamRam, palette, buffer)); }
|
||||
DllExport void __stdcall GetSpriteList(CpuType cpuType, GetSpritePreviewOptions options, BaseState& state, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo sprites[]) { WithToolVoid(GetPpuTools(cpuType), GetSpriteList(options, state, vram, oamRam, palette, sprites)); }
|
||||
|
||||
DllExport DebugPaletteInfo __stdcall GetPaletteInfo(CpuType cpuType) { return WithTool(DebugPaletteInfo, GetPpuTools(cpuType), GetPaletteInfo()); }
|
||||
DllExport DebugPaletteInfo __stdcall GetPaletteInfo(CpuType cpuType, GetPaletteInfoOptions options) { return WithTool(DebugPaletteInfo, GetPpuTools(cpuType), GetPaletteInfo(options)); }
|
||||
DllExport void __stdcall SetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t color) { WithToolVoid(GetPpuTools(DebugUtilities::ToCpuType(tileAddress.Type)), SetTilePixel(tileAddress, format, x, y, color)); }
|
||||
|
||||
DllExport void __stdcall SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle, CpuType cpuType) { WithToolVoid(GetPpuTools(cpuType), SetViewerUpdateTiming(viewerId, scanline, cycle)); }
|
||||
|
||||
|
|
|
@ -9,6 +9,14 @@
|
|||
<Application.Resources>
|
||||
<l:EnumConverter x:Key="Enum" />
|
||||
<u:FontNameConverter x:Key="FontNameConverter" />
|
||||
<VisualBrush x:Key="ViewerBgBrush" TileMode="Tile" Stretch="None" AlignmentX="Left" AlignmentY="Top" SourceRect="0,0,5,5" DestinationRect="0,0,5,5">
|
||||
<VisualBrush.Visual>
|
||||
<Panel Background="#202020">
|
||||
<Line StartPoint="0, 0" EndPoint="5, 5" Stroke="Gray" StrokeThickness="1" Opacity="0.5" />
|
||||
<Line StartPoint="5, 0" EndPoint="0, 5" Stroke="LightGray" StrokeThickness="1" Opacity="0.5" />
|
||||
</Panel>
|
||||
</VisualBrush.Visual>
|
||||
</VisualBrush>
|
||||
</Application.Resources>
|
||||
|
||||
<Application.DataTemplates>
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace Mesen.Config
|
|||
public TilemapViewerConfig TilemapViewer { get; set; } = new TilemapViewerConfig();
|
||||
public TileViewerConfig TileViewer { get; set; } = new TileViewerConfig();
|
||||
public PaletteViewerConfig PaletteViewer { get; set; } = new PaletteViewerConfig();
|
||||
public TileEditorConfig TileEditor { get; set; } = new TileEditorConfig();
|
||||
public RegisterViewerConfig RegisterViewer { get; set; } = new RegisterViewerConfig();
|
||||
public SpriteViewerConfig SpriteViewer { get; set; } = new SpriteViewerConfig();
|
||||
public IntegrationConfig Integration { get; set; } = new IntegrationConfig();
|
||||
|
|
|
@ -202,14 +202,17 @@ namespace Mesen.Config
|
|||
Add(new() { Shortcut = DebuggerShortcut.ResetWorkspace, KeyBinding = new() });
|
||||
|
||||
Add(new() { Shortcut = DebuggerShortcut.TilemapViewer_ViewInMemoryViewer, KeyBinding = new(Key.F1) });
|
||||
Add(new() { Shortcut = DebuggerShortcut.TilemapViewer_ViewInTileViewer, KeyBinding = new(Key.F2) });
|
||||
Add(new() { Shortcut = DebuggerShortcut.TilemapViewer_EditTilemapBreakpoint, KeyBinding = new(Key.F3) });
|
||||
Add(new() { Shortcut = DebuggerShortcut.TilemapViewer_EditAttributeBreakpoint, KeyBinding = new(Key.F4) });
|
||||
Add(new() { Shortcut = DebuggerShortcut.TilemapViewer_EditTile, KeyBinding = new(Key.F2) });
|
||||
Add(new() { Shortcut = DebuggerShortcut.TilemapViewer_ViewInTileViewer, KeyBinding = new(Key.F3) });
|
||||
Add(new() { Shortcut = DebuggerShortcut.TilemapViewer_EditTilemapBreakpoint, KeyBinding = new(Key.F4) });
|
||||
Add(new() { Shortcut = DebuggerShortcut.TilemapViewer_EditAttributeBreakpoint, KeyBinding = new() });
|
||||
|
||||
Add(new() { Shortcut = DebuggerShortcut.SpriteViewer_ViewInMemoryViewer, KeyBinding = new(Key.F1) });
|
||||
Add(new() { Shortcut = DebuggerShortcut.SpriteViewer_ViewInTileViewer, KeyBinding = new(Key.F2) });
|
||||
|
||||
Add(new() { Shortcut = DebuggerShortcut.SpriteViewer_ViewInTileViewer, KeyBinding = new(Key.F3) });
|
||||
Add(new() { Shortcut = DebuggerShortcut.SpriteViewer_EditSprite, KeyBinding = new(Key.F2) });
|
||||
|
||||
Add(new() { Shortcut = DebuggerShortcut.TileViewer_ViewInMemoryViewer, KeyBinding = new(Key.F1) });
|
||||
Add(new() { Shortcut = DebuggerShortcut.TileViewer_EditTile, KeyBinding = new(Key.F2) });
|
||||
|
||||
//Memory Tools
|
||||
//Add(new() { Shortcut = eDebuggerShortcut.MemoryViewer_Freeze, KeyBinding = new(KeyModifiers.Control, Key.Q) });
|
||||
|
@ -358,9 +361,12 @@ namespace Mesen.Config
|
|||
TilemapViewer_EditAttributeBreakpoint,
|
||||
TilemapViewer_ViewInTileViewer,
|
||||
TilemapViewer_ViewInMemoryViewer,
|
||||
TilemapViewer_EditTile,
|
||||
SpriteViewer_ViewInMemoryViewer,
|
||||
SpriteViewer_ViewInTileViewer,
|
||||
SpriteViewer_EditSprite,
|
||||
TileViewer_ViewInMemoryViewer,
|
||||
TileViewer_EditTile,
|
||||
}
|
||||
|
||||
public class DebuggerShortcutInfo : ViewModelBase
|
||||
|
|
11
NewUI/Config/Debugger/TileEditorConfig.cs
Normal file
11
NewUI/Config/Debugger/TileEditorConfig.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using Mesen.Interop;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace Mesen.Config
|
||||
{
|
||||
public class TileEditorConfig : BaseWindowConfig<TileEditorConfig>
|
||||
{
|
||||
[Reactive] public int ImageScale { get; set; } = 8;
|
||||
[Reactive] public TileBackground Background { get; set; } = TileBackground.Transparent;
|
||||
}
|
||||
}
|
|
@ -18,7 +18,6 @@ namespace Mesen.Config
|
|||
[Reactive] public int RowCount { get; set; } = 64;
|
||||
[Reactive] public int ColumnCount { get; set; } = 32;
|
||||
[Reactive] public int StartAddress { get; set; } = 0;
|
||||
[Reactive] public int SelectedPalette { get; set; } = 0;
|
||||
[Reactive] public bool UseGrayscalePalette { get; set; } = false;
|
||||
|
||||
[Reactive] public RefreshTimingConfig RefreshTiming { get; set; } = new();
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
MinWidth="{Binding FirstColumnWidth, ElementName=root}"
|
||||
IsVisible="{Binding Name.Length}"
|
||||
Margin="0 3 10 2"
|
||||
/>
|
||||
/>
|
||||
<ContentControl Content="{Binding Value}">
|
||||
<ContentControl.DataTemplates>
|
||||
<DataTemplate DataType="x:String">
|
||||
|
|
|
@ -324,6 +324,9 @@ namespace Mesen.Debugger.Controls
|
|||
|
||||
RaiseEvent(new ColorClickEventArgs() { ColorIndex = paletteIndex, Color = Color.FromUInt32(PaletteColors[paletteIndex]) });
|
||||
|
||||
if(SelectionMode == PaletteSelectionMode.None) {
|
||||
return;
|
||||
}
|
||||
if(SelectionMode == PaletteSelectionMode.SingleColor) {
|
||||
paletteIndex /= 1;
|
||||
} else if(SelectionMode == PaletteSelectionMode.FourColors) {
|
||||
|
|
|
@ -214,12 +214,12 @@ namespace Mesen.Debugger.Controls
|
|||
|
||||
public void ZoomIn()
|
||||
{
|
||||
Zoom = Math.Min(20, Math.Max(1, Zoom + 1));
|
||||
Zoom = Math.Min(40, Math.Max(1, Zoom + 1));
|
||||
}
|
||||
|
||||
public void ZoomOut()
|
||||
{
|
||||
Zoom = Math.Min(20, Math.Max(1, Zoom - 1));
|
||||
Zoom = Math.Min(40, Math.Max(1, Zoom - 1));
|
||||
}
|
||||
|
||||
public async void ExportToPng()
|
||||
|
@ -247,12 +247,24 @@ namespace Mesen.Debugger.Controls
|
|||
protected override void OnPointerMoved(PointerEventArgs e)
|
||||
{
|
||||
base.OnPointerMoved(e);
|
||||
PixelPoint? p = GetGridPointFromMousePoint(e.GetCurrentPoint(this).Position);
|
||||
if(p == null) {
|
||||
e.Handled = true;
|
||||
MouseOverRect = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if(ShowMousePosition) {
|
||||
PixelPoint? p = GetGridPointFromMousePoint(e.GetCurrentPoint(this).Position);
|
||||
if(p != null) {
|
||||
MouseOverRect = GetTileRect(p.Value);
|
||||
} else {
|
||||
MouseOverRect = null;
|
||||
MouseOverRect = GetTileRect(p.Value);
|
||||
}
|
||||
|
||||
PointerPointProperties props = e.GetCurrentPoint(this).Properties;
|
||||
if(props.IsLeftButtonPressed || props.IsRightButtonPressed) {
|
||||
PositionClickedEventArgs args = new(p.Value, props, PositionClickedEvent);
|
||||
RaiseEvent(args);
|
||||
|
||||
if(!args.Handled && AllowSelection) {
|
||||
SelectionRect = GetTileRect(p.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -271,7 +283,7 @@ namespace Mesen.Debugger.Controls
|
|||
return;
|
||||
}
|
||||
|
||||
PositionClickedEventArgs args = new() { RoutedEvent = PositionClickedEvent, Position = p.Value };
|
||||
PositionClickedEventArgs args = new(p.Value, e.GetCurrentPoint(this).Properties, PositionClickedEvent);
|
||||
RaiseEvent(args);
|
||||
|
||||
if(!args.Handled && AllowSelection) {
|
||||
|
@ -441,6 +453,14 @@ namespace Mesen.Debugger.Controls
|
|||
public class PositionClickedEventArgs : RoutedEventArgs
|
||||
{
|
||||
public PixelPoint Position;
|
||||
public PointerPointProperties Properties;
|
||||
|
||||
public PositionClickedEventArgs(PixelPoint position, PointerPointProperties properties, RoutedEvent evt)
|
||||
{
|
||||
Position = position;
|
||||
Properties = properties;
|
||||
RoutedEvent = evt;
|
||||
}
|
||||
}
|
||||
|
||||
public class GridRowColumn
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
x:Name="root"
|
||||
x:Class="Mesen.Debugger.Controls.ScrollPictureViewer"
|
||||
>
|
||||
<Border BorderBrush="Gray" BorderThickness="1" Background="#202020">
|
||||
<Border BorderBrush="Gray" BorderThickness="1">
|
||||
<ScrollViewer
|
||||
Name="scrollViewer"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
|
@ -17,26 +17,28 @@
|
|||
AllowAutoHide="False"
|
||||
Offset="{CompiledBinding ScrollOffset, ElementName=root, Mode=TwoWay}"
|
||||
>
|
||||
<dc:PictureViewer
|
||||
x:Name="picViewer"
|
||||
PointerPressed="Viewer_PointerPressed"
|
||||
PointerMoved="Viewer_PointerMoved"
|
||||
Source="{CompiledBinding Source, ElementName=root}"
|
||||
Zoom="{CompiledBinding Zoom, ElementName=root}"
|
||||
AllowSelection="{CompiledBinding AllowSelection, ElementName=root}"
|
||||
ShowMousePosition="{CompiledBinding ShowMousePosition, ElementName=root}"
|
||||
GridSizeX="{CompiledBinding GridSizeX, ElementName=root}"
|
||||
GridSizeY="{CompiledBinding GridSizeY, ElementName=root}"
|
||||
ShowGrid="{CompiledBinding ShowGrid, ElementName=root}"
|
||||
AltGridSizeX="{CompiledBinding AltGridSizeX, ElementName=root}"
|
||||
AltGridSizeY="{CompiledBinding AltGridSizeY, ElementName=root}"
|
||||
ShowAltGrid="{CompiledBinding ShowAltGrid, ElementName=root}"
|
||||
SelectionRect="{CompiledBinding SelectionRect, ElementName=root}"
|
||||
OverlayRect="{CompiledBinding OverlayRect, ElementName=root}"
|
||||
MouseOverRect="{CompiledBinding MouseOverRect, ElementName=root}"
|
||||
HighlightRects="{CompiledBinding HighlightRects, ElementName=root}"
|
||||
GridHighlight="{CompiledBinding GridHighlight, ElementName=root}"
|
||||
<Border Background="{StaticResource ViewerBgBrush}" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<dc:PictureViewer
|
||||
x:Name="picViewer"
|
||||
PointerPressed="Viewer_PointerPressed"
|
||||
PointerMoved="Viewer_PointerMoved"
|
||||
Source="{CompiledBinding Source, ElementName=root}"
|
||||
Zoom="{CompiledBinding Zoom, ElementName=root}"
|
||||
AllowSelection="{CompiledBinding AllowSelection, ElementName=root}"
|
||||
ShowMousePosition="{CompiledBinding ShowMousePosition, ElementName=root}"
|
||||
GridSizeX="{CompiledBinding GridSizeX, ElementName=root}"
|
||||
GridSizeY="{CompiledBinding GridSizeY, ElementName=root}"
|
||||
ShowGrid="{CompiledBinding ShowGrid, ElementName=root}"
|
||||
AltGridSizeX="{CompiledBinding AltGridSizeX, ElementName=root}"
|
||||
AltGridSizeY="{CompiledBinding AltGridSizeY, ElementName=root}"
|
||||
ShowAltGrid="{CompiledBinding ShowAltGrid, ElementName=root}"
|
||||
SelectionRect="{CompiledBinding SelectionRect, ElementName=root}"
|
||||
OverlayRect="{CompiledBinding OverlayRect, ElementName=root}"
|
||||
MouseOverRect="{CompiledBinding MouseOverRect, ElementName=root}"
|
||||
HighlightRects="{CompiledBinding HighlightRects, ElementName=root}"
|
||||
GridHighlight="{CompiledBinding GridHighlight, ElementName=root}"
|
||||
/>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</UserControl>
|
|
@ -25,6 +25,7 @@ namespace Mesen.Debugger.Controls
|
|||
public static readonly StyledProperty<int> AltGridSizeYProperty = AvaloniaProperty.Register<ScrollPictureViewer, int>(nameof(AltGridSizeY), 8);
|
||||
public static readonly StyledProperty<bool> ShowAltGridProperty = AvaloniaProperty.Register<ScrollPictureViewer, bool>(nameof(ShowAltGrid), false);
|
||||
public static readonly StyledProperty<bool> AllowSelectionProperty = AvaloniaProperty.Register<ScrollPictureViewer, bool>(nameof(AllowSelection), true);
|
||||
public static readonly StyledProperty<bool> AllowClickDragProperty = AvaloniaProperty.Register<ScrollPictureViewer, bool>(nameof(AllowClickDrag), true);
|
||||
|
||||
public static readonly StyledProperty<GridRowColumn?> GridHighlightProperty = AvaloniaProperty.Register<ScrollPictureViewer, GridRowColumn?>(nameof(GridHighlight), null);
|
||||
|
||||
|
@ -102,6 +103,12 @@ namespace Mesen.Debugger.Controls
|
|||
set { SetValue(ShowAltGridProperty, value); }
|
||||
}
|
||||
|
||||
public bool AllowClickDrag
|
||||
{
|
||||
get { return GetValue(AllowClickDragProperty); }
|
||||
set { SetValue(AllowClickDragProperty, value); }
|
||||
}
|
||||
|
||||
public Rect SelectionRect
|
||||
{
|
||||
get { return GetValue(SelectionRectProperty); }
|
||||
|
@ -141,6 +148,7 @@ namespace Mesen.Debugger.Controls
|
|||
public ScrollPictureViewer()
|
||||
{
|
||||
InitializeComponent();
|
||||
Background = new SolidColorBrush(0xFF202020);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
|
@ -157,7 +165,7 @@ namespace Mesen.Debugger.Controls
|
|||
|
||||
private void Viewer_PointerMoved(object? sender, PointerEventArgs e)
|
||||
{
|
||||
if(e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) {
|
||||
if(AllowClickDrag && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) {
|
||||
Vector offset = ScrollOffset;
|
||||
offset -= e.GetPosition(this) - _lastPosition;
|
||||
if(offset.X < 0) {
|
||||
|
|
|
@ -665,5 +665,12 @@ namespace Mesen.Debugger.Utilities
|
|||
FindNext,
|
||||
[IconFile("PreviousArrow")]
|
||||
FindPrev,
|
||||
|
||||
[IconFile("Edit")]
|
||||
EditTile,
|
||||
[IconFile("Edit")]
|
||||
EditTiles,
|
||||
[IconFile("Edit")]
|
||||
EditSprite,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Mesen.Debugger.Utilities
|
|||
private static object _windowNotifLock = new();
|
||||
private static bool _loadingGame = false;
|
||||
|
||||
public static T OpenDebugWindow<T>(Func<T> createWindow) where T : Window
|
||||
public static T CreateDebugWindow<T>(Func<T> createWindow) where T : Window
|
||||
{
|
||||
if(Interlocked.Increment(ref _debugWindowCounter) == 1) {
|
||||
//Opened a debug window and nothing else was opened, load the saved workspace
|
||||
|
@ -35,6 +35,12 @@ namespace Mesen.Debugger.Utilities
|
|||
}
|
||||
};
|
||||
_openedWindows.TryAdd(wnd, true);
|
||||
return wnd;
|
||||
}
|
||||
|
||||
public static T OpenDebugWindow<T>(Func<T> createWindow) where T : Window
|
||||
{
|
||||
T wnd = CreateDebugWindow<T>(createWindow);
|
||||
wnd.Show();
|
||||
return wnd;
|
||||
}
|
||||
|
|
|
@ -109,9 +109,12 @@ namespace Mesen.Debugger.ViewModels
|
|||
DebuggerShortcut.TilemapViewer_ViewInTileViewer,
|
||||
DebuggerShortcut.TilemapViewer_EditTilemapBreakpoint,
|
||||
DebuggerShortcut.TilemapViewer_EditAttributeBreakpoint,
|
||||
DebuggerShortcut.TilemapViewer_EditTile,
|
||||
DebuggerShortcut.TileViewer_ViewInMemoryViewer,
|
||||
DebuggerShortcut.TileViewer_EditTile,
|
||||
DebuggerShortcut.SpriteViewer_ViewInMemoryViewer,
|
||||
DebuggerShortcut.SpriteViewer_ViewInTileViewer
|
||||
DebuggerShortcut.SpriteViewer_ViewInTileViewer,
|
||||
DebuggerShortcut.SpriteViewer_EditSprite
|
||||
});
|
||||
|
||||
DebuggerShortcuts = CreateShortcutList(new DebuggerShortcut[] {
|
||||
|
|
|
@ -32,6 +32,10 @@ namespace Mesen.Debugger.ViewModels
|
|||
[Reactive] public bool HorizontalMirror { get; set; }
|
||||
[Reactive] public bool VerticalMirror { get; set; }
|
||||
[Reactive] public bool UseExtendedVram { get; set; }
|
||||
|
||||
public UInt32 TileCount { get; set; }
|
||||
public UInt32[] TileAddresses { get; set; } = Array.Empty<UInt32>();
|
||||
|
||||
[Reactive] public NullableBoolean UseSecondTable { get; set; }
|
||||
|
||||
[Reactive] public DynamicBitmap? SpritePreview { get; set; }
|
||||
|
@ -60,6 +64,16 @@ namespace Mesen.Debugger.ViewModels
|
|||
UseSecondTable = sprite.UseSecondTable;
|
||||
UseExtendedVram = sprite.UseExtendedVram;
|
||||
|
||||
TileCount = sprite.TileCount;
|
||||
fixed(UInt32* p = sprite.TileAddresses) {
|
||||
if(TileAddresses == null || TileAddresses.Length < TileCount) {
|
||||
TileAddresses = new UInt32[TileCount];
|
||||
}
|
||||
for(int i = 0; i < sprite.TileCount; i++) {
|
||||
TileAddresses[i] = p[i];
|
||||
}
|
||||
}
|
||||
|
||||
fixed(UInt32* p = sprite.SpritePreview) {
|
||||
if(SpritePreview == null || SpritePreview.PixelSize.Width != sprite.Width || SpritePreview.PixelSize.Height != sprite.Height) {
|
||||
SpritePreview = new DynamicBitmap(new PixelSize(Width, Height), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Premul);
|
||||
|
|
|
@ -19,6 +19,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Mesen.Debugger.ViewModels
|
||||
{
|
||||
|
@ -110,7 +111,12 @@ namespace Mesen.Debugger.ViewModels
|
|||
},
|
||||
});
|
||||
|
||||
if(Design.IsDesignMode || wnd == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugShortcutManager.CreateContextMenu(picViewer, new List<object> {
|
||||
GetEditTileAction(wnd),
|
||||
GetViewInMemoryViewerAction(),
|
||||
GetViewInTileViewerAction(),
|
||||
new ContextMenuSeparator(),
|
||||
|
@ -121,19 +127,17 @@ namespace Mesen.Debugger.ViewModels
|
|||
});
|
||||
|
||||
DebugShortcutManager.CreateContextMenu(_spriteGrid, new List<object> {
|
||||
GetEditTileAction(wnd),
|
||||
GetViewInMemoryViewerAction(),
|
||||
GetViewInTileViewerAction()
|
||||
});
|
||||
|
||||
DebugShortcutManager.CreateContextMenu(listView, new List<object> {
|
||||
GetEditTileAction(wnd),
|
||||
GetViewInMemoryViewerAction(),
|
||||
GetViewInTileViewerAction()
|
||||
});
|
||||
|
||||
if(Design.IsDesignMode || wnd == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AddDisposable(this.WhenAnyValue(x => x.SelectedSprite).Subscribe(x => {
|
||||
UpdateSelectionPreview();
|
||||
if(x != null) {
|
||||
|
@ -153,6 +157,29 @@ namespace Mesen.Debugger.ViewModels
|
|||
DebugShortcutManager.RegisterActions(wnd, ViewMenuActions);
|
||||
}
|
||||
|
||||
private ContextMenuAction GetEditTileAction(Window wnd)
|
||||
{
|
||||
return new ContextMenuAction() {
|
||||
ActionType = ActionType.EditSprite,
|
||||
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.SpriteViewer_EditSprite),
|
||||
IsEnabled = () => GetSelectedSprite() != null,
|
||||
OnClick = () => {
|
||||
SpritePreviewModel? sprite = GetSelectedSprite();
|
||||
if(sprite?.TileAddress >= 0 && _palette != null) {
|
||||
PixelSize size = sprite.Format.GetTileSize();
|
||||
DebugPaletteInfo pal = _palette.Get();
|
||||
int paletteOffset = (int)(pal.BgColorCount / pal.ColorsPerPalette);
|
||||
TileEditorWindow.OpenAtTile(
|
||||
sprite.TileAddresses.Select(x => new AddressInfo() { Address = (int)x, Type = CpuType.GetVramMemoryType(sprite.UseExtendedVram) }).ToList(),
|
||||
sprite.Width / size.Width,
|
||||
sprite.Format,
|
||||
sprite.Palette + paletteOffset,
|
||||
wnd);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ContextMenuAction GetViewInMemoryViewerAction()
|
||||
{
|
||||
return new ContextMenuAction() {
|
||||
|
|
179
NewUI/Debugger/ViewModels/TileEditorViewModel.cs
Normal file
179
NewUI/Debugger/ViewModels/TileEditorViewModel.cs
Normal file
|
@ -0,0 +1,179 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Threading;
|
||||
using Mesen.Config;
|
||||
using Mesen.Debugger.Controls;
|
||||
using Mesen.Debugger.Utilities;
|
||||
using Mesen.Debugger.Windows;
|
||||
using Mesen.Interop;
|
||||
using Mesen.Utilities;
|
||||
using Mesen.ViewModels;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace Mesen.Debugger.ViewModels;
|
||||
|
||||
public class TileEditorViewModel : DisposableViewModel
|
||||
{
|
||||
[Reactive] public DynamicBitmap ViewerBitmap { get; private set; }
|
||||
|
||||
[Reactive] public UInt32[] PaletteColors { get; set; } = Array.Empty<UInt32>();
|
||||
[Reactive] public UInt32[] RawPalette { get; set; } = Array.Empty<UInt32>();
|
||||
[Reactive] public RawPaletteFormat RawFormat { get; set; }
|
||||
[Reactive] public int PaletteColumnCount { get; private set; } = 16;
|
||||
[Reactive] public int SelectedColor { get; set; } = 0;
|
||||
|
||||
public TileEditorConfig Config { get; }
|
||||
|
||||
public List<object> FileMenuActions { get; private set; } = new();
|
||||
public List<object> ViewMenuActions { get; private set; } = new();
|
||||
|
||||
private CpuType _cpuType;
|
||||
private List<AddressInfo> _tileAddresses;
|
||||
private int _columnCount = 1;
|
||||
private int _rowCount = 1;
|
||||
private TileFormat _tileFormat;
|
||||
private byte[] _sourceData = Array.Empty<byte>();
|
||||
private UInt32[] _tileBuffer = Array.Empty<UInt32>();
|
||||
|
||||
[Obsolete("For designer only")]
|
||||
public TileEditorViewModel() : this(new() { new() }, 1, TileFormat.Bpp4, 0) { }
|
||||
|
||||
public TileEditorViewModel(List<AddressInfo> tileAddresses, int columnCount, TileFormat format, int initialPalette)
|
||||
{
|
||||
Config = ConfigManager.Config.Debug.TileEditor;
|
||||
_tileAddresses = tileAddresses;
|
||||
_columnCount = columnCount;
|
||||
_rowCount = tileAddresses.Count / columnCount;
|
||||
_cpuType = tileAddresses[0].Type.ToCpuType();
|
||||
_tileFormat = format;
|
||||
SelectedColor = initialPalette * GetColorsPerPalette(_tileFormat);
|
||||
|
||||
PixelSize size = format.GetTileSize();
|
||||
_tileBuffer = new UInt32[size.Width * size.Height];
|
||||
ViewerBitmap = new DynamicBitmap(new PixelSize(size.Width * _columnCount, size.Height * _rowCount), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Premul);
|
||||
|
||||
if(Design.IsDesignMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
AddDisposable(this.WhenAnyValue(x => x.Config.Background).Subscribe(x => RefreshViewer()));
|
||||
AddDisposable(this.WhenAnyValue(x => x.SelectedColor).Subscribe(x => RefreshViewer()));
|
||||
AddDisposable(this.WhenAnyValue(x => x.Config.ImageScale).Subscribe(x => {
|
||||
if(Config.ImageScale < 4) {
|
||||
Config.ImageScale = 4;
|
||||
}
|
||||
}));
|
||||
|
||||
RefreshViewer();
|
||||
}
|
||||
|
||||
public void InitActions(PictureViewer picViewer, Window wnd)
|
||||
{
|
||||
FileMenuActions = AddDisposables(new List<object>() {
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.ExportToPng,
|
||||
OnClick = () => picViewer.ExportToPng()
|
||||
},
|
||||
new ContextMenuSeparator(),
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.Exit,
|
||||
OnClick = () => wnd.Close()
|
||||
}
|
||||
});
|
||||
|
||||
ViewMenuActions = AddDisposables(new List<object>() {
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.ZoomIn,
|
||||
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.ZoomIn),
|
||||
OnClick = () => picViewer.ZoomIn()
|
||||
},
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.ZoomOut,
|
||||
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.ZoomOut),
|
||||
OnClick = () => picViewer.ZoomOut()
|
||||
},
|
||||
});
|
||||
|
||||
DebugShortcutManager.RegisterActions(wnd, FileMenuActions);
|
||||
DebugShortcutManager.RegisterActions(wnd, ViewMenuActions);
|
||||
}
|
||||
|
||||
public void UpdatePixel(PixelPoint position, bool clearPixel)
|
||||
{
|
||||
int pixelColor = clearPixel ? 0 : SelectedColor % GetColorsPerPalette(_tileFormat);
|
||||
PixelSize tileSize = _tileFormat.GetTileSize();
|
||||
|
||||
int column = position.X / tileSize.Width;
|
||||
int row = position.Y / tileSize.Height;
|
||||
int tileX = position.X % tileSize.Width;
|
||||
int tileY = position.Y % tileSize.Height;
|
||||
DebugApi.SetTilePixel(_tileAddresses[column+row*_columnCount], _tileFormat, tileX, tileY, pixelColor);
|
||||
RefreshViewer();
|
||||
}
|
||||
|
||||
private unsafe void RefreshViewer()
|
||||
{
|
||||
Dispatcher.UIThread.Post((Action)(() => {
|
||||
DebugPaletteInfo palette = DebugApi.GetPaletteInfo(_cpuType, new GetPaletteInfoOptions() { Format = _tileFormat });
|
||||
PaletteColors = palette.GetRgbPalette();
|
||||
RawPalette = palette.GetRawPalette();
|
||||
RawFormat = palette.RawFormat;
|
||||
PaletteColumnCount = PaletteColors.Length > 16 ? 16 : 4;
|
||||
|
||||
_sourceData = DebugApi.GetMemoryState(_tileAddresses[0].Type);
|
||||
PixelSize tileSize = _tileFormat.GetTileSize();
|
||||
|
||||
using(var framebuffer = ViewerBitmap.Lock()) {
|
||||
for(int y = 0; y < _rowCount; y++) {
|
||||
for(int x = 0; x < _columnCount; x++) {
|
||||
fixed(UInt32* ptr = _tileBuffer) {
|
||||
DebugApi.GetTileView(_cpuType, GetOptions(x, y), _sourceData, _sourceData.Length, PaletteColors, (IntPtr)ptr);
|
||||
UInt32* viewer = (UInt32*)framebuffer.FrameBuffer.Address;
|
||||
int rowPitch = ViewerBitmap.PixelSize.Width;
|
||||
int baseOffset = x * tileSize.Width + y * tileSize.Height * rowPitch;
|
||||
for(int j = 0; j < tileSize.Height; j++) {
|
||||
for(int i = 0; i < tileSize.Width; i++) {
|
||||
viewer[baseOffset + j * rowPitch + i] = ptr[j * tileSize.Width + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private int GetColorsPerPalette(TileFormat format)
|
||||
{
|
||||
return format.GetBitsPerPixel() switch {
|
||||
2 => 4, //4-color palettes
|
||||
4 => 16, //16-color palettes
|
||||
_ => 256
|
||||
};
|
||||
}
|
||||
|
||||
private GetTileViewOptions GetOptions(int column, int row)
|
||||
{
|
||||
int tileIndex = row * _columnCount + column;
|
||||
|
||||
return new GetTileViewOptions() {
|
||||
MemType = _tileAddresses[tileIndex].Type,
|
||||
Format = _tileFormat,
|
||||
Width = _tileFormat.GetTileSize().Width / 8,
|
||||
Height = _tileFormat.GetTileSize().Height / 8,
|
||||
Palette = SelectedColor / GetColorsPerPalette(_tileFormat),
|
||||
Layout = TileLayout.Normal,
|
||||
Filter = TileFilter.None,
|
||||
StartAddress = _tileAddresses[tileIndex].Address,
|
||||
Background = Config.Background,
|
||||
UseGrayscalePalette = false
|
||||
};
|
||||
}
|
||||
}
|
|
@ -40,6 +40,7 @@ namespace Mesen.Debugger.ViewModels
|
|||
[Reactive] public RawPaletteFormat RawFormat { get; set; }
|
||||
[Reactive] public PaletteSelectionMode PaletteSelectionMode { get; private set; }
|
||||
[Reactive] public int PaletteColumnCount { get; private set; } = 16;
|
||||
[Reactive] public int SelectedPalette { get; set; } = 0;
|
||||
|
||||
[Reactive] public int AddressIncrement { get; private set; }
|
||||
[Reactive] public int MaximumAddress { get; private set; } = int.MaxValue;
|
||||
|
@ -115,6 +116,43 @@ namespace Mesen.Debugger.ViewModels
|
|||
});
|
||||
|
||||
DebugShortcutManager.CreateContextMenu(picViewer, new List<object> {
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.EditTile,
|
||||
HintText = () => $"{GridSizeX}px x {GridSizeY}px",
|
||||
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.TileViewer_EditTile),
|
||||
IsEnabled = () => GetSelectedTileAddress() >= 0,
|
||||
OnClick = () => EditTileGrid(1, 1, wnd)
|
||||
},
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.EditTiles,
|
||||
SubActions = new() {
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.Custom,
|
||||
CustomText = $"1x2 ({GridSizeX}px x {GridSizeY*2}px)",
|
||||
IsEnabled = () => GetSelectedTileAddress() >= 0,
|
||||
OnClick = () => EditTileGrid(1, 2, wnd)
|
||||
},
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.Custom,
|
||||
CustomText = $"2x1 ({GridSizeX*2}px x {GridSizeY}px)",
|
||||
IsEnabled = () => GetSelectedTileAddress() >= 0,
|
||||
OnClick = () => EditTileGrid(2, 1, wnd)
|
||||
},
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.Custom,
|
||||
CustomText = $"2x2 ({GridSizeX*2}px x {GridSizeY*2}px)",
|
||||
IsEnabled = () => GetSelectedTileAddress() >= 0,
|
||||
OnClick = () => EditTileGrid(2, 2, wnd)
|
||||
},
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.Custom,
|
||||
CustomText = $"4x4 ({GridSizeX*4}px x {GridSizeY*4}px)",
|
||||
IsEnabled = () => GetSelectedTileAddress() >= 0,
|
||||
OnClick = () => EditTileGrid(4, 4, wnd)
|
||||
}
|
||||
}
|
||||
},
|
||||
new ContextMenuSeparator(),
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.ViewInMemoryViewer,
|
||||
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.TileViewer_ViewInMemoryViewer),
|
||||
|
@ -139,19 +177,21 @@ namespace Mesen.Debugger.ViewModels
|
|||
InitForCpuType();
|
||||
|
||||
AddDisposable(this.WhenAnyValue(x => x.Config.Format).Subscribe(x => {
|
||||
PaletteSelectionMode = GetBitsPerPixel(x) switch {
|
||||
PaletteSelectionMode = x.GetBitsPerPixel() switch {
|
||||
2 => PaletteSelectionMode.FourColors,
|
||||
4 => PaletteSelectionMode.SixteenColors,
|
||||
_ => PaletteSelectionMode.None
|
||||
};
|
||||
|
||||
PixelSize tileSize = GetTileSize(x);
|
||||
PixelSize tileSize = x.GetTileSize();
|
||||
if(GridSizeX != tileSize.Width || GridSizeY != tileSize.Height) {
|
||||
GridSizeX = tileSize.Width;
|
||||
GridSizeY = tileSize.Height;
|
||||
SelectionRect = Rect.Empty;
|
||||
PreviewPanel = null;
|
||||
}
|
||||
|
||||
RefreshPalette();
|
||||
}));
|
||||
|
||||
AddDisposable(this.WhenAnyValue(x => x.Config.Layout).Subscribe(x => {
|
||||
|
@ -160,7 +200,7 @@ namespace Mesen.Debugger.ViewModels
|
|||
|
||||
AddDisposable(this.WhenAnyValue(x => x.Config.ColumnCount, x => x.Config.RowCount, x => x.Config.Format).Subscribe(x => {
|
||||
ApplyColumnRowCountRestrictions();
|
||||
AddressIncrement = Config.ColumnCount * Config.RowCount * 8 * 8 * GetBitsPerPixel(Config.Format) / 8;
|
||||
AddressIncrement = Config.ColumnCount * Config.RowCount * 8 * 8 * Config.Format.GetBitsPerPixel() / 8;
|
||||
}));
|
||||
|
||||
AddDisposable(this.WhenAnyValue(x => x.Config.Source).Subscribe(memType => {
|
||||
|
@ -172,6 +212,7 @@ namespace Mesen.Debugger.ViewModels
|
|||
RefreshData();
|
||||
}));
|
||||
|
||||
AddDisposable(this.WhenAnyValue(x => x.SelectedPalette).Subscribe(x => RefreshTab()));
|
||||
AddDisposable(this.WhenAnyValue(x => x.SelectionRect).Subscribe(x => UpdatePreviewPanel()));
|
||||
|
||||
AddDisposable(ReactiveHelper.RegisterRecursiveObserver(Config, Config_PropertyChanged));
|
||||
|
@ -365,14 +406,15 @@ namespace Mesen.Debugger.ViewModels
|
|||
int[,] layerBpp = new int[8, 4] { { 2, 2, 2, 2 }, { 4, 4, 2, 0 }, { 4, 4, 0, 0 }, { 8, 4, 0, 0 }, { 8, 2, 0, 0 }, { 4, 2, 0, 0 }, { 4, 0, 0, 0 }, { 8, 0, 0, 0 } };
|
||||
SnesPpuState ppu = (SnesPpuState)state;
|
||||
Config.Source = MemoryType.SnesVideoRam;
|
||||
Config.StartAddress = ppu.Layers[layer].ChrAddress * 2;
|
||||
Config.ColumnCount = 16;
|
||||
Config.RowCount = 16;
|
||||
Config.Layout = TileLayout.Normal;
|
||||
if(ppu.BgMode == 7) {
|
||||
Config.Format = ppu.DirectColorMode ? TileFormat.Mode7DirectColor : TileFormat.Mode7;
|
||||
Config.SelectedPalette = 0;
|
||||
Config.Format = ppu.ExtBgEnabled ? TileFormat.Mode7ExtBg : (ppu.DirectColorMode ? TileFormat.Mode7DirectColor : TileFormat.Mode7);
|
||||
Config.StartAddress = 0;
|
||||
SelectedPalette = 0;
|
||||
} else {
|
||||
Config.StartAddress = ppu.Layers[layer].ChrAddress * 2;
|
||||
Config.Format = layerBpp[ppu.BgMode, layer] switch {
|
||||
2 => TileFormat.Bpp2,
|
||||
4 => TileFormat.Bpp4,
|
||||
|
@ -380,8 +422,8 @@ namespace Mesen.Debugger.ViewModels
|
|||
_ => TileFormat.Bpp2
|
||||
};
|
||||
|
||||
if(layerBpp[ppu.BgMode, layer] == 8 || Config.SelectedPalette >= (layerBpp[ppu.BgMode, layer] == 2 ? 32 : 8)) {
|
||||
Config.SelectedPalette = 0;
|
||||
if(layerBpp[ppu.BgMode, layer] == 8 || SelectedPalette >= (layerBpp[ppu.BgMode, layer] == 2 ? 32 : 8)) {
|
||||
SelectedPalette = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -395,8 +437,8 @@ namespace Mesen.Debugger.ViewModels
|
|||
Config.RowCount = 16;
|
||||
Config.Layout = TileLayout.Normal;
|
||||
Config.Format = TileFormat.NesBpp2;
|
||||
if(Config.SelectedPalette >= 4) {
|
||||
Config.SelectedPalette = 0;
|
||||
if(SelectedPalette >= 4) {
|
||||
SelectedPalette = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -410,8 +452,8 @@ namespace Mesen.Debugger.ViewModels
|
|||
Config.Layout = TileLayout.Normal;
|
||||
Config.Format = TileFormat.Bpp2;
|
||||
Config.Background = ppu.CgbEnabled ? TileBackground.PaletteColor : TileBackground.Default;
|
||||
if(!ppu.CgbEnabled || Config.SelectedPalette > 8) {
|
||||
Config.SelectedPalette = 0;
|
||||
if(!ppu.CgbEnabled || SelectedPalette > 8) {
|
||||
SelectedPalette = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -424,8 +466,8 @@ namespace Mesen.Debugger.ViewModels
|
|||
Config.Layout = TileLayout.Normal;
|
||||
Config.Format = TileFormat.Bpp4;
|
||||
Config.Background = TileBackground.Default;
|
||||
if(Config.SelectedPalette >= 16) {
|
||||
Config.SelectedPalette = 0;
|
||||
if(SelectedPalette >= 16) {
|
||||
SelectedPalette = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -447,8 +489,8 @@ namespace Mesen.Debugger.ViewModels
|
|||
Config.ColumnCount = 16;
|
||||
Config.RowCount = 16;
|
||||
Config.StartAddress = (ppu.OamBaseAddress + (layer == 1 ? ppu.OamAddressOffset : 0)) * 2;
|
||||
if(Config.SelectedPalette < 8 || Config.SelectedPalette >= 16) {
|
||||
Config.SelectedPalette = 8;
|
||||
if(SelectedPalette < 8 || SelectedPalette >= 16) {
|
||||
SelectedPalette = 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -467,8 +509,8 @@ namespace Mesen.Debugger.ViewModels
|
|||
Config.Layout = TileLayout.Normal;
|
||||
}
|
||||
Config.Source = MemoryType.NesPpuMemory;
|
||||
if(Config.SelectedPalette < 4 || Config.SelectedPalette >= 8) {
|
||||
Config.SelectedPalette = 4;
|
||||
if(SelectedPalette < 4 || SelectedPalette >= 8) {
|
||||
SelectedPalette = 4;
|
||||
}
|
||||
Config.Format = TileFormat.NesBpp2;
|
||||
break;
|
||||
|
@ -483,10 +525,10 @@ namespace Mesen.Debugger.ViewModels
|
|||
Config.Layout = TileLayout.Normal;
|
||||
Config.Background = TileBackground.Black;
|
||||
Config.Format = TileFormat.Bpp2;
|
||||
if(ppu.CgbEnabled && Config.SelectedPalette < 8) {
|
||||
Config.SelectedPalette = 8;
|
||||
} else if(!ppu.CgbEnabled && Config.SelectedPalette == 0) {
|
||||
Config.SelectedPalette = 1;
|
||||
if(ppu.CgbEnabled && SelectedPalette < 8) {
|
||||
SelectedPalette = 8;
|
||||
} else if(!ppu.CgbEnabled && SelectedPalette == 0) {
|
||||
SelectedPalette = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -499,8 +541,8 @@ namespace Mesen.Debugger.ViewModels
|
|||
Config.Layout = TileLayout.Normal;
|
||||
Config.Format = TileFormat.PceSpriteBpp4;
|
||||
Config.Background = TileBackground.Default;
|
||||
if(Config.SelectedPalette < 16) {
|
||||
Config.SelectedPalette = 16;
|
||||
if(SelectedPalette < 16) {
|
||||
SelectedPalette = 16;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -511,11 +553,11 @@ namespace Mesen.Debugger.ViewModels
|
|||
{
|
||||
Config.Source = type;
|
||||
Config.Format = format;
|
||||
Config.SelectedPalette = paletteIndex;
|
||||
SelectedPalette = paletteIndex;
|
||||
Config.StartAddress = address / AddressIncrement * AddressIncrement;
|
||||
Config.Layout = layout;
|
||||
int bitsPerPixel = GetBitsPerPixel(Config.Format);
|
||||
PixelSize tileSize = GetTileSize(Config.Format);
|
||||
int bitsPerPixel = Config.Format.GetBitsPerPixel();
|
||||
PixelSize tileSize = Config.Format.GetTileSize();
|
||||
int bytesPerTile = tileSize.Width * tileSize.Height * bitsPerPixel / 8;
|
||||
|
||||
int gap = address - Config.StartAddress;
|
||||
|
@ -606,16 +648,20 @@ namespace Mesen.Debugger.ViewModels
|
|||
public void RefreshData()
|
||||
{
|
||||
_ppuState = DebugApi.GetPpuState(CpuType);
|
||||
|
||||
RefreshPalette();
|
||||
_sourceData = DebugApi.GetMemoryState(Config.Source);
|
||||
|
||||
DebugPaletteInfo palette = DebugApi.GetPaletteInfo(CpuType);
|
||||
RefreshTab();
|
||||
}
|
||||
|
||||
private void RefreshPalette()
|
||||
{
|
||||
DebugPaletteInfo palette = DebugApi.GetPaletteInfo(CpuType, new GetPaletteInfoOptions() { Format = Config.Format });
|
||||
PaletteColors = palette.GetRgbPalette();
|
||||
RawPalette = palette.GetRawPalette();
|
||||
RawFormat = palette.RawFormat;
|
||||
PaletteColumnCount = PaletteColors.Length > 16 ? 16 : 4;
|
||||
|
||||
_sourceData = DebugApi.GetMemoryState(Config.Source);
|
||||
|
||||
RefreshTab();
|
||||
}
|
||||
|
||||
private void RefreshTab()
|
||||
|
@ -633,8 +679,8 @@ namespace Mesen.Debugger.ViewModels
|
|||
|
||||
private int GetTileAddress(PixelPoint pixelPosition)
|
||||
{
|
||||
int bitsPerPixel = GetBitsPerPixel(Config.Format);
|
||||
PixelSize tileSize = GetTileSize(Config.Format);
|
||||
int bitsPerPixel = Config.Format.GetBitsPerPixel();
|
||||
PixelSize tileSize = Config.Format.GetTileSize();
|
||||
int bytesPerTile = tileSize.Width * tileSize.Height * bitsPerPixel / 8;
|
||||
PixelPoint pos = FromLayoutCoordinates(Config.Layout, new PixelPoint(pixelPosition.X / tileSize.Width, pixelPosition.Y / tileSize.Height));
|
||||
int offset = (pos.Y * Config.ColumnCount * 8 / tileSize.Width + pos.X) * bytesPerTile;
|
||||
|
@ -662,7 +708,7 @@ namespace Mesen.Debugger.ViewModels
|
|||
|
||||
entries.StartUpdate();
|
||||
|
||||
PixelSize tileSize = GetTileSize(Config.Format);
|
||||
PixelSize tileSize = Config.Format.GetTileSize();
|
||||
PixelRect cropRect = new PixelRect(p.X / tileSize.Width * tileSize.Width, p.Y / tileSize.Height * tileSize.Height, tileSize.Width, tileSize.Height);
|
||||
entries.AddPicture("Tile", ViewerBitmap, 6, cropRect);
|
||||
|
||||
|
@ -678,6 +724,18 @@ namespace Mesen.Debugger.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
private void EditTileGrid(int columnCount, int rowCount, Window wnd)
|
||||
{
|
||||
PixelPoint p = ViewerMousePos ?? PixelPoint.FromPoint(SelectionRect.TopLeft, 1);
|
||||
List<AddressInfo> addresses = new();
|
||||
for(int row = 0; row < rowCount; row++) {
|
||||
for(int col = 0; col < columnCount; col++) {
|
||||
addresses.Add(new AddressInfo() { Address = GetTileAddress(new PixelPoint(p.X + col*GridSizeX, p.Y + row*GridSizeY)), Type = Config.Source });
|
||||
}
|
||||
}
|
||||
TileEditorWindow.OpenAtTile(addresses, columnCount, Config.Format, SelectedPalette, wnd);
|
||||
}
|
||||
|
||||
private GetTileViewOptions GetOptions()
|
||||
{
|
||||
return new GetTileViewOptions() {
|
||||
|
@ -685,7 +743,7 @@ namespace Mesen.Debugger.ViewModels
|
|||
Format = Config.Format,
|
||||
Width = Config.ColumnCount,
|
||||
Height = Config.RowCount,
|
||||
Palette = Config.SelectedPalette,
|
||||
Palette = SelectedPalette,
|
||||
Layout = Config.Layout,
|
||||
Filter = ShowFilterDropdown ? Config.Filter : TileFilter.None,
|
||||
StartAddress = Config.StartAddress,
|
||||
|
@ -704,28 +762,6 @@ namespace Mesen.Debugger.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
private int GetBitsPerPixel(TileFormat format)
|
||||
{
|
||||
return format switch {
|
||||
TileFormat.Bpp2 => 2,
|
||||
TileFormat.Bpp4 => 4,
|
||||
TileFormat.DirectColor => 8,
|
||||
TileFormat.Mode7 => 16,
|
||||
TileFormat.Mode7DirectColor => 16,
|
||||
TileFormat.NesBpp2 => 2,
|
||||
TileFormat.PceSpriteBpp4 => 4,
|
||||
_ => 8,
|
||||
};
|
||||
}
|
||||
|
||||
private PixelSize GetTileSize(TileFormat format)
|
||||
{
|
||||
return format switch {
|
||||
TileFormat.PceSpriteBpp4 => new PixelSize(16, 16),
|
||||
_ => new PixelSize(8,8),
|
||||
};
|
||||
}
|
||||
|
||||
public void OnGameLoaded()
|
||||
{
|
||||
InitForCpuType();
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace Mesen.Debugger.ViewModels
|
|||
private DebugTilemapInfo _tilemapInfo;
|
||||
private PictureViewer _picViewer;
|
||||
private BaseState? _ppuState;
|
||||
private byte[]? _prevVram;
|
||||
private byte[] _prevVram = Array.Empty<byte>();
|
||||
private byte[] _vram = Array.Empty<byte>();
|
||||
private UInt32[] _rgbPalette = Array.Empty<UInt32>();
|
||||
private UInt32[] _rawPalette = Array.Empty<UInt32>();
|
||||
|
@ -144,6 +144,37 @@ namespace Mesen.Debugger.ViewModels
|
|||
}
|
||||
},
|
||||
new ContextMenuSeparator(),
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.EditTile,
|
||||
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.TilemapViewer_EditTile),
|
||||
OnClick = () => EditTileGrid(1, 1, wnd)
|
||||
},
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.EditTiles,
|
||||
SubActions = new() {
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.Custom,
|
||||
CustomText = $"1x2 ({GridSizeX}px x {GridSizeY*2}px)",
|
||||
OnClick = () => EditTileGrid(1, 2, wnd)
|
||||
},
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.Custom,
|
||||
CustomText = $"2x1 ({GridSizeX*2}px x {GridSizeY}px)",
|
||||
OnClick = () => EditTileGrid(2, 1, wnd)
|
||||
},
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.Custom,
|
||||
CustomText = $"2x2 ({GridSizeX*2}px x {GridSizeY*2}px)",
|
||||
OnClick = () => EditTileGrid(2, 2, wnd)
|
||||
},
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.Custom,
|
||||
CustomText = $"4x4 ({GridSizeX*4}px x {GridSizeY*4}px)",
|
||||
OnClick = () => EditTileGrid(4, 4, wnd)
|
||||
}
|
||||
}
|
||||
},
|
||||
new ContextMenuSeparator(),
|
||||
new ContextMenuAction() {
|
||||
ActionType = ActionType.EditTilemapBreakpoint,
|
||||
HintText = () => {
|
||||
|
@ -277,7 +308,7 @@ namespace Mesen.Debugger.ViewModels
|
|||
|
||||
private DebugTilemapTileInfo? GetSelectedTileInfo()
|
||||
{
|
||||
if(_ppuState == null || _prevVram == null) {
|
||||
if(_ppuState == null || _vram == null) {
|
||||
return null;
|
||||
} else {
|
||||
PixelPoint p;
|
||||
|
@ -289,7 +320,7 @@ namespace Mesen.Debugger.ViewModels
|
|||
}
|
||||
p = PixelPoint.FromPoint(SelectionRect.TopLeft, 1);
|
||||
}
|
||||
return DebugApi.GetTilemapTileInfo((uint)p.X, (uint)p.Y, CpuType, GetOptions(SelectedTab), _prevVram, _ppuState);
|
||||
return DebugApi.GetTilemapTileInfo((uint)p.X, (uint)p.Y, CpuType, GetOptions(SelectedTab), _vram, _ppuState);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,7 +354,7 @@ namespace Mesen.Debugger.ViewModels
|
|||
{
|
||||
return new GetTilemapOptions() {
|
||||
Layer = (byte)tab.Layer,
|
||||
CompareVram = prevVram,
|
||||
CompareVram = prevVram?.Length > 0 ? prevVram : null,
|
||||
AccessCounters = accessCounters,
|
||||
TileHighlightMode = Config.TileHighlightMode,
|
||||
AttributeHighlightMode = Config.AttributeHighlightMode,
|
||||
|
@ -360,7 +391,7 @@ namespace Mesen.Debugger.ViewModels
|
|||
}
|
||||
|
||||
BaseState ppuState = _ppuState;
|
||||
byte[]? prevVram = _prevVram;
|
||||
byte[] prevVram = _prevVram;
|
||||
byte[] vram = _vram;
|
||||
uint[] palette = _rgbPalette;
|
||||
AddressCounters[] accessCounters = _accessCounters;
|
||||
|
@ -428,11 +459,11 @@ namespace Mesen.Debugger.ViewModels
|
|||
|
||||
public DynamicTooltip? GetPreviewPanel(PixelPoint p, DynamicTooltip? tooltipToUpdate)
|
||||
{
|
||||
if(_ppuState == null || _prevVram == null) {
|
||||
if(_ppuState == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DebugTilemapTileInfo? result = DebugApi.GetTilemapTileInfo((uint)p.X, (uint)p.Y, CpuType, GetOptions(SelectedTab), _prevVram, _ppuState);
|
||||
DebugTilemapTileInfo? result = DebugApi.GetTilemapTileInfo((uint)p.X, (uint)p.Y, CpuType, GetOptions(SelectedTab), _vram, _ppuState);
|
||||
if(result == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -492,6 +523,31 @@ namespace Mesen.Debugger.ViewModels
|
|||
}
|
||||
}
|
||||
|
||||
private void EditTileGrid(int columnCount, int rowCount, Window wnd)
|
||||
{
|
||||
if(_ppuState != null) {
|
||||
PixelPoint p = ViewerMousePos ?? PixelPoint.FromPoint(SelectionRect.TopLeft, 1);
|
||||
List<AddressInfo> addresses = new();
|
||||
MemoryType memType = GetVramMemoryType();
|
||||
int palette = -1;
|
||||
for(int row = 0; row < rowCount; row++) {
|
||||
for(int col = 0; col < columnCount; col++) {
|
||||
DebugTilemapTileInfo? tile = DebugApi.GetTilemapTileInfo((uint)(p.X + GridSizeX*col), (uint)(p.Y + GridSizeY*row), CpuType, GetOptions(SelectedTab), _vram, _ppuState);
|
||||
if(tile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(palette == -1) {
|
||||
palette = tile.Value.PaletteIndex;
|
||||
}
|
||||
addresses.Add(new AddressInfo() { Address = tile.Value.TileAddress, Type = memType });
|
||||
}
|
||||
}
|
||||
palette = Math.Max(0, palette);
|
||||
TileEditorWindow.OpenAtTile(addresses, columnCount, _tilemapInfo.Format, palette, wnd);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnGameLoaded()
|
||||
{
|
||||
_inGameLoaded = true;
|
||||
|
|
80
NewUI/Debugger/Windows/TileEditorWindow.axaml
Normal file
80
NewUI/Debugger/Windows/TileEditorWindow.axaml
Normal file
|
@ -0,0 +1,80 @@
|
|||
<Window
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:m="clr-namespace:Mesen"
|
||||
xmlns:vm="using:Mesen.ViewModels"
|
||||
xmlns:sys="using:System"
|
||||
xmlns:v="using:Mesen.Views"
|
||||
xmlns:c="using:Mesen.Controls"
|
||||
xmlns:i="using:Mesen.Interop"
|
||||
xmlns:l="using:Mesen.Localization"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:dvm="using:Mesen.Debugger.ViewModels"
|
||||
xmlns:dc="using:Mesen.Debugger.Controls"
|
||||
xmlns:dv="using:Mesen.Debugger.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="600"
|
||||
x:Class="Mesen.Debugger.Windows.TileEditorWindow"
|
||||
x:DataType="dvm:TileEditorViewModel"
|
||||
Width="600" Height="600"
|
||||
Title="{l:Translate wndTitle}"
|
||||
Icon="/Assets/Edit.png"
|
||||
>
|
||||
<Design.DataContext>
|
||||
<dvm:TileEditorViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<DockPanel>
|
||||
<Panel DockPanel.Dock="Top">
|
||||
<Menu DockPanel.Dock="Top">
|
||||
<MenuItem Classes="ActionMenu" Header="{l:Translate mnuFile}" Items="{CompiledBinding FileMenuActions}" />
|
||||
<MenuItem Classes="ActionMenu" Header="{l:Translate mnuView}" Items="{CompiledBinding ViewMenuActions}" />
|
||||
</Menu>
|
||||
</Panel>
|
||||
|
||||
<ScrollViewer Margin="3 0" DockPanel.Dock="Right">
|
||||
<StackPanel>
|
||||
<Border BorderBrush="Gray" BorderThickness="1" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<dc:PaletteSelector
|
||||
ColumnCount="{CompiledBinding PaletteColumnCount}"
|
||||
BlockSize="14"
|
||||
SelectionMode="SingleColor"
|
||||
PaletteColors="{CompiledBinding PaletteColors}"
|
||||
RawPalette="{CompiledBinding RawPalette}"
|
||||
RawFormat="{CompiledBinding RawFormat}"
|
||||
SelectedPalette="{CompiledBinding SelectedColor}"
|
||||
/>
|
||||
</Border>
|
||||
<DockPanel Margin="3 5">
|
||||
<TextBlock Text="{l:Translate lblBackground}" VerticalAlignment="Center" DockPanel.Dock="Left" />
|
||||
<c:EnumComboBox SelectedItem="{CompiledBinding Config.Background}" />
|
||||
</DockPanel>
|
||||
<StackPanel Margin="3 0 3 5">
|
||||
<TextBlock Text="{l:Translate lblHint1}" />
|
||||
<TextBlock Text="{l:Translate lblHint2}" />
|
||||
<TextBlock Text="{l:Translate lblHint3}" />
|
||||
</StackPanel>
|
||||
<c:GroupBox Header="{l:Translate lblPreview}">
|
||||
<Border BorderBrush="Gray" Background="{StaticResource ViewerBgBrush}" BorderThickness="1" HorizontalAlignment="Left">
|
||||
<dc:PictureViewer
|
||||
Zoom="4"
|
||||
Source="{CompiledBinding ViewerBitmap}"
|
||||
AllowSelection="False"
|
||||
ShowMousePosition="False"
|
||||
/>
|
||||
</Border>
|
||||
</c:GroupBox>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<dc:ScrollPictureViewer
|
||||
x:Name="picViewer"
|
||||
AllowClickDrag="False"
|
||||
AllowSelection="False"
|
||||
Zoom="{CompiledBinding Config.ImageScale}"
|
||||
Source="{CompiledBinding ViewerBitmap}"
|
||||
GridSizeX="1"
|
||||
GridSizeY="1"
|
||||
/>
|
||||
</DockPanel>
|
||||
</Window>
|
90
NewUI/Debugger/Windows/TileEditorWindow.axaml.cs
Normal file
90
NewUI/Debugger/Windows/TileEditorWindow.axaml.cs
Normal file
|
@ -0,0 +1,90 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using System;
|
||||
using Mesen.Debugger.Controls;
|
||||
using Mesen.Debugger.ViewModels;
|
||||
using Mesen.Interop;
|
||||
using System.ComponentModel;
|
||||
using Avalonia.Interactivity;
|
||||
using Mesen.Debugger.Utilities;
|
||||
using Mesen.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Threading;
|
||||
using System.Linq;
|
||||
using Mesen.Config;
|
||||
|
||||
namespace Mesen.Debugger.Windows
|
||||
{
|
||||
public class TileEditorWindow : Window, INotificationHandler
|
||||
{
|
||||
private TileEditorViewModel _model;
|
||||
|
||||
[Obsolete("For designer only")]
|
||||
public TileEditorWindow() : this(new()) { }
|
||||
|
||||
public TileEditorWindow(TileEditorViewModel model)
|
||||
{
|
||||
InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
|
||||
PictureViewer picViewer = this.FindControl<ScrollPictureViewer>("picViewer").InnerViewer;
|
||||
picViewer.PositionClicked += PicViewer_PositionClicked;
|
||||
_model = model;
|
||||
_model.InitActions(picViewer, this);
|
||||
DataContext = _model;
|
||||
|
||||
_model.Config.LoadWindowSettings(this);
|
||||
}
|
||||
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
base.OnClosing(e);
|
||||
_model.Config.SaveWindowSettings(this);
|
||||
}
|
||||
|
||||
private void PicViewer_PositionClicked(object? sender, PositionClickedEventArgs e)
|
||||
{
|
||||
_model.UpdatePixel(e.Position, e.Properties.IsRightButtonPressed);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public static void OpenAtTile(AddressInfo tileAddr, TileFormat tileFormat, int selectedPalette, Window parent)
|
||||
{
|
||||
OpenAtTile(new List<AddressInfo>() { tileAddr }, 1, tileFormat, selectedPalette, parent);
|
||||
}
|
||||
|
||||
public static void OpenAtTile(List<AddressInfo> tileAddresses, int columnCount, TileFormat tileFormat, int selectedPalette, Window parent)
|
||||
{
|
||||
for(int i = 0; i < tileAddresses.Count; i++) {
|
||||
AddressInfo addr = tileAddresses[i];
|
||||
if(addr.Type.IsRelativeMemory()) {
|
||||
tileAddresses[i] = DebugApi.GetAbsoluteAddress(addr);
|
||||
}
|
||||
}
|
||||
|
||||
if(tileAddresses.Any(x => x.Address < 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TileEditorViewModel model = new(tileAddresses, columnCount, tileFormat, selectedPalette);
|
||||
TileEditorWindow wnd = DebugWindowManager.CreateDebugWindow<TileEditorWindow>(() => new TileEditorWindow(model));
|
||||
wnd.ShowCentered((Control)parent);
|
||||
}
|
||||
|
||||
public void ProcessNotification(NotificationEventArgs e)
|
||||
{
|
||||
if(e.NotificationType == ConsoleNotificationType.GameLoaded) {
|
||||
Dispatcher.UIThread.Post(() => {
|
||||
Close();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -134,10 +134,10 @@
|
|||
IsEnabled="{CompiledBinding !Config.UseGrayscalePalette}"
|
||||
BlockSize="12"
|
||||
SelectionMode="{CompiledBinding PaletteSelectionMode}"
|
||||
SelectedPalette="{CompiledBinding Config.SelectedPalette}"
|
||||
PaletteColors="{CompiledBinding PaletteColors}"
|
||||
RawPalette="{CompiledBinding RawPalette}"
|
||||
RawFormat="{CompiledBinding RawFormat}"
|
||||
SelectedPalette="{CompiledBinding SelectedPalette}"
|
||||
/>
|
||||
<Rectangle
|
||||
Fill="{StaticResource DisabledOverlayBrush}"
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia;
|
||||
using Mesen.Debugger;
|
||||
using Mesen.Utilities;
|
||||
|
||||
|
@ -279,7 +280,8 @@ namespace Mesen.Interop
|
|||
return sprites;
|
||||
}
|
||||
|
||||
[DllImport(DllPath)] public static extern DebugPaletteInfo GetPaletteInfo(CpuType cpuType);
|
||||
[DllImport(DllPath)] public static extern DebugPaletteInfo GetPaletteInfo(CpuType cpuType, GetPaletteInfoOptions options = new());
|
||||
[DllImport(DllPath)] public static extern void SetTilePixel(AddressInfo tileAddress, TileFormat format, int x, int y, int color);
|
||||
|
||||
[DllImport(DllPath)] public static extern void SetViewerUpdateTiming(Int32 viewerId, Int32 scanline, Int32 cycle, CpuType cpuType);
|
||||
|
||||
|
@ -781,11 +783,12 @@ namespace Mesen.Interop
|
|||
|
||||
public enum TileBackground
|
||||
{
|
||||
Default = 0,
|
||||
PaletteColor = 1,
|
||||
Black = 2,
|
||||
White = 3,
|
||||
Magenta = 4
|
||||
Default,
|
||||
Transparent,
|
||||
PaletteColor,
|
||||
Black,
|
||||
White,
|
||||
Magenta,
|
||||
}
|
||||
|
||||
public enum NullableBoolean
|
||||
|
@ -874,6 +877,11 @@ namespace Mesen.Interop
|
|||
public Int32 SelectedSprite;
|
||||
}
|
||||
|
||||
public struct GetPaletteInfoOptions
|
||||
{
|
||||
public TileFormat Format;
|
||||
}
|
||||
|
||||
public struct DebugSpritePreviewInfo
|
||||
{
|
||||
public UInt32 Width;
|
||||
|
@ -919,6 +927,9 @@ namespace Mesen.Interop
|
|||
[MarshalAs(UnmanagedType.I1)] public bool UseExtendedVram;
|
||||
public NullableBoolean UseSecondTable;
|
||||
|
||||
public UInt32 TileCount;
|
||||
public fixed UInt32 TileAddresses[8*8];
|
||||
|
||||
public fixed UInt32 SpritePreview[64*64];
|
||||
}
|
||||
|
||||
|
@ -976,6 +987,31 @@ namespace Mesen.Interop
|
|||
PceSpriteBpp4
|
||||
}
|
||||
|
||||
public static class TileFormatExtensions
|
||||
{
|
||||
public static PixelSize GetTileSize(this TileFormat format)
|
||||
{
|
||||
return format switch {
|
||||
TileFormat.PceSpriteBpp4 => new PixelSize(16, 16),
|
||||
_ => new PixelSize(8, 8),
|
||||
};
|
||||
}
|
||||
|
||||
public static int GetBitsPerPixel(this TileFormat format)
|
||||
{
|
||||
return format switch {
|
||||
TileFormat.Bpp2 => 2,
|
||||
TileFormat.Bpp4 => 4,
|
||||
TileFormat.DirectColor => 8,
|
||||
TileFormat.Mode7 => 16,
|
||||
TileFormat.Mode7DirectColor => 16,
|
||||
TileFormat.NesBpp2 => 2,
|
||||
TileFormat.PceSpriteBpp4 => 4,
|
||||
_ => 8,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum TileLayout
|
||||
{
|
||||
Normal,
|
||||
|
|
|
@ -795,7 +795,20 @@
|
|||
<Control ID="lblAttributeHighlightMode">Attributes:</Control>
|
||||
<Control ID="chkShowScrollOverlay">Show scroll overlay</Control>
|
||||
</Form>
|
||||
|
||||
<Form ID="TileEditorWindow">
|
||||
<Control ID="wndTitle">Tile Editor</Control>
|
||||
<Control ID="mnuFile">_File</Control>
|
||||
<Control ID="mnuView">_View</Control>
|
||||
|
||||
<Control ID="lblBackground">Background:</Control>
|
||||
<Control ID="lblPreview">Preview</Control>
|
||||
|
||||
<Control ID="lblHint1">Select color by clicking above.</Control>
|
||||
<Control ID="lblHint2">Left-click: Draw selected color</Control>
|
||||
<Control ID="lblHint3">Right-click: Make pixel transparent</Control>
|
||||
</Form>
|
||||
|
||||
<Form ID="TileViewerWindow">
|
||||
<Control ID="wndTitle">Tile Viewer</Control>
|
||||
<Control ID="mnuFile">_File</Control>
|
||||
|
@ -2003,6 +2016,7 @@
|
|||
<Value ID="Black">Black</Value>
|
||||
<Value ID="White">White</Value>
|
||||
<Value ID="Magenta">Magenta</Value>
|
||||
<Value ID="Transparent">Transparent</Value>
|
||||
</Enum>
|
||||
<Enum ID="TilemapDisplayMode">
|
||||
<Value ID="Default">Tilemap</Value>
|
||||
|
@ -2270,9 +2284,12 @@
|
|||
<Value ID="TilemapViewer_EditAttributeBreakpoint">Tilemap Viewer - Edit Breakpoint (Attribute)</Value>
|
||||
<Value ID="TilemapViewer_ViewInMemoryViewer">Tilemap Viewer - View in Memory Viewer</Value>
|
||||
<Value ID="TilemapViewer_ViewInTileViewer">Tilemap Viewer - View in Tile Viewer</Value>
|
||||
<Value ID="TilemapViewer_EditTile">Tilemap Viewer - Edit Tile</Value>
|
||||
<Value ID="TileViewer_ViewInMemoryViewer">Tile Viewer - View in Memory Viewer</Value>
|
||||
<Value ID="TileViewer_EditTile">Tile Viewer - Edit Tile</Value>
|
||||
<Value ID="SpriteViewer_ViewInMemoryViewer">Sprite Viewer - View in Memory Viewer</Value>
|
||||
<Value ID="SpriteViewer_ViewInTileViewer">Sprite Viewer - View in Tile Viewer</Value>
|
||||
<Value ID="SpriteViewer_EditSprite">Sprite Viewer - Edit Sprite</Value>
|
||||
<Value ID="MemoryViewer_Freeze">Freeze</Value>
|
||||
<Value ID="MemoryViewer_Unfreeze">Unfreeze</Value>
|
||||
<Value ID="MemoryViewer_AddToWatch">Add to Watch</Value>
|
||||
|
@ -2524,6 +2541,10 @@
|
|||
<Value ID="Find">Find...</Value>
|
||||
<Value ID="FindNext">Find next</Value>
|
||||
<Value ID="FindPrev">Find previous</Value>
|
||||
|
||||
<Value ID="EditTile">Edit tile</Value>
|
||||
<Value ID="EditTiles">Edit tiles</Value>
|
||||
<Value ID="EditSprite">Edit sprite</Value>
|
||||
</Enum>
|
||||
</Enums>
|
||||
</Resources>
|
|
@ -357,6 +357,9 @@
|
|||
<Compile Update="Debugger\Windows\PaletteViewerWindow.axaml.cs">
|
||||
<DependentUpon>PaletteViewerWindow.axaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Debugger\Windows\TileEditorWindow.axaml.cs">
|
||||
<DependentUpon>TileEditorWindow.axaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Debugger\Windows\TraceLoggerWindow.axaml.cs">
|
||||
<DependentUpon>TraceLoggerWindow.axaml</DependentUpon>
|
||||
</Compile>
|
||||
|
|
Loading…
Add table
Reference in a new issue