mirror of
https://github.com/haven1433/HexManiacAdvance.git
synced 2026-06-01 04:53:29 -05:00
bugfixes
* bitfields should support names with the `'` character. * don't let tables/ascii runs go past the end of the file * don't let arrays have zero length per element * allow editing record enums in tables * don't create change tokens when no change is needed
This commit is contained in:
parent
e1da957c30
commit
5543b64e15
|
|
@ -2445,6 +2445,8 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
return new ErrorInfo("An existing anchor starts before the new one ends.");
|
||||
} else if (!name.All(c => char.IsLetterOrDigit(c) || "-._".Contains(c))) { // at this point, the name might have a "-1" on the end, so still allow the dash
|
||||
return new ErrorInfo("Anchor names must contain only letters, numbers, dots, and underscores.");
|
||||
} else if (runToWrite.Start + runToWrite.Length > model.Count) {
|
||||
return new ErrorInfo("Anchor format must not go past the end of the file.");
|
||||
} else {
|
||||
return ErrorInfo.NoError;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -528,6 +528,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
|
|||
ElementContent = ParseSegments(segments, data);
|
||||
if (ElementContent.Count == 0) throw new ArrayRunParseException("Array Content must not be empty.");
|
||||
ElementLength = ElementContent.Sum(e => e.Length);
|
||||
if (ElementLength == 0) throw new ArrayRunParseException("Array Content Length must not be zero.");
|
||||
|
||||
FormatMatchFlags flags = default;
|
||||
if (ElementContent.Count == 1) flags |= FormatMatchFlags.IsSingleSegment;
|
||||
|
|
@ -1243,6 +1244,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
|
|||
segments = segments.Substring(nameEnd);
|
||||
var (format, formatLength, segmentLength) = ExtractSingleFormat(segments, model);
|
||||
if (name == string.Empty && format != ElementContentType.Splitter) throw new ArrayRunParseException("expected name, but none was found: " + segments);
|
||||
if (format == ElementContentType.PCS && segmentLength < 1) throw new ArrayRunParseException("Cannot have 0-length text: " + name);
|
||||
|
||||
// check to see if a name or length is part of the format
|
||||
if (format == ElementContentType.Integer && segments.Length > formatLength && segments[formatLength] != ' ') {
|
||||
|
|
|
|||
|
|
@ -2601,7 +2601,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
|
|||
|
||||
// normal case: whether or not to accept the edit depends on the existing cell format
|
||||
var dataIndex = scroll.ViewPointToDataIndex(point);
|
||||
var completeEditOperation = new CompleteCellEdit(Model, scroll, dataIndex, underEdit.CurrentText, history.CurrentChange);
|
||||
var completeEditOperation = new CompleteCellEdit(Model, scroll, dataIndex, underEdit.CurrentText, () => history.CurrentChange);
|
||||
using (ModelCacheScope.CreateScope(Model)) {
|
||||
(underEdit.OriginalFormat ?? Undefined.Instance).Visit(completeEditOperation, element.Value);
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
|
|||
private void GenerateOptions(IDataFormatInstance format) {
|
||||
var arrayRun = (ITableRun)Model.GetNextRun(format.Source);
|
||||
var offsets = arrayRun.ConvertByteOffsetToArrayOffset(format.Source);
|
||||
var segment = (IHasOptions)arrayRun.ElementContent[offsets.SegmentIndex];
|
||||
var contentSegment = arrayRun.ElementContent[offsets.SegmentIndex];
|
||||
if (contentSegment is ArrayRunRecordSegment record) contentSegment = record.CreateConcrete(Model, format.Source);
|
||||
var segment = (IHasOptions)contentSegment;
|
||||
var allOptions = segment.GetOptions(Model).Where(option => option != null).Select(option => option + " ");
|
||||
Result = AutoCompleteSelectionItem.Generate(allOptions.Where(option => option.MatchesPartial(InputText, onlyCheckLettersAndDigits: true)), -1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
|
|||
private readonly IDataModel Model;
|
||||
private readonly int memoryLocation;
|
||||
private readonly string CurrentText;
|
||||
private readonly ModelDelta CurrentChange;
|
||||
private readonly Func<ModelDelta> currentChange;
|
||||
private readonly ScrollRegion scroll;
|
||||
|
||||
public bool Result { get; private set; } // if true, the edit was completed correctly
|
||||
|
|
@ -27,12 +27,14 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
|
|||
// and refresh the one cell (along with any other UnderEdit cells)
|
||||
// if result is true and this _is_ null, then the entire screen needs to be refreshed.
|
||||
|
||||
public CompleteCellEdit(IDataModel model, ScrollRegion scroll, int memoryLocation, string currentText, ModelDelta currentChange) {
|
||||
private ModelDelta CurrentChange => currentChange();
|
||||
|
||||
public CompleteCellEdit(IDataModel model, ScrollRegion scroll, int memoryLocation, string currentText, Func<ModelDelta> currentChange) {
|
||||
Model = model;
|
||||
this.scroll = scroll;
|
||||
this.memoryLocation = memoryLocation;
|
||||
CurrentText = currentText;
|
||||
CurrentChange = currentChange;
|
||||
this.currentChange = currentChange;
|
||||
|
||||
NewDataIndex = memoryLocation;
|
||||
}
|
||||
|
|
@ -585,7 +587,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
|
|||
string sanitizedText = CurrentText.Replace(')', ' ');
|
||||
var array = (ITableRun)Model.GetNextRun(memoryLocation);
|
||||
var offsets = array.ConvertByteOffsetToArrayOffset(memoryLocation);
|
||||
var segment = (ArrayRunEnumSegment)array.ElementContent[offsets.SegmentIndex];
|
||||
var contentSegment = array.ElementContent[offsets.SegmentIndex];
|
||||
if (contentSegment is ArrayRunRecordSegment record) contentSegment = record.CreateConcrete(Model, memoryLocation);
|
||||
var segment = (ArrayRunEnumSegment)contentSegment;
|
||||
if (segment.TryParse(Model, sanitizedText, out int value)) {
|
||||
Model.WriteMultiByteValue(offsets.SegmentStart, segment.Length, CurrentChange, value);
|
||||
NewDataIndex = offsets.SegmentStart + segment.Length;
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
|
|||
|
||||
public void Visit(IntegerEnum integer, byte data) {
|
||||
Result = integer.CanStartWithCharacter(Input) ||
|
||||
".'~|,_&%)".Contains(Input) ||
|
||||
".'~|,_&%()".Contains(Input) ||
|
||||
char.IsWhiteSpace(Input);
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
|
|||
}
|
||||
|
||||
public void Visit(BitArray array, byte data) {
|
||||
Result = char.IsLetterOrDigit(Input) || Input.IsAny('"', '-', ' ');
|
||||
Result = char.IsLetterOrDigit(Input) || "?-\" .'~|,_&%)".Contains(Input);
|
||||
}
|
||||
|
||||
public void Visit(MatchedWord word, byte data) => Visit((None)null, data);
|
||||
|
|
|
|||
|
|
@ -980,6 +980,13 @@ namespace HavenSoft.HexManiac.Tests {
|
|||
Assert.Empty(Errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoChanges_StartChangeThenCancel_NoSaveOption() {
|
||||
ViewPort.Edit("<");
|
||||
|
||||
Assert.False(ViewPort.Save.CanExecute(FileSystem));
|
||||
}
|
||||
|
||||
private void StandardSetup(out byte[] data, out PokemonModel model, out ViewPort viewPort) {
|
||||
data = new byte[0x200];
|
||||
model = new PokemonModel(data);
|
||||
|
|
|
|||
|
|
@ -757,6 +757,18 @@ namespace HavenSoft.HexManiac.Tests {
|
|||
Assert.IsAssignableFrom<ITableRun>(Model.GetNextRun(0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TableWithZeroLengthString_Error() {
|
||||
ViewPort.Edit("^table[text\"\"0]1 ");
|
||||
Assert.Single(Errors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Table_OnlyZeroLengthSegments_Error() {
|
||||
ViewPort.Edit("^table[field|=() ]1 ");
|
||||
Assert.Single(Errors);
|
||||
}
|
||||
|
||||
private void HackTextConverter(string game) {
|
||||
var converter = new PCSConverter(game);
|
||||
var property = Model.GetType().GetProperty(nameof(Model.TextConverter));
|
||||
|
|
|
|||
|
|
@ -357,5 +357,15 @@ namespace HavenSoft.HexManiac.Tests {
|
|||
|
||||
Assert.Equal(10, ViewPort.DataLength);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("^table[a: b: c: d: e:]3 ")]
|
||||
[InlineData("^text`asc`20 ")]
|
||||
public void EndOfModel_LongRunFormat_Error(string format) {
|
||||
ViewPort.Goto.Execute(0x1F0);
|
||||
ViewPort.Edit(format);
|
||||
Assert.Single(Errors);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using HavenSoft.HexManiac.Core;
|
||||
using HavenSoft.HexManiac.Core.Models;
|
||||
using HavenSoft.HexManiac.Core.Models.Runs;
|
||||
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
|
||||
using HavenSoft.HexManiac.Core.ViewModels.Tools;
|
||||
using HavenSoft.HexManiac.Core.ViewModels.Visitors;
|
||||
using System;
|
||||
|
|
@ -317,5 +318,15 @@ DefaultHash = '''0BEEDA92'''
|
|||
Assert.Equal("name2", list[2]);
|
||||
Assert.Equal(3, list.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ListWithApostrophe_TypeIntoBitFieldFromList_AcceptsApostrophe() {
|
||||
Model.SetList("list", new[] { "Item", "Arby's", "Content" });
|
||||
ViewPort.Edit("^table[content|b[]list]1 ");
|
||||
|
||||
ViewPort.Edit("arby'");
|
||||
|
||||
Assert.IsType<UnderEdit>(ViewPort[0, 0].Format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1322,6 +1322,16 @@ namespace HavenSoft.HexManiac.Tests {
|
|||
Assert.Equal(0x108, ViewPort.ConvertViewPointToAddress(ViewPort.SelectionStart));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SwitchFormat_EditEnum_NoError() {
|
||||
CreateTextTable("names", 0x100, "adam", "bob", "carl", "dave");
|
||||
ViewPort.Edit("@000 ^table[input: arg:|s=input(0=names)]2 ");
|
||||
|
||||
ViewPort.Edit("@002 carl ");
|
||||
|
||||
Assert.Equal(2, Model[2]);
|
||||
}
|
||||
|
||||
private void ArrangeTrainerPokemonTeamData(byte structType, byte pokemonCount, int trainerCount) {
|
||||
CreateTextTable(HardcodeTablesModel.PokemonNameTable, 0x180, "ABCDEFGHIJKLMNOP".Select(c => c.ToString()).ToArray());
|
||||
CreateTextTable(HardcodeTablesModel.MoveNamesTable, 0x1B0, "qrstuvwxyz".Select(c => c.ToString()).ToArray());
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user