mirror of
https://github.com/haven1433/HexManiacAdvance.git
synced 2026-05-27 18:42:45 -05:00
improve search so that it works with strings
This commit is contained in:
parent
258d5c4562
commit
b079ca65ee
|
|
@ -46,6 +46,7 @@
|
|||
<Compile Include="Models\FormattedRun.cs" />
|
||||
<Compile Include="Models\DataFormatModel.cs" />
|
||||
<Compile Include="Models\PCSString.cs" />
|
||||
<Compile Include="Models\SearchByte.cs" />
|
||||
<Compile Include="NumericExtensions.cs" />
|
||||
<Compile Include="Models\Enums.cs" />
|
||||
<Compile Include="Models\IFileSystem.cs" />
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ namespace HavenSoft.Gen3Hex.Core.Models {
|
|||
}
|
||||
|
||||
public static List<byte> Convert(string input) {
|
||||
if (input.StartsWith("\"")) input = input.Substring(1); // trim leading " at start of string
|
||||
var result = new List<byte>();
|
||||
|
||||
int index = 0;
|
||||
|
|
|
|||
30
src/Gen3Hex.Core/Models/SearchByte.cs
Normal file
30
src/Gen3Hex.Core/Models/SearchByte.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace HavenSoft.Gen3Hex.Core.Models {
|
||||
public interface ISearchByte {
|
||||
bool Match(byte value);
|
||||
}
|
||||
public class SearchByte : ISearchByte {
|
||||
private readonly byte value;
|
||||
public SearchByte(int value) => this.value = (byte)value;
|
||||
public static explicit operator SearchByte (byte value) => new SearchByte(value);
|
||||
public bool Match(byte value) => value == this.value;
|
||||
}
|
||||
public class PCSSearchByte : ISearchByte {
|
||||
private readonly byte match1, match2;
|
||||
public PCSSearchByte(int value) {
|
||||
match1 = (byte)value;
|
||||
match2 = match1;
|
||||
if (PCSString.PCS[match1] == null) return;
|
||||
var valueAsChar = PCSString.PCS[match1][0];
|
||||
if (char.IsUpper(valueAsChar)) {
|
||||
Debug.Assert(IndexOf(PCSString.PCS, "a") - IndexOf(PCSString.PCS, "A") == 0x1A);
|
||||
match2 += 0x1A;
|
||||
}
|
||||
}
|
||||
public bool Match(byte value) => value == match1 || value == match2;
|
||||
private static int IndexOf(IReadOnlyList<string> pcs, string value) => Enumerable.Range(0, 0x100).Single(i => pcs[i] == value);
|
||||
}
|
||||
}
|
||||
|
|
@ -344,23 +344,40 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
|
|||
|
||||
public IReadOnlyList<int> Find(string rawSearch) {
|
||||
var results = new List<int>();
|
||||
var cleanedSearchString = rawSearch.Replace(" ", string.Empty).ToUpper();
|
||||
var searchBytes = new List<byte>();
|
||||
var cleanedSearchString = rawSearch.ToUpper();
|
||||
var searchBytes = new List<ISearchByte>();
|
||||
var hex = "0123456789ABCDEF";
|
||||
|
||||
// precheck: it might be a string with no quotes, we should check for matches for that.
|
||||
if (cleanedSearchString.Length > 3 && !cleanedSearchString.Contains('"')) {
|
||||
var pcsBytes = PCSString.Convert(cleanedSearchString);
|
||||
searchBytes.AddRange(pcsBytes.Select(b => new PCSSearchByte(b)));
|
||||
for (int i = 0; i < Model.Count - searchBytes.Count; i++) {
|
||||
for (int j = 0; j < searchBytes.Count; j++) {
|
||||
if (!searchBytes[j].Match(Model[i + j])) break;
|
||||
if (j == searchBytes.Count - 1) results.Add(i);
|
||||
}
|
||||
}
|
||||
searchBytes.Clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < cleanedSearchString.Length;) {
|
||||
if (cleanedSearchString[i] == ' ') {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (cleanedSearchString[i] == '<') {
|
||||
var pointerEnd = cleanedSearchString.IndexOf('>', i);
|
||||
if (pointerEnd == -1) { OnError(this, "Search mismatch: no closing >"); return results; }
|
||||
var pointerContents = cleanedSearchString.Substring(i + 1, pointerEnd - i - 2);
|
||||
var address = Model.GetAddressFromAnchor(-1, pointerContents);
|
||||
if (address != Pointer.NULL) {
|
||||
searchBytes.Add((byte)(address >> 0));
|
||||
searchBytes.Add((byte)(address >> 8));
|
||||
searchBytes.Add((byte)(address >> 16));
|
||||
searchBytes.Add(0x08);
|
||||
searchBytes.Add((SearchByte)(address >> 0));
|
||||
searchBytes.Add((SearchByte)(address >> 8));
|
||||
searchBytes.Add((SearchByte)(address >> 16));
|
||||
searchBytes.Add((SearchByte)0x08);
|
||||
} else if (pointerContents.All(hex.Contains) && pointerContents.Length <= 6) {
|
||||
searchBytes.AddRange(Parse(pointerContents).Reverse().Append((byte)0x08));
|
||||
searchBytes.AddRange(Parse(pointerContents).Reverse().Append((byte)0x08).Select(b => (SearchByte)b));
|
||||
} else {
|
||||
OnError(this, $"Could not parse pointer <{pointerContents}>");
|
||||
return results;
|
||||
|
|
@ -368,18 +385,31 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
|
|||
i = pointerEnd + 1;
|
||||
continue;
|
||||
}
|
||||
if (cleanedSearchString[i] == '"') {
|
||||
var endIndex = cleanedSearchString.IndexOf('"', i + 1);
|
||||
while (endIndex > i && cleanedSearchString[endIndex - 1] == '\\') endIndex = cleanedSearchString.IndexOf('"', endIndex + 1);
|
||||
if (endIndex > i) {
|
||||
var pcsBytes = PCSString.Convert(cleanedSearchString.Substring(i, endIndex + 1 - i));
|
||||
i = endIndex + 1;
|
||||
if (i == cleanedSearchString.Length) pcsBytes.RemoveAt(pcsBytes.Count - 1);
|
||||
searchBytes.AddRange(pcsBytes.Select(b => new PCSSearchByte(b)));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (cleanedSearchString.Length >= i + 2 && cleanedSearchString.Substring(i, 2).All(hex.Contains)) {
|
||||
searchBytes.AddRange(Parse(cleanedSearchString.Substring(i, 2)));
|
||||
searchBytes.AddRange(Parse(cleanedSearchString.Substring(i, 2)).Select(b => (SearchByte)b));
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
OnError(this, $"Could not parse search term {cleanedSearchString.Substring(i)}");
|
||||
if (results.Count == 0) {
|
||||
OnError(this, $"Could not parse search term {cleanedSearchString.Substring(i)}");
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Model.Count - searchBytes.Count; i++) {
|
||||
for (int j = 0; j < searchBytes.Count; j++) {
|
||||
if (Model[i + j] != searchBytes[j]) break;
|
||||
if (!searchBytes[j].Match(Model[i + j])) break;
|
||||
if (j == searchBytes.Count - 1) results.Add(i);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -231,7 +231,31 @@ namespace HavenSoft.Gen3Hex.Tests {
|
|||
Assert.Equal("^bob\"\" \"Hello World!\"", fileSystem.CopyText);
|
||||
}
|
||||
|
||||
// TODO Find
|
||||
[Fact]
|
||||
public void FindForStringsIsNotCaseSensitive() {
|
||||
var buffer = Enumerable.Repeat((byte)0xFF, 0x200).ToArray();
|
||||
for (int i = 0; i < 0x10; i++) buffer[i] = 0x00;
|
||||
var model = new PointerAndStringModel(buffer);
|
||||
var viewPort = new ViewPort(new LoadedFile("test.txt", buffer), model) { Width = 0x10, Height = 0x10 };
|
||||
viewPort.Edit("^bob\"\" \"Text and BULBASAUR!\"");
|
||||
|
||||
var results = viewPort.Find("\"bulbasaur\"");
|
||||
Assert.Single(results);
|
||||
Assert.Equal(9, results[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindForStringsWorksWithoutQuotes() {
|
||||
var buffer = Enumerable.Repeat((byte)0xFF, 0x200).ToArray();
|
||||
for (int i = 0; i < 0x10; i++) buffer[i] = 0x00;
|
||||
var model = new PointerAndStringModel(buffer);
|
||||
var viewPort = new ViewPort(new LoadedFile("test.txt", buffer), model) { Width = 0x10, Height = 0x10 };
|
||||
viewPort.Edit("^bob\"\" \"Text and BULBASAUR!\"");
|
||||
|
||||
var results = viewPort.Find("bulbasaur");
|
||||
Assert.Single(results);
|
||||
Assert.Equal(9, results[0]);
|
||||
}
|
||||
|
||||
// TODO undo/redo
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user