Merge pull request #45 from haven1433/dev

Bugfix: Tutor Expansion
This commit is contained in:
haven1433 2019-12-15 15:06:59 -06:00 committed by GitHub
commit 5ef7df15c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 6 deletions

View File

@ -21,7 +21,7 @@ lt=1011 @ < less than (signed)
gt=1100 @ > greater than (signed)
le=1101 @ <= less than or equal (signed)
al=1110 @ always
ne=1111 @ never
nv=1111 @ never
@ opcodes and args
0000000000000000 | nop @ does nothing

View File

@ -259,7 +259,9 @@ namespace HavenSoft.HexManiac.Core.Models {
// wild pokemon
source = Find("0348048009E00000FFFF0000");
string table(int length) => $"<[rate:: list<[low. high. species:pokenames]{length}>]1>";
AddTable(source, WildTableName, $"[bank. map. unused: grass{table(12)} surf{table(5)} tree{table(5)} fish{table(10)}]");
using (ModelCacheScope.CreateScope(this)) { // cares about pokenames
AddTable(source, WildTableName, $"[bank. map. unused: grass{table(12)} surf{table(5)} tree{table(5)} fish{table(10)}]");
}
// specials
switch (gameCode) {

View File

@ -53,6 +53,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.QuickEditItems {
var (getTutorMove, canPokemonLearnTutorMove, getTutorMove_Length, canPokemonLearnTutorMove_Length) = GetOffsets(viewPort, gameCode);
var specialsAddress = model.GetAddressFromAnchor(token, -1, HardcodeTablesModel.SpecialsTable);
var tutorSpecial = model.ReadPointer(specialsAddress + 397 * 4); // Emerald tutors is actually special 477, but we don't need to edit it so it doesn't matter.
tutorSpecial -= 1; // the pointer is to thumb code, so it's off by one.
var tutormoves = model.GetAddressFromAnchor(viewPort.CurrentChange, -1, MoveTutors);
var tutorcompatibility = model.GetAddressFromAnchor(viewPort.CurrentChange, -1, TutorCompatibility);
@ -139,11 +140,18 @@ namespace HavenSoft.HexManiac.Core.ViewModels.QuickEditItems {
private void UpdateRoutine_TutorSpecial(ViewPort viewPort, int tutorSpecial, string gameCode) {
if (gameCode == Emerald) return; // Emerald's tutor special doesn't have a limiter, so it doesn't need to be updated.
// change the code from 'branch-hi' to 'branch-never' so that the standard codepath is taken for tutorID>14
const int instructionIndex = 5;
const int instructionWidth = 2;
// change the code from 'branch-hi' to 'nop' so that the standard codepath is taken for tutorID>14
int instructionIndex = 5;
int instructionWidth = 2;
var branchOffset = tutorSpecial + instructionIndex * instructionWidth;
viewPort.CurrentChange.ChangeData(viewPort.Model, branchOffset, 0xDF);
viewPort.Model.WriteMultiByteValue(branchOffset, 2, viewPort.CurrentChange, 0x0000);
// a separate routine several layers down also needs to be updated
// change the code from 'branch-hi' to 'nop' so that the standard codepath is taken for tutorID>14
instructionIndex = 20;
branchOffset = (gameCode == FireRed) ? 0x11F430 : 0x11F408; // FireRed / LeafGreen
branchOffset += instructionIndex * instructionWidth;
viewPort.Model.WriteMultiByteValue(branchOffset, 2, viewPort.CurrentChange, 0x0000);
}
}
}

View File

@ -9,6 +9,7 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
@ -899,6 +900,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
results.AddRange(Search(searchBytes).Select(result => (result, result + 3)));
}
// it might be a bl command
if (cleanedSearchString.StartsWith("BL ") && cleanedSearchString.Contains("<") && cleanedSearchString.EndsWith(">")) {
results.AddRange(FindBranchLink(cleanedSearchString));
}
// attempt to parse the search string fully
if (TryParseSearchString(searchBytes, cleanedSearchString, errorOnParseError: results.Count == 0)) {
// find matches
@ -915,6 +921,46 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
return results;
}
private IEnumerable<(int start, int end)> FindBranchLink(string command) {
var addressStart = command.IndexOf(" <") + 2;
var addressEnd = command.LastIndexOf(">");
if (addressEnd < addressStart) yield break;
var addressText = command.Substring(addressStart, addressEnd - addressStart);
if (!int.TryParse(addressText, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out int address)) {
address = Model.GetAddressFromAnchor(CurrentChange, -1, addressText);
if (address < 0 || address >= Model.Count) yield break;
}
// I want to know, for any given point in the raw data, if it's possible a branch-link command pointing to `address`
// branch link commands are always 4 bytes and have the following format:
// 11111 #11 11110 #11, where #=pc+#*2+4
// note that this command is 4 bytes long, stored byte reversed. So in the data, it's:
// 8 bits: bits 11-18 of a 22 bit signed offset
// 8 bits:
// the low 3 bits are bits 19-21 of a 22 bit signed offset
// the high 5 bits are always 11110
// 8 bits: bits 0-7 of a 22 bit signed offset
// 8 bits:
// the low 3 bits are bits 8-10 of a 22 bit signed offset
// the high 5 bits are always 11111
// the command is always 2-byte aligned
//
// bit order is really weird (11-18, 19-21, 0-7, 8-10) because BL is made of **2** instructions,
// and each instruction is stored little-endian
// start as early as possible in the file: maximum offset, or offset for source=0
int offset = Math.Min(0b0111111111111111111111, (address - 4) / 2);
for (; true; offset--) { // traveling down the offsets means traveling up the source options
int source = address - 4 - offset * 2;
if (source + 4 > Model.RawData.Length) break;
if (Model.RawData[source + 2] != (byte)offset) continue; // check source+2 first because it's the simplest, and thus fastest
if (Model.RawData[source + 0] != (byte)(offset >> 11)) continue;
if (Model.RawData[source + 3] != (0b11111000 | (0b111 & offset >> 8))) continue;
if (Model.RawData[source + 1] != (0b11110000 | (0b111 & offset >> 19))) continue;
yield return (source, source + 3);
}
}
private IEnumerable<(int start, int end)> FindUnquotedText(string cleanedSearchString, List<ISearchByte> searchBytes) {
var pcsBytes = PCSString.Convert(cleanedSearchString);
pcsBytes.RemoveAt(pcsBytes.Count - 1); // remove the 0xFF that was added, since we're searching for a string segment instead of a whole string.

View File

@ -2,6 +2,7 @@
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
@ -335,6 +336,21 @@ namespace HavenSoft.HexManiac.Tests {
}
}
[Fact]
public void CanSearchForBranchLink() {
var test = new BaseViewModelTestClass();
var command1 = test.ViewPort.Tools.CodeTool.Parser.Compile(test.Model, 0x0010, "bl <000060>").ToArray();
var command2 = test.ViewPort.Tools.CodeTool.Parser.Compile(test.Model, 0x0100, "bl <000060>").ToArray();
Array.Copy(command1, 0, test.Model.RawData, 0x0010, command1.Length);
Array.Copy(command2, 0, test.Model.RawData, 0x0100, command2.Length);
var results = test.ViewPort.Find("bl <000060>").ToList();
Assert.Equal(2, results.Count);
Assert.Equal(0x010, results[0].start);
Assert.Equal(0x100, results[1].start);
}
private void StandardSetup(out byte[] data, out PokemonModel model, out ViewPort viewPort) {
data = Enumerable.Repeat((byte)0xFF, 0x200).ToArray();
model = new PokemonModel(data);