mirror of
https://github.com/haven1433/HexManiacAdvance.git
synced 2026-05-24 14:28:04 -05:00
Implement simple Tuple Stream Serialization
This commit is contained in:
parent
6ff5e621a2
commit
7bbf669c10
|
|
@ -329,12 +329,47 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
|
|||
Elements = content;
|
||||
if (content.Sum(seg => seg.BitWidth) > length * 8) throw new ArrayRunParseException($"{name}: tuple too long to fit in field!");
|
||||
}
|
||||
|
||||
public override string ToText(IDataModel rawData, int offset, bool deep = false) {
|
||||
var result = "(";
|
||||
var bitOffset = 0;
|
||||
foreach (var segment in Elements) {
|
||||
if (!string.IsNullOrEmpty(segment.SourceName)) {
|
||||
// TODO
|
||||
} else if (segment.BitWidth == 1) {
|
||||
result += segment.Read(rawData, offset, bitOffset) == 1 ? "true" : "false";
|
||||
} else {
|
||||
result += segment.Read(rawData, offset, bitOffset);
|
||||
}
|
||||
result += " ";
|
||||
bitOffset += segment.BitWidth;
|
||||
}
|
||||
return result.Trim() + ")";
|
||||
}
|
||||
}
|
||||
public class TupleSegment {
|
||||
public string Name { get; }
|
||||
public string SourceName { get; }
|
||||
public int BitWidth { get; }
|
||||
public TupleSegment(string name, int width, string sourceName = null) => (Name, BitWidth, SourceName) = (name, width, sourceName);
|
||||
public int Read(IDataModel model, int start, int bitOffset) {
|
||||
var requiredByteLength = (bitOffset + BitWidth + 7) / 8;
|
||||
if (requiredByteLength > 4) return 0;
|
||||
var bitArray = model.ReadMultiByteValue(start, requiredByteLength);
|
||||
bitArray >>= bitOffset;
|
||||
bitArray &= (1 << BitWidth) - 1;
|
||||
return bitArray;
|
||||
}
|
||||
public void Write(IDataModel model, ModelDelta token, int start, int bitOffset, int value) {
|
||||
var requiredByteLength = (bitOffset + BitWidth+ 7) / 8;
|
||||
if (requiredByteLength > 4) return;
|
||||
var bitArray = model.ReadMultiByteValue(start, requiredByteLength);
|
||||
var mask = (1 << BitWidth) - 1;
|
||||
value &= mask;
|
||||
bitArray &= ~(mask << bitOffset);
|
||||
bitArray |= value << bitOffset;
|
||||
model.WriteMultiByteValue(start, requiredByteLength, token, bitArray);
|
||||
}
|
||||
}
|
||||
|
||||
public class ArrayRunColorSegment : ArrayRunElementSegment {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using HavenSoft.HexManiac.Core.Models;
|
||||
using HavenSoft.HexManiac.Core.Models.Runs;
|
||||
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
||||
|
|
@ -24,7 +24,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
var bitOffset = 0;
|
||||
for (int i = 0; i < tupleItem.Elements.Count; i++) {
|
||||
if (tupleItem.Elements[i].BitWidth == 1) {
|
||||
Children.Add(new CheckBoxTupleElementViewModel(viewPort, start, bitOffset, tupleItem.Elements[i].Name));
|
||||
Children.Add(new CheckBoxTupleElementViewModel(viewPort, start, bitOffset, tupleItem.Elements[i]));
|
||||
} else if (!string.IsNullOrEmpty(tupleItem.Elements[i].SourceName)) {
|
||||
Children.Add(new EnumTupleElementViewModel(viewPort, start, bitOffset, tupleItem.Elements[i]));
|
||||
} else {
|
||||
|
|
@ -56,37 +56,23 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
}
|
||||
|
||||
public class NumericTupleElementViewModel : ViewModelCore, ITupleElementViewModel {
|
||||
protected ViewPort ViewPort { get; }
|
||||
public string Name { get; }
|
||||
private readonly TupleSegment seg;
|
||||
private readonly ViewPort viewPort;
|
||||
public string Name => seg.Name;
|
||||
public int BitOffset { get; }
|
||||
public int BitLength { get; }
|
||||
public int BitLength => seg.BitWidth;
|
||||
public int Start { get; private set; }
|
||||
|
||||
public int Content {
|
||||
get {
|
||||
var requiredByteLength = (BitOffset + BitLength + 7) / 8;
|
||||
if (requiredByteLength > 4) return 0;
|
||||
var bitArray = ViewPort.Model.ReadMultiByteValue(Start, requiredByteLength);
|
||||
bitArray >>= BitOffset;
|
||||
bitArray &= (1 << BitLength) - 1;
|
||||
return bitArray;
|
||||
}
|
||||
get => seg.Read(viewPort.Model, Start, BitOffset);
|
||||
set {
|
||||
var requiredByteLength = (BitOffset + BitLength + 7) / 8;
|
||||
if (requiredByteLength > 4) return;
|
||||
var bitArray = ViewPort.Model.ReadMultiByteValue(Start, requiredByteLength);
|
||||
var mask = (1 << BitLength) - 1;
|
||||
value &= mask;
|
||||
bitArray &= ~(mask << BitOffset);
|
||||
bitArray |= value << BitOffset;
|
||||
ViewPort.Model.WriteMultiByteValue(Start, requiredByteLength, ViewPort.CurrentChange, bitArray);
|
||||
seg.Write(viewPort.Model, viewPort.CurrentChange, Start, BitOffset, value);
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public NumericTupleElementViewModel(ViewPort viewPort, int start, int bitOffset, TupleSegment segment) {
|
||||
ViewPort = viewPort;
|
||||
(Name, Start, BitOffset, BitLength) = (segment.Name, start, bitOffset, segment.BitWidth);
|
||||
(this.viewPort, seg, Start, BitOffset) = (viewPort, segment, start, bitOffset);
|
||||
}
|
||||
|
||||
public virtual bool TryCopy(ITupleElementViewModel other) {
|
||||
|
|
@ -103,37 +89,26 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
}
|
||||
|
||||
public class CheckBoxTupleElementViewModel : ViewModelCore, ITupleElementViewModel {
|
||||
private readonly TupleSegment seg;
|
||||
private readonly ViewPort viewPort;
|
||||
public string Name { get; }
|
||||
public string Name => seg.Name;
|
||||
public int BitOffset { get; }
|
||||
public int BitLength => 1;
|
||||
public int Start { get; private set; }
|
||||
|
||||
public bool IsChecked {
|
||||
get {
|
||||
var requiredByteLength = (BitOffset + BitLength + 7) / 8;
|
||||
if (requiredByteLength > 4) return false;
|
||||
var bitArray = viewPort.Model.ReadMultiByteValue(Start, requiredByteLength);
|
||||
bitArray >>= BitOffset;
|
||||
return (bitArray & 1) == 1;
|
||||
}
|
||||
get => seg.Read(viewPort.Model, Start, BitOffset) == 1;
|
||||
set {
|
||||
var requiredByteLength = (BitOffset + BitLength + 7) / 8;
|
||||
if (requiredByteLength > 4) return;
|
||||
var bitArray = viewPort.Model.ReadMultiByteValue(Start, requiredByteLength);
|
||||
if (value) {
|
||||
bitArray |= 1 << BitOffset;
|
||||
} else {
|
||||
bitArray &= ~(1 << BitOffset);
|
||||
}
|
||||
viewPort.Model.WriteMultiByteValue(Start, requiredByteLength, viewPort.CurrentChange, bitArray);
|
||||
var bit = value ? 1 : 0;
|
||||
seg.Write(viewPort.Model, viewPort.CurrentChange, Start, BitOffset, bit);
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public CheckBoxTupleElementViewModel(ViewPort viewPort, int start, int bitOffset, string name) {
|
||||
public CheckBoxTupleElementViewModel(ViewPort viewPort, int start, int bitOffset, TupleSegment segment) {
|
||||
Debug.Assert(segment.BitWidth == 1, "Checkboxes should not be constructed from segments with BitWidth other than 1!");
|
||||
this.viewPort = viewPort;
|
||||
(Name, Start, BitOffset) = (name, start, bitOffset);
|
||||
(seg, Start, BitOffset) = (segment, start, bitOffset);
|
||||
}
|
||||
|
||||
public bool TryCopy(ITupleElementViewModel other) {
|
||||
|
|
@ -148,29 +123,38 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
}
|
||||
}
|
||||
|
||||
public class EnumTupleElementViewModel : NumericTupleElementViewModel {
|
||||
public string EnumName { get; }
|
||||
public class EnumTupleElementViewModel : ViewModelCore, ITupleElementViewModel {
|
||||
private readonly TupleSegment seg;
|
||||
private readonly ViewPort viewPort;
|
||||
public string Name => seg.Name;
|
||||
public int BitOffset { get; }
|
||||
public int BitLength => seg.BitWidth;
|
||||
public int Start { get; private set; }
|
||||
public string EnumName => seg.SourceName;
|
||||
|
||||
public IReadOnlyList<string> Options => ArrayRunEnumSegment.GetOptions(ViewPort.Model, EnumName).ToList();
|
||||
public IReadOnlyList<string> Options => ArrayRunEnumSegment.GetOptions(viewPort.Model, EnumName).ToList();
|
||||
|
||||
public int SelectedIndex {
|
||||
get => Content;
|
||||
get => seg.Read(viewPort.Model, Start, BitOffset);
|
||||
set {
|
||||
Content = value;
|
||||
seg.Write(viewPort.Model, viewPort.CurrentChange, Start, BitOffset, value);
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public EnumTupleElementViewModel(ViewPort viewPort, int start, int bitOffset, TupleSegment segment) : base(viewPort, start, bitOffset, segment) {
|
||||
EnumName = segment.SourceName;
|
||||
public EnumTupleElementViewModel(ViewPort viewPort, int start, int bitOffset, TupleSegment segment) {
|
||||
(this.viewPort, Start, BitOffset, seg) = (viewPort, start, BitOffset, segment);
|
||||
}
|
||||
|
||||
public override bool TryCopy(ITupleElementViewModel other) {
|
||||
public bool TryCopy(ITupleElementViewModel other) {
|
||||
if (!(other is EnumTupleElementViewModel that)) return false;
|
||||
if (EnumName != that.EnumName) return false;
|
||||
if (Name != that.Name) return false;
|
||||
if (BitOffset != that.BitOffset) return false;
|
||||
if (BitLength != that.BitLength) return false;
|
||||
|
||||
if (!base.TryCopy(other)) return false;
|
||||
|
||||
Start = that.Start;
|
||||
NotifyPropertyChanged(nameof(Start));
|
||||
NotifyPropertyChanged(nameof(SelectedIndex));
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using HavenSoft.HexManiac.Core;
|
||||
using HavenSoft.HexManiac.Core.Models.Runs;
|
||||
using HavenSoft.HexManiac.Core.ViewModels.Tools;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -124,6 +125,25 @@ namespace HavenSoft.HexManiac.Tests {
|
|||
Assert.Equal(0b1000, Model[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TupleStream_Serialize_ElementsAreWrappedInParenthesis() {
|
||||
Model[10] = 0xFF;
|
||||
Model[11] = 0xFF;
|
||||
ViewPort.Edit("^table[value.|t|a:|b. next.|h]!FFFF ");
|
||||
|
||||
var text = ((IStreamRun)Model.GetNextRun(0)).SerializeRun();
|
||||
|
||||
var lines = text.Split(Environment.NewLine);
|
||||
Assert.Equal("(0 false), 00", lines[0]);
|
||||
Assert.Equal(5, lines.Length);
|
||||
}
|
||||
|
||||
// TODO test that we can serialize tuple enum segments
|
||||
// TODO test that we can read from streams to tuples
|
||||
// TODO check that tablestream dependencies works right for tuple enums
|
||||
// TODO tuple segments with empty names do not appear in the table tool
|
||||
// TODO tuple segments with empty names do not appear in the stream
|
||||
|
||||
private TupleArrayElementViewModel TupleTable => (TupleArrayElementViewModel)ViewPort.Tools.TableTool.Children.Where(child => child is TupleArrayElementViewModel).Single();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user