implement additional UBL features

universal branchlink can now handle selection/cut/paste in cases involving pointers and anchors
This commit is contained in:
haven1433 2022-12-09 08:40:49 -06:00
parent 30026eea8d
commit 8a5e5c793c
2 changed files with 75 additions and 14 deletions

View File

@ -1,13 +1,11 @@
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using IronPython.Compiler;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
namespace HavenSoft.HexManiac.Core.Models.Code {
@ -330,6 +328,7 @@ namespace HavenSoft.HexManiac.Core.Models.Code {
}
foreach (var token in longBranchTokens.Values) {
while ((start + result.Count) % 4 != 0) result.Add(0);
token.Write(start + result.Count, result);
}
@ -393,26 +392,33 @@ namespace HavenSoft.HexManiac.Core.Models.Code {
}
public static bool IsThumbSelection(IDataModel data, int start, int length) {
var firstRun = data.GetNextRun(start);
if (firstRun.Start == start && firstRun is NoInfoRun && !string.IsNullOrEmpty(data.GetAnchorFromAddress(-1, start))) {
var run = data.GetNextRun(start);
if (run.Start == start && run is NoInfoRun && !string.IsNullOrEmpty(data.GetAnchorFromAddress(-1, start))) {
// this is fine, but the next run may be a problem
firstRun = data.GetNextRun(start + 1);
run = data.GetNextRun(start + 1);
}
if (firstRun.Start < start + length) {
return false; // no formatting is allowed
// only pointer formats are allowed
while (run.Start < start + length) {
if (run is not PointerRun) return false;
run = data.GetNextRun(run.Start + run.Length);
}
if (start < 0 || start + length > data.Count) return false;
if (data[start + 1] != 0xB5) return false;
if (start + length == data.Count) return true;
if (start + length + 1 == data.Count || data[start + length + 1] != 0xB5) return false;
return true;
if (start + length + 1 == data.Count) return false;
if (data[start + length] == 0xFF && data[start + length + 1] == 0xFF) return true;
return data[start + length + 1] == 0xB5;
}
public static int GetSelectionLength(IReadOnlyList<byte> data, int start) {
if (start < 0 || start >= data.Count - 1) return -1;
if (data[start + 1] != 0xB5) return -1;
int i = start + 3;
while (i < data.Count && data[i] != 0xB5) i += 2;
while (i < data.Count) {
if (data[i] == 0xB5) break;
if (data.ReadMultiByteValue(i - 1, 2) == 0xFFFF) break;
i += 2;
}
if (i >= data.Count) return -1;
var length = i - 1 - start;
if (length > 1000) return -1;
@ -435,6 +441,7 @@ namespace HavenSoft.HexManiac.Core.Models.Code {
data.ClearFormat(token, start, 1);
// clear remaining bytes
if (length > write.Count + 4) data.ClearFormat(token, start + write.Count + 4, length - write.Count - 4);
for (int i = write.Count + 4; i < length; i++) token.ChangeData(data, start + i, 0xFF);
return true;
}

View File

@ -1,5 +1,6 @@
using HavenSoft.HexManiac.Core;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Code;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.Tools;
using HavenSoft.HexManiac.Core.ViewModels.Visitors;
@ -342,12 +343,65 @@ namespace HavenSoft.HexManiac.Tests {
Assert.Equal(19, ViewPort.ConvertViewPointToAddress(ViewPort.SelectionEnd));
}
// TODO include a 'nop' if needed for alignment
[Fact]
public void LongBranchLink_OddNumberOfCommands_NopInsertedBeforeLongBranchLinkCode() {
ThumbScript = "bl <C00000>; bx r0";
Assert.Equal(0, Model.ReadMultiByteValue(6, 2));
}
// TODO double-click to select should work even if there's a pointer run involved
[Fact]
public void LongBranchLinkOddNumberCommands_StartsAtMultipleOfTwo_NoNopInserted() {
ViewPort.Goto.Execute(2);
ThumbScript = "bl <C00000>; bx r0";
Assert.NotEqual(0, Model.ReadMultiByteValue(8, 2));
}
// TODO double-click to select should work even if there's an named-anchor at the starting address
[Fact]
public void CodeWithPointer_DoubleClick_SelectCodeAndPointer() {
ThumbScript = "push {lr}; ldr r0, =<800000>; pop {pc}";
ViewPort.SelectionStart = ViewPort.ConvertAddressToViewPoint(12);
ThumbScript = "push {lr}";
// TODO cut should paste a UBL even if there's a pointer run involved
ViewPort.Goto.Execute(0);
ViewPort.ExpandSelection(0, 0);
Assert.IsType<PointerRun>(Model.GetNextRun(8));
Assert.Equal(11, ViewPort.ConvertViewPointToAddress(ViewPort.SelectionEnd));
}
[Fact]
public void CodeWithPointer_ReplaceWithLongBranchLink_Success() {
ThumbScript = "push {lr}; ldr r0, =<800000>; pop {pc}";
ViewPort.Goto.Execute(12);
ThumbScript = "push {lr}";
ViewPort.Goto.Execute(0);
ViewPort.ExpandSelection(0, 0);
ViewPort.Cut(FileSystem);
// if we succeeded, then the value at 0x8 should be <null>
Assert.Equal(0, Model.ReadMultiByteValue(8, 4));
}
[Fact]
public void AnchorAtStart_ExpandSelectionToThumbRoutine_SelectionExpanded() {
ThumbScript = "push {lr}; pop {pc}; push {lr}";
ViewPort.Edit("^some.anchor ");
ViewPort.ExpandSelection(0, 0);
Assert.Equal(3, ViewPort.ConvertViewPointToAddress(ViewPort.SelectionEnd));
}
[Fact]
public void ThumbRoutineWithPointer_Cut_RemovePointerFormat() {
ThumbScript = "push {lr}; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; ldr r0, =<800000>; pop {pc}";
ViewPort.ExpandSelection(0, 0);
ViewPort.Cut(FileSystem);
Assert.IsType<OffsetPointerRun>(Model.GetNextRun(8));
Assert.IsNotType<PointerRun>(Model.GetNextRun(0xC));
}
}
}