mirror of
https://github.com/haven1433/HexManiacAdvance.git
synced 2026-03-21 17:34:13 -05:00
'Add page' for compressed sprites/palettes
Existing images sometimes include multiple sprites/palettes within the same compressed data. Example: Castform. Make it possible to add extra pages through the tool.
This commit is contained in:
parent
fa7d3d70a8
commit
58344f4631
|
|
@ -7,3 +7,8 @@ indent_size = 3
|
|||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
csharp_new_line_before_open_brace = none
|
||||
|
||||
[*.xaml]
|
||||
indent_style = space
|
||||
indent_size = 3
|
||||
trim_trailing_whitespace = true
|
||||
|
|
|
|||
|
|
@ -81,5 +81,25 @@ namespace HavenSoft.HexManiac.Core.Models.Runs.Sprites {
|
|||
model.ObserveRunWritten(token, newRun);
|
||||
return newRun;
|
||||
}
|
||||
|
||||
public LzPaletteRun AppendPage(ModelDelta token) {
|
||||
var data = Decompress(Model, Start);
|
||||
var lastPage = Pages - 1;
|
||||
var pageLength = (int)Math.Pow(2, PaletteFormat.Bits) * 2;
|
||||
var newData = new byte[data.Length + pageLength];
|
||||
Array.Copy(data, newData, data.Length);
|
||||
Array.Copy(data, lastPage * pageLength, newData, data.Length, pageLength);
|
||||
var newModelData = Compress(newData, 0, newData.Length);
|
||||
|
||||
var newRun = (LzPaletteRun)Model.RelocateForExpansion(token, this, newModelData.Count);
|
||||
for (int i = 0; i < newModelData.Count; i++) token.ChangeData(Model, newRun.Start + i, newModelData[i]);
|
||||
newRun = new LzPaletteRun(PaletteFormat, Model, newRun.Start, newRun.PointerSources);
|
||||
Model.ObserveRunWritten(token, newRun);
|
||||
return newRun;
|
||||
}
|
||||
|
||||
public LzPaletteRun DeletePage(int page, ModelDelta token) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,5 +68,26 @@ namespace HavenSoft.HexManiac.Core.Models.Runs.Sprites {
|
|||
model.ObserveRunWritten(token, newRun);
|
||||
return newRun;
|
||||
}
|
||||
|
||||
public LzSpriteRun AppendPage(ModelDelta token) {
|
||||
var data = Decompress(Model, Start);
|
||||
var lastPage = Pages - 1;
|
||||
var pageLength = SpriteFormat.TileWidth * SpriteFormat.TileHeight * 8 * SpriteFormat.BitsPerPixel;
|
||||
var newData = new byte[data.Length + pageLength];
|
||||
Array.Copy(data, newData, data.Length);
|
||||
Array.Copy(data, lastPage * pageLength, newData, data.Length, pageLength);
|
||||
var newModelData = Compress(newData, 0, newData.Length);
|
||||
|
||||
var newRun = (LzSpriteRun)Model.RelocateForExpansion(token, this, newModelData.Count);
|
||||
for (int i = 0; i < newModelData.Count; i++) token.ChangeData(Model, newRun.Start + i, newModelData[i]);
|
||||
newRun = new LzSpriteRun(SpriteFormat, Model, newRun.Start, newRun.PointerSources);
|
||||
Model.ObserveRunWritten(token, newRun);
|
||||
return newRun;
|
||||
}
|
||||
|
||||
public LzSpriteRun DeletePage(int page, ModelDelta token) {
|
||||
// TODO
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs.Sprites {
|
|||
foreach (var relatedTable in model.GetRelatedArrays(spriteTable)) {
|
||||
results.AddRange(model.GetPointedChildren<IPaletteRun>(relatedTable, offset.ElementIndex));
|
||||
}
|
||||
|
||||
results.AddRange(model.GetPointedChildren<IPaletteRun>(spriteTable, offset.ElementIndex));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,10 +109,12 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
}
|
||||
|
||||
public interface IPagedViewModel : IStreamArrayElementViewModel {
|
||||
bool HasMultiplePages { get; }
|
||||
bool ShowPageControls { get; }
|
||||
int Pages { get; }
|
||||
int CurrentPage { get; set; }
|
||||
ICommand PreviousPage { get; }
|
||||
ICommand NextPage { get; }
|
||||
ICommand AddPage { get; }
|
||||
ICommand DeletePage { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using System.Windows.Input;
|
|||
namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
||||
public abstract class PagedElementViewModel : StreamElementViewModel, IPagedViewModel {
|
||||
|
||||
public bool HasMultiplePages => Pages > 1;
|
||||
public bool ShowPageControls => Pages > 1 || CanExecuteAddPage();
|
||||
|
||||
private int currentPage;
|
||||
public int CurrentPage {
|
||||
|
|
@ -15,18 +15,43 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
previousPage.CanExecuteChanged.Invoke(previousPage, EventArgs.Empty);
|
||||
nextPage.CanExecuteChanged.Invoke(nextPage, EventArgs.Empty);
|
||||
UpdateOtherPagedViewModels();
|
||||
addPage?.CanExecuteChanged.Invoke(addPage, EventArgs.Empty);
|
||||
PageChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public int Pages { get; protected set; }
|
||||
|
||||
#region Commands
|
||||
|
||||
private readonly StubCommand previousPage = new StubCommand();
|
||||
public ICommand PreviousPage => previousPage;
|
||||
|
||||
private readonly StubCommand nextPage = new StubCommand();
|
||||
public ICommand NextPage => nextPage;
|
||||
|
||||
private StubCommand addPage, deletePage;
|
||||
public ICommand AddPage => StubCommand(ref addPage, ExecuteAddPage, CanExecuteAddPage);
|
||||
public ICommand DeletePage => StubCommand(ref deletePage, ExecuteDeletePage, CanExecuteDeletePage);
|
||||
protected abstract bool CanExecuteAddPage();
|
||||
protected abstract bool CanExecuteDeletePage();
|
||||
protected virtual void ExecuteAddPage() {
|
||||
foreach (var child in ViewPort.Tools.TableTool.Children) {
|
||||
if (!(child is PagedElementViewModel pvm)) continue;
|
||||
if (pvm.Pages == Pages - 1) pvm.ExecuteAddPage();
|
||||
}
|
||||
deletePage?.CanExecuteChanged.Invoke(deletePage, EventArgs.Empty);
|
||||
}
|
||||
protected virtual void ExecuteDeletePage() {
|
||||
foreach (var child in ViewPort.Tools.TableTool.Children) {
|
||||
if (!(child is PagedElementViewModel pvm)) continue;
|
||||
if (pvm.Pages == Pages + 1) pvm.ExecuteDeletePage();
|
||||
}
|
||||
deletePage?.CanExecuteChanged.Invoke(deletePage, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public PagedElementViewModel(ViewPort viewPort, int start) : base(viewPort, start) {
|
||||
Pages = 1;
|
||||
|
||||
|
|
@ -54,7 +79,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
currentPage = that.currentPage;
|
||||
Pages = that.Pages;
|
||||
NotifyPropertyChanged(nameof(Pages));
|
||||
NotifyPropertyChanged(nameof(HasMultiplePages));
|
||||
NotifyPropertyChanged(nameof(ShowPageControls));
|
||||
addPage?.CanExecuteChanged.Invoke(addPage, EventArgs.Empty);
|
||||
deletePage?.CanExecuteChanged.Invoke(deletePage, EventArgs.Empty);
|
||||
NotifyPropertyChanged(nameof(CurrentPage));
|
||||
previousPage.CanExecuteChanged.Invoke(previousPage, EventArgs.Empty);
|
||||
nextPage.CanExecuteChanged.Invoke(nextPage, EventArgs.Empty);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using HavenSoft.HexManiac.Core.Models;
|
||||
using HavenSoft.HexManiac.Core.Models.Runs;
|
||||
using HavenSoft.HexManiac.Core.Models.Runs.Sprites;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
||||
public class PaletteElementViewModel : PagedElementViewModel, IPagedViewModel {
|
||||
public class PaletteElementViewModel : PagedElementViewModel {
|
||||
private PaletteFormat format;
|
||||
|
||||
public string TableName { get; private set; }
|
||||
|
|
@ -40,6 +40,35 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
|
||||
public void Activate() => UpdateSprites(TableName);
|
||||
|
||||
protected override bool CanExecuteAddPage() {
|
||||
var destination = ViewPort.Model.ReadPointer(Start);
|
||||
var run = ViewPort.Model.GetNextRun(destination) as IPaletteRun;
|
||||
return run is LzPaletteRun && CurrentPage == run.Pages - 1 && run.FindDependentSprites(Model).All(sprite => sprite.Pages == run.Pages && sprite is LzSpriteRun);
|
||||
}
|
||||
|
||||
protected override void ExecuteAddPage() {
|
||||
var destination = ViewPort.Model.ReadPointer(Start);
|
||||
if (!(ViewPort.Model.GetNextRun(destination) is LzPaletteRun run)) return;
|
||||
var newRun = run.AppendPage(ViewPort.CurrentChange);
|
||||
if (newRun.Start != run.Start) {
|
||||
ViewPort.RaiseMessage($"Palette moved from {run.Start:X6} to {newRun.Start:X6}. Pointers were updated.");
|
||||
}
|
||||
Pages = newRun.Pages;
|
||||
CurrentPage = newRun.Pages - 1;
|
||||
base.ExecuteAddPage();
|
||||
}
|
||||
|
||||
protected override bool CanExecuteDeletePage() {
|
||||
var destination = ViewPort.Model.ReadPointer(Start);
|
||||
var run = ViewPort.Model.GetNextRun(destination) as IPaletteRun;
|
||||
return run is LzPaletteRun && Pages > 1 && run.FindDependentSprites(Model).All(sprite => sprite.Pages == run.Pages && sprite is LzSpriteRun);
|
||||
}
|
||||
|
||||
protected override void ExecuteDeletePage() {
|
||||
// TODO
|
||||
base.ExecuteDeletePage();
|
||||
}
|
||||
|
||||
private void UpdateSprites(string hint = null) {
|
||||
foreach (var child in ViewPort.Tools.TableTool.Children) {
|
||||
if (child == this) break;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
using HavenSoft.HexManiac.Core.Models;
|
||||
using HavenSoft.HexManiac.Core.Models.Runs;
|
||||
using HavenSoft.HexManiac.Core.Models.Runs.Sprites;
|
||||
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
||||
public class SpriteElementViewModel : PagedElementViewModel, IPagedViewModel, IPixelViewModel {
|
||||
public class SpriteElementViewModel : PagedElementViewModel, IPixelViewModel {
|
||||
private SpriteFormat format;
|
||||
private string paletteHint;
|
||||
|
||||
|
|
@ -46,6 +47,36 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
UpdateTiles(Start, page, false);
|
||||
}
|
||||
|
||||
protected override bool CanExecuteAddPage() {
|
||||
var destination = ViewPort.Model.ReadPointer(Start);
|
||||
var run = ViewPort.Model.GetNextRun(destination) as ISpriteRun;
|
||||
return run is LzSpriteRun && CurrentPage == run.Pages - 1 && run.FindRelatedPalettes(Model).All(pal => pal.Pages == run.Pages && pal is LzPaletteRun);
|
||||
}
|
||||
|
||||
protected override void ExecuteAddPage() {
|
||||
var destination = ViewPort.Model.ReadPointer(Start);
|
||||
if (!(ViewPort.Model.GetNextRun(destination) is LzSpriteRun run)) return;
|
||||
var newRun = run.AppendPage(ViewPort.CurrentChange);
|
||||
if (newRun.Start != run.Start) {
|
||||
ViewPort.RaiseMessage($"Sprite moved from {run.Start:X6} to {newRun.Start:X6}. Pointers were updated.");
|
||||
}
|
||||
Pages = newRun.Pages;
|
||||
CurrentPage = newRun.Pages - 1;
|
||||
base.ExecuteAddPage();
|
||||
UpdateTiles(Start, CurrentPage, true); // update the tiles once all the related palettes have their pages added
|
||||
}
|
||||
|
||||
protected override bool CanExecuteDeletePage() {
|
||||
var destination = ViewPort.Model.ReadPointer(Start);
|
||||
var run = ViewPort.Model.GetNextRun(destination) as ISpriteRun;
|
||||
return run is LzSpriteRun && Pages > 1 && run.FindRelatedPalettes(Model).All(pal => pal.Pages == run.Pages && pal is LzPaletteRun);
|
||||
}
|
||||
|
||||
protected override void ExecuteDeletePage() {
|
||||
// TODO
|
||||
base.ExecuteDeletePage();
|
||||
}
|
||||
|
||||
private int[,] lastPixels;
|
||||
private IReadOnlyList<short> lastColors;
|
||||
private void UpdateTiles(int start, int page, bool exitPaletteSearchEarly) {
|
||||
|
|
|
|||
|
|
@ -503,6 +503,36 @@ namespace HavenSoft.HexManiac.Tests {
|
|||
Assert.IsType<SpriteRun>(Model.GetNextRun(0xA0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanAddPageToCompressedSpritesAndPalettes() {
|
||||
// Arrange: place some sprites and palettes, linked to a name table
|
||||
// 000-010 : pokenames table
|
||||
// 010-020 : sprite/palette tables (each is a single pointer)
|
||||
// 100-130 : front sprites
|
||||
// 130-160 : back sprites
|
||||
// 160-190 : normal palettes
|
||||
// 190-1B0 : shiny palettes
|
||||
ViewPort.Edit("FF @00 ^pokenames[name\"\"15]1 Castform\"");
|
||||
ViewPort.Edit("@100 10 20 @130 10 20 @160 10 20 @190 10 20 @10 ");
|
||||
ViewPort.Edit("^front.sprites[sprite<`lzs4x1x1`>]pokenames <100>");
|
||||
ViewPort.Edit("^back.sprites[sprite<`lzs4x1x1`>]pokenames <130>");
|
||||
ViewPort.Edit("^normal.palette[pal<`lzp4`>]pokenames <160>");
|
||||
ViewPort.Edit("^shiny.palette[pal<`lzp4`>]pokenames <190>");
|
||||
|
||||
// Act: expand each by a single page
|
||||
ViewPort.Goto.Execute("pokenames");
|
||||
var sevm = (SpriteElementViewModel)ViewPort.Tools.TableTool.Children.First(child => child is SpriteElementViewModel);
|
||||
sevm.AddPage.Execute();
|
||||
|
||||
// Assert: each has 2 pages now
|
||||
foreach(var child in ViewPort.Tools.TableTool.Children) {
|
||||
if (child is IPagedViewModel pvm) {
|
||||
Assert.Equal(2, pvm.Pages);
|
||||
Assert.Equal(1, pvm.CurrentPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateLzRun(int start, params byte[] data) {
|
||||
for (int i = 0; i < data.Length; i++) Model[start + i] = data[i];
|
||||
var run = new LZRun(Model, start);
|
||||
|
|
|
|||
|
|
@ -259,8 +259,9 @@
|
|||
<StackPanel Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}">
|
||||
<hsg3hv:CommonTableStreamControl />
|
||||
<hsg3hv:PixelImage SnapsToDevicePixels="True" />
|
||||
<DockPanel Visibility="{Binding HasMultiplePages, Converter={StaticResource BoolToVisibility}}" LastChildFill="False">
|
||||
<DockPanel Visibility="{Binding ShowPageControls, Converter={StaticResource BoolToVisibility}}" LastChildFill="False">
|
||||
<Button Content="Previous" Command="{Binding PreviousPage}" DockPanel.Dock="Left"/>
|
||||
<Button Content="+" Command="{Binding AddPage}" DockPanel.Dock="Right" ToolTip="Add a new page to this sprite and palette"/>
|
||||
<Button Content="Next" Command="{Binding NextPage}" DockPanel.Dock="Right"/>
|
||||
</DockPanel>
|
||||
</StackPanel>
|
||||
|
|
@ -269,8 +270,9 @@
|
|||
<StackPanel PreviewMouseDown="ActivatePalette" Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}">
|
||||
<hsg3hv:CommonTableStreamControl />
|
||||
<hsg3hv:PaletteControl DataContext="{Binding Colors}" />
|
||||
<DockPanel Visibility="{Binding HasMultiplePages, Converter={StaticResource BoolToVisibility}}" LastChildFill="False">
|
||||
<DockPanel Visibility="{Binding ShowPageControls, Converter={StaticResource BoolToVisibility}}" LastChildFill="False">
|
||||
<Button Content="Previous" Command="{Binding PreviousPage}" DockPanel.Dock="Left"/>
|
||||
<Button Content="+" Command="{Binding AddPage}" DockPanel.Dock="Right" ToolTip="Add a new page to this sprite and palette"/>
|
||||
<Button Content="Next" Command="{Binding NextPage}" DockPanel.Dock="Right"/>
|
||||
</DockPanel>
|
||||
</StackPanel>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user