diff --git a/src/HexManiac.Core/HexManiac.Core.csproj b/src/HexManiac.Core/HexManiac.Core.csproj
index 4b48dca3..597219fc 100644
--- a/src/HexManiac.Core/HexManiac.Core.csproj
+++ b/src/HexManiac.Core/HexManiac.Core.csproj
@@ -59,6 +59,7 @@
+
diff --git a/src/HexManiac.Core/Models/AutoSearchModel.cs b/src/HexManiac.Core/Models/AutoSearchModel.cs
index d5136de0..975d6fd7 100644
--- a/src/HexManiac.Core/Models/AutoSearchModel.cs
+++ b/src/HexManiac.Core/Models/AutoSearchModel.cs
@@ -110,10 +110,10 @@ namespace HavenSoft.HexManiac.Core.Models {
var pokenames = GetNextRun(GetAddressFromAnchor(noChangeDelta, -1, EggMoveRun.PokemonNameTable)) as ArrayRun;
if (pokenames != null && TrySearch(this, noChangeDelta, format, out var pokestatdata, run => run.PointerSources.Count > 5)) {
if (pokestatdata.ElementCount < pokenames.ElementCount) {
- pokenames = pokenames.Append(pokestatdata.ElementCount - pokenames.ElementCount);
+ pokenames = pokenames.Append(noChangeDelta, pokestatdata.ElementCount - pokenames.ElementCount);
ObserveAnchorWritten(noChangeDelta, EggMoveRun.PokemonNameTable, pokenames);
} else if (pokestatdata.ElementCount > pokenames.ElementCount) {
- pokestatdata = pokestatdata.Append(pokenames.ElementCount - pokestatdata.ElementCount);
+ pokestatdata = pokestatdata.Append(noChangeDelta, pokenames.ElementCount - pokestatdata.ElementCount);
}
ObserveAnchorWritten(noChangeDelta, "pokestats", pokestatdata);
diff --git a/src/HexManiac.Core/Models/HardcodeTablesModel.cs b/src/HexManiac.Core/Models/HardcodeTablesModel.cs
index fbd36b1b..9d0d61c6 100644
--- a/src/HexManiac.Core/Models/HardcodeTablesModel.cs
+++ b/src/HexManiac.Core/Models/HardcodeTablesModel.cs
@@ -13,6 +13,7 @@ namespace HavenSoft.HexManiac.Core.Models {
/// Lengths of some tables are still calculated dynamically based on best-fit, so operations like adding pokemon from a separate tool should still be picked up correctly.
///
public class HardcodeTablesModel : PokemonModel {
+ public const string ItemsTableName = "items";
private readonly string gameCode;
private readonly ModelDelta noChangeDelta = new NoDataChangeDeltaModel();
@@ -111,7 +112,7 @@ namespace HavenSoft.HexManiac.Core.Models {
case FireRed: case LeafGreen: case Emerald: source = 0x0001BC; break;
case Ruby: case Sapphire: source = 0x010B64; break;
}
- var format = $"[hp. attack. def. speed. spatk. spdef. type1.types type2.types catchRate. baseExp. evs: item1:items item2:items genderratio. steps2hatch. basehappiness. growthrate. egg1. egg2. ability1.abilitynames ability2.abilitynames runrate. unknown. padding:]{EggMoveRun.PokemonNameTable}";
+ var format = $"[hp. attack. def. speed. spatk. spdef. type1.types type2.types catchRate. baseExp. evs: item1:{ItemsTableName} item2:{ItemsTableName} genderratio. steps2hatch. basehappiness. growthrate. egg1. egg2. ability1.abilitynames ability2.abilitynames runrate. unknown. padding:]{EggMoveRun.PokemonNameTable}";
AddTable(source, "pokestats", format);
// items
@@ -120,7 +121,7 @@ namespace HavenSoft.HexManiac.Core.Models {
case Ruby: case Sapphire: source = 0x0A98F0; break;
}
format = $"[name\"\"14 index: price: holdeffect: description<{PCSRun.SharedFormatString}> keyitemvalue. bagkeyitem. pocket. type. fieldeffect<> battleusage:: battleeffect<> battleextra::]";
- AddTable(source, "items", format);
+ AddTable(source, ItemsTableName, format);
// movedata
switch (gameCode) {
@@ -184,7 +185,7 @@ namespace HavenSoft.HexManiac.Core.Models {
case Ruby: case Sapphire: source = -1; break;
}
source = GetNextRun(source).Start;
- AddTable(source, "itemimages", "[image<> palette<>]items");
+ AddTable(source, "itemimages", $"[image<> palette<>]{ItemsTableName}");
// trainer teams
switch (gameCode) {
@@ -192,7 +193,7 @@ namespace HavenSoft.HexManiac.Core.Models {
case Emerald: source = 0x03587C; break;
case Ruby: case Sapphire: source = 0x00D890; break;
}
- AddTable(source, "trainerdata", "[structType.4 class.trainerclassnames introMusic. sprite. name\"\"12 item1:items item2:items item3:items item4:items doubleBattle:: ai:: pokemonCount:: pokemon<>]");
+ AddTable(source, "trainerdata", $"[structType.4 class.trainerclassnames introMusic. sprite. name\"\"12 item1:{ItemsTableName} item2:{ItemsTableName} item3:{ItemsTableName} item4:{ItemsTableName} doubleBattle:: ai:: pokemonCount:: pokemon<`tpt`>]");
}
private void DecodeStreams() {
@@ -223,7 +224,7 @@ namespace HavenSoft.HexManiac.Core.Models {
if (interruptingRun.Start < destination && interruptingRun is ArrayRun array) {
var elementLength = array.ElementLength;
var elementCount = (destination - array.Start) / array.ElementLength;
- array = array.Append(elementCount - array.ElementCount);
+ array = array.Append(noChangeDelta, elementCount - array.ElementCount);
ObserveAnchorWritten(noChangeDelta, GetAnchorFromAddress(-1, array.Start), array);
}
diff --git a/src/HexManiac.Core/Models/IDataModel.cs b/src/HexManiac.Core/Models/IDataModel.cs
index 6a66beb4..0b5e2932 100644
--- a/src/HexManiac.Core/Models/IDataModel.cs
+++ b/src/HexManiac.Core/Models/IDataModel.cs
@@ -30,7 +30,7 @@ namespace HavenSoft.HexManiac.Core.Models {
bool TryGetUsefulHeader(int address, out string header);
- bool IsAtEndOfArray(int dataIndex, out ArrayRun arrayRun); // is this byte the first one after the end of an array run? (also return true if the array is length 0 and starts right here)
+ bool IsAtEndOfArray(int dataIndex, out ITableRun tableRun); // is this byte the first one after the end of a table run? (also return true if the table is length 0 and starts right here)
void ObserveRunWritten(ModelDelta changeToken, IFormattedRun run);
void ObserveAnchorWritten(ModelDelta changeToken, string anchorName, IFormattedRun run);
@@ -101,7 +101,7 @@ namespace HavenSoft.HexManiac.Core.Models {
public virtual bool TryGetUsefulHeader(int address, out string header) { header = null; return false; }
- public abstract bool IsAtEndOfArray(int dataIndex, out ArrayRun arrayRun);
+ public abstract bool IsAtEndOfArray(int dataIndex, out ITableRun tableRun);
public virtual void Load(byte[] newData, StoredMetadata metadata) => RawData = newData;
@@ -251,27 +251,30 @@ namespace HavenSoft.HexManiac.Core.Models {
return format;
}
- public static ErrorInfo CompleteArrayExtension(this IDataModel model, ModelDelta changeToken, ref ArrayRun arrayRun) {
- var currentArrayName = model.GetAnchorFromAddress(-1, arrayRun.Start);
+ public static ErrorInfo CompleteArrayExtension(this IDataModel model, ModelDelta changeToken, ref ITableRun table) {
+ var currentArrayName = model.GetAnchorFromAddress(-1, table.Start);
- var visitedNames = new List() { model.GetAnchorFromAddress(-1, arrayRun.Start) };
- var visitedAddress = new List() { arrayRun.Start };
+ var visitedNames = new List() { model.GetAnchorFromAddress(-1, table.Start) };
+ var visitedAddress = new List() { table.Start };
- while (arrayRun.LengthFromAnchor != string.Empty) {
- if (visitedNames.Contains(arrayRun.LengthFromAnchor)) {
- // We kept going up the chain of tables but didn't find a top table. table length definitions are circular.
- return new ErrorInfo($"Could not extend table safely. Table length has a circular dependency involving {arrayRun.LengthFromAnchor}.");
+ if (table is ArrayRun arrayRun) {
+ while (arrayRun.LengthFromAnchor != string.Empty) {
+ if (visitedNames.Contains(arrayRun.LengthFromAnchor)) {
+ // We kept going up the chain of tables but didn't find a top table. table length definitions are circular.
+ return new ErrorInfo($"Could not extend table safely. Table length has a circular dependency involving {arrayRun.LengthFromAnchor}.");
+ }
+
+ var address = model.GetAddressFromAnchor(new NoDataChangeDeltaModel(), -1, arrayRun.LengthFromAnchor);
+ visitedNames.Add(arrayRun.LengthFromAnchor);
+ visitedAddress.Add(address);
+ arrayRun = (ArrayRun)model.GetNextRun(address);
}
-
- var address = model.GetAddressFromAnchor(new NoDataChangeDeltaModel(), -1, arrayRun.LengthFromAnchor);
- visitedNames.Add(arrayRun.LengthFromAnchor);
- visitedAddress.Add(address);
- arrayRun = (ArrayRun)model.GetNextRun(address);
+ table = arrayRun;
}
- ExtendArrayAndChildren(model, changeToken, arrayRun);
+ ExtendTableAndChildren(model, changeToken, table);
- arrayRun = (ArrayRun)model.GetNextRun(model.GetAddressFromAnchor(new NoDataChangeDeltaModel(), -1, currentArrayName));
+ table = (ITableRun)model.GetNextRun(model.GetAddressFromAnchor(new NoDataChangeDeltaModel(), -1, currentArrayName));
var changedNames = new List();
for (int i = 0; i < visitedNames.Count; i++) {
@@ -284,9 +287,9 @@ namespace HavenSoft.HexManiac.Core.Models {
return new ErrorInfo($"Tables {all} were moved. Pointers have been updated.", isWarningLevel: true);
}
- private static void ExtendArrayAndChildren(IDataModel model, ModelDelta changeToken, ArrayRun array) {
- var newRun = (ArrayRun)model.RelocateForExpansion(changeToken, array, array.Length + array.ElementLength);
- newRun = newRun.Append(1);
+ private static void ExtendTableAndChildren(IDataModel model, ModelDelta changeToken, ITableRun array) {
+ var newRun = (ITableRun)model.RelocateForExpansion(changeToken, array, array.Length + array.ElementLength);
+ newRun = newRun.Append(changeToken, 1);
model.ObserveRunWritten(changeToken, newRun);
}
@@ -314,7 +317,7 @@ namespace HavenSoft.HexManiac.Core.Models {
public override string GetAnchorFromAddress(int requestSource, int destination) => string.Empty;
public override IFormattedRun GetNextRun(int dataIndex) => NoInfoRun.NullRun;
public override IFormattedRun GetNextAnchor(int dataIndex) => NoInfoRun.NullRun;
- public override bool IsAtEndOfArray(int dataIndex, out ArrayRun arrayRun) { arrayRun = null; return false; }
+ public override bool IsAtEndOfArray(int dataIndex, out ITableRun tableRun) { tableRun = null; return false; }
public override void ObserveRunWritten(ModelDelta changeToken, IFormattedRun run) { }
public override void ObserveAnchorWritten(ModelDelta changeToken, string anchorName, IFormattedRun run) { }
public override void MassUpdateFromDelta(IReadOnlyDictionary runsToRemove, IReadOnlyDictionary runsToAdd, IReadOnlyDictionary namesToRemove, IReadOnlyDictionary namesToAdd, IReadOnlyDictionary unmappedPointersToRemove, IReadOnlyDictionary unmappedPointersToAdd, IReadOnlyDictionary matchedWordsToRemove, IReadOnlyDictionary matchedWordsToAdd) { }
diff --git a/src/HexManiac.Core/Models/PokemonModel.cs b/src/HexManiac.Core/Models/PokemonModel.cs
index e9b7a4eb..36970891 100644
--- a/src/HexManiac.Core/Models/PokemonModel.cs
+++ b/src/HexManiac.Core/Models/PokemonModel.cs
@@ -192,7 +192,7 @@ namespace HavenSoft.HexManiac.Core.Models {
if (i == runs.Count - 1 || runs[i].Start + runs[i].Length <= runs[i + 1].Start) continue;
var debugRunStart1 = runs[i].Start.ToString("X6");
var debugRunStart2 = runs[i + 1].Start.ToString("X6");
- Debug.Fail("Conflict: there's a run that ends before the next run starts!");
+ Debug.Fail("Conflict: there's a run that ends after the next run starts!");
}
}
@@ -394,10 +394,10 @@ namespace HavenSoft.HexManiac.Core.Models {
return true;
}
- public override bool IsAtEndOfArray(int dataIndex, out ArrayRun arrayRun) {
+ public override bool IsAtEndOfArray(int dataIndex, out ITableRun arrayRun) {
var index = BinarySearch(dataIndex);
if (index >= 0 && runs[index].Length == 0) {
- arrayRun = runs[index] as ArrayRun;
+ arrayRun = runs[index] as ITableRun;
return arrayRun != null;
}
@@ -409,7 +409,7 @@ namespace HavenSoft.HexManiac.Core.Models {
return false;
}
- arrayRun = runs[index] as ArrayRun;
+ arrayRun = runs[index] as ITableRun;
return arrayRun != null && runs[index].Start + runs[index].Length == dataIndex;
}
@@ -493,7 +493,7 @@ namespace HavenSoft.HexManiac.Core.Models {
/// Remove the sources that match the array's original location.
/// Add new sources corresponding to the array's new location.
///
- private void UpdateAnchorsFromArrayMove(ModelDelta changeToken, ArrayRun original, ArrayRun moved) {
+ private void UpdateAnchorsFromArrayMove(ModelDelta changeToken, ITableRun original, ITableRun moved) {
int originalOffset = original.Start;
int segmentOffset = moved.Start;
// i loops over the different segments in the array
@@ -531,7 +531,7 @@ namespace HavenSoft.HexManiac.Core.Models {
if (anchor.Equals(table.LengthFromAnchor)) {
if (arrayRun.ElementCount == table.ElementCount) continue;
newTable = (ArrayRun)RelocateForExpansion(changeToken, table, arrayRun.ElementCount * table.ElementLength);
- newTable = newTable.Append(arrayRun.ElementCount - table.ElementCount);
+ newTable = newTable.Append(changeToken, arrayRun.ElementCount - table.ElementCount);
ObserveRunWritten(changeToken, newTable);
}
// option 2: this table includes a bit-array based on the given table
@@ -605,8 +605,8 @@ namespace HavenSoft.HexManiac.Core.Models {
// the pointer points to a known normal anchor
var existingRun = runs[index];
changeToken.RemoveRun(existingRun);
- UpdateNewRunFromPointerFormat(ref existingRun, segment as ArrayRunPointerSegment, changeToken);
existingRun = existingRun.MergeAnchor(new[] { start });
+ UpdateNewRunFromPointerFormat(ref existingRun, segment as ArrayRunPointerSegment, changeToken);
index = BinarySearch(destination); // runs could've been removed during UpdateNewRunFromPointerFormat: search for the index again.
if (index < 0) {
runs.Insert(~index, existingRun);
@@ -636,6 +636,10 @@ namespace HavenSoft.HexManiac.Core.Models {
run = runAttempt.MergeAnchor(run.PointerSources);
ClearFormat(token, run.Start, run.Length);
}
+ } else if (segment.InnerFormat == TrainerPokemonTeamRun.SharedFormatString) {
+ var runAttempt = new TrainerPokemonTeamRun(this, run.Start, run.PointerSources);
+ ClearFormat(token, run.Start, runAttempt.Length);
+ run = runAttempt;
} else {
throw new NotImplementedException();
}
@@ -1249,6 +1253,8 @@ namespace HavenSoft.HexManiac.Core.Models {
private static ErrorInfo TryParseFormat(IDataModel model, string format, int dataIndex, out IFormattedRun run) {
run = new NoInfoRun(dataIndex);
+ var existingRun = model.GetNextRun(dataIndex);
+ if (existingRun.Start == run.Start) run = run.MergeAnchor(existingRun.PointerSources);
if (format == PCSRun.SharedFormatString) {
var length = PCSString.ReadString(model, dataIndex, true);
@@ -1269,6 +1275,8 @@ namespace HavenSoft.HexManiac.Core.Models {
} else if (format == PLMRun.SharedFormatString) {
run = new PLMRun(model, dataIndex);
if (run.Length == 0) return new ErrorInfo("Format specified was for pokemon level-up move data, but could not parse that location as level-up move data.");
+ } else if (format == TrainerPokemonTeamRun.SharedFormatString) {
+ run = new TrainerPokemonTeamRun(model, dataIndex, run.PointerSources);
} else {
var errorInfo = TryParse(model, format, dataIndex, null, out var arrayRun);
if (errorInfo == ErrorInfo.NoError) {
@@ -1368,19 +1376,9 @@ namespace HavenSoft.HexManiac.Core.Models {
}
// move run
- IFormattedRun newRun;
- if (run is PCSRun pcs) {
- newRun = new PCSRun(this, newStart, run.Length, run.PointerSources);
- } else if (run is ArrayRun array) {
- var array1 = array.Move(newStart);
- UpdateAnchorsFromArrayMove(changeToken, array, array1);
- newRun = array1;
- } else if (run is EggMoveRun egg) {
- newRun = new EggMoveRun(this, newStart);
- } else if (run is PLMRun plm) {
- newRun = new PLMRun(this, newStart);
- } else {
- throw new NotImplementedException();
+ var newRun = run.Duplicate(newStart, run.PointerSources.ToArray());
+ if (newRun is ITableRun array) {
+ UpdateAnchorsFromArrayMove(changeToken, (ITableRun)run, array);
}
int index = BinarySearch(run.Start);
diff --git a/src/HexManiac.Core/Models/Runs/ArrayRun.cs b/src/HexManiac.Core/Models/Runs/ArrayRun.cs
index 4958d3b7..f4af0496 100644
--- a/src/HexManiac.Core/Models/Runs/ArrayRun.cs
+++ b/src/HexManiac.Core/Models/Runs/ArrayRun.cs
@@ -5,6 +5,54 @@ using System.Linq;
using System.Text;
namespace HavenSoft.HexManiac.Core.Models.Runs {
+ public interface ITableRun : IFormattedRun {
+ int ElementCount { get; }
+ int ElementLength { get; }
+ IReadOnlyList ElementNames { get; }
+ IReadOnlyList ElementContent { get; }
+ ITableRun Append(ModelDelta token, int length);
+ }
+
+ public static class ITableRunExtensions {
+ public static ArrayOffset ConvertByteOffsetToArrayOffset(this ITableRun self, int byteOffset) {
+ var offset = byteOffset - self.Start;
+ int elementIndex = offset / self.ElementLength;
+ int elementOffset = offset % self.ElementLength;
+ int segmentIndex = 0, segmentOffset = elementOffset;
+ while (self.ElementContent[segmentIndex].Length <= segmentOffset) {
+ segmentOffset -= self.ElementContent[segmentIndex].Length; segmentIndex++;
+ }
+ return new ArrayOffset(elementIndex, segmentIndex, byteOffset - segmentOffset, segmentOffset);
+ }
+
+ public static IDataFormat CreateSegmentDataFormat(this ITableRun self, IDataModel data, int index) {
+ var offsets = self.ConvertByteOffsetToArrayOffset(index);
+ var currentSegment = self.ElementContent[offsets.SegmentIndex];
+ var position = index - offsets.SegmentStart;
+ if (currentSegment.Type == ElementContentType.Integer) {
+ if (currentSegment is ArrayRunEnumSegment enumSegment) {
+ var value = enumSegment.ToText(data, index);
+ return new IntegerEnum(offsets.SegmentStart, position, value, currentSegment.Length);
+ } else {
+ var value = ArrayRunElementSegment.ToInteger(data, offsets.SegmentStart, currentSegment.Length);
+ return new Integer(offsets.SegmentStart, position, value, currentSegment.Length);
+ }
+ }
+
+ if (currentSegment.Type == ElementContentType.Pointer) {
+ var destination = data.ReadPointer(offsets.SegmentStart);
+ var destinationName = data.GetAnchorFromAddress(offsets.SegmentStart, destination);
+ return new Pointer(offsets.SegmentStart, position, destination, destinationName);
+ }
+
+ if (currentSegment.Type == ElementContentType.BitArray) {
+ return new BitArray(offsets.SegmentStart, position, currentSegment.Length);
+ }
+
+ throw new NotImplementedException();
+ }
+ }
+
public class ArrayOffset {
///
/// Ranges from 0 to ElementCount
@@ -30,7 +78,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
}
}
- public class ArrayRun : BaseRun {
+ public class ArrayRun : BaseRun, ITableRun {
public const char ExtendArray = '+';
public const char ArrayStart = '[';
public const char ArrayEnd = ']';
@@ -296,7 +344,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
private string cachedCurrentString;
private int currentCachedStartIndex = -1, currentCachedIndex = -1;
public override IDataFormat CreateDataFormat(IDataModel data, int index) {
- var offsets = ConvertByteOffsetToArrayOffset(index);
+ var offsets = this.ConvertByteOffsetToArrayOffset(index);
var currentSegment = ElementContent[offsets.SegmentIndex];
if (currentSegment.Type == ElementContentType.PCS) {
if (currentCachedStartIndex != offsets.SegmentStart || currentCachedIndex > offsets.SegmentOffset) {
@@ -308,42 +356,11 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
return PCSRun.CreatePCSFormat(data, offsets.SegmentStart, index, cachedCurrentString);
}
- var position = index - offsets.SegmentStart;
- if (currentSegment.Type == ElementContentType.Integer) {
- if (currentSegment is ArrayRunEnumSegment enumSegment) {
- var value = enumSegment.ToText(data, index);
- return new IntegerEnum(offsets.SegmentStart, position, value, currentSegment.Length);
- } else {
- var value = ArrayRunElementSegment.ToInteger(data, offsets.SegmentStart, currentSegment.Length);
- return new Integer(offsets.SegmentStart, position, value, currentSegment.Length);
- }
- }
-
- if (currentSegment.Type == ElementContentType.Pointer) {
- var destination = data.ReadPointer(offsets.SegmentStart);
- var destinationName = data.GetAnchorFromAddress(offsets.SegmentStart, destination);
- return new Pointer(offsets.SegmentStart, position, destination, destinationName);
- }
-
- if (currentSegment.Type == ElementContentType.BitArray) {
- return new BitArray(offsets.SegmentStart, position, currentSegment.Length);
- }
-
- throw new NotImplementedException();
+ return this.CreateSegmentDataFormat(data, index);
}
- public ArrayOffset ConvertByteOffsetToArrayOffset(int byteOffset) {
- var offset = byteOffset - Start;
- int elementIndex = offset / ElementLength;
- int elementOffset = offset % ElementLength;
- int segmentIndex = 0, segmentOffset = elementOffset;
- while (ElementContent[segmentIndex].Length <= segmentOffset) {
- segmentOffset -= ElementContent[segmentIndex].Length; segmentIndex++;
- }
- return new ArrayOffset(elementIndex, segmentIndex, byteOffset - segmentOffset, segmentOffset);
- }
-
- public ArrayRun Append(int elementCount) {
+ ITableRun ITableRun.Append(ModelDelta token, int elementCount) => Append(token, elementCount);
+ public ArrayRun Append(ModelDelta token, int elementCount) {
var lastArrayCharacterIndex = FormatString.LastIndexOf(ArrayEnd);
var newFormat = FormatString.Substring(0, lastArrayCharacterIndex + 1);
if (newFormat != FormatString) newFormat += ElementCount + elementCount;
@@ -429,7 +446,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
}
public void AppendTo(IDataModel data, StringBuilder text, int start, int length) {
- var offsets = ConvertByteOffsetToArrayOffset(start);
+ var offsets = this.ConvertByteOffsetToArrayOffset(start);
length += offsets.SegmentOffset;
for (int i = offsets.ElementIndex; i < ElementCount && length > 0; i++) {
var offset = offsets.SegmentStart;
@@ -643,7 +660,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
if (destination == Pointer.NULL) return true;
if (0 > destination || destination > owner.Count) return false;
if (segment is ArrayRunPointerSegment pointerSegment) {
- if (!pointerSegment.DestinationDataMatchesPointerFormat(owner, new NoDataChangeDeltaModel(), destination)) return false;
+ if (!pointerSegment.DestinationDataMatchesPointerFormat(owner, new NoDataChangeDeltaModel(), start, destination)) return false;
}
return true;
case ElementContentType.BitArray:
diff --git a/src/HexManiac.Core/Models/Runs/ArrayRunElementSegment.cs b/src/HexManiac.Core/Models/Runs/ArrayRunElementSegment.cs
index d6511f19..c37b56ce 100644
--- a/src/HexManiac.Core/Models/Runs/ArrayRunElementSegment.cs
+++ b/src/HexManiac.Core/Models/Runs/ArrayRunElementSegment.cs
@@ -157,6 +157,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
get {
if (InnerFormat == PCSRun.SharedFormatString) return true;
if (InnerFormat == PLMRun.SharedFormatString) return true;
+ if (InnerFormat == TrainerPokemonTeamRun.SharedFormatString) return true;
return false;
}
}
@@ -165,7 +166,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
InnerFormat = innerFormat;
}
- public bool DestinationDataMatchesPointerFormat(IDataModel owner, ModelDelta token, int destination) {
+ public bool DestinationDataMatchesPointerFormat(IDataModel owner, ModelDelta token, int source, int destination) {
if (destination == Pointer.NULL) return true;
var run = owner.GetNextAnchor(destination);
if (run.Start < destination) return false;
@@ -187,11 +188,19 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
if (!(token is NoDataChangeDeltaModel)) owner.ObserveRunWritten(token, plmRun);
return true;
}
+ } else if (InnerFormat == TrainerPokemonTeamRun.SharedFormatString) {
+ var teamRun = new TrainerPokemonTeamRun(owner, destination, new[] { source });
+ var length = teamRun.Length;
+ if (length >= 2) {
+ if (!(token is NoDataChangeDeltaModel)) owner.ObserveRunWritten(token, teamRun);
+ return true;
+ }
}
} else {
// easy case: already have a useful format, just see if it matches
if (InnerFormat == PCSRun.SharedFormatString) return run is PCSRun;
if (InnerFormat == PLMRun.SharedFormatString) return run is PLMRun;
+ if (InnerFormat == TrainerPokemonTeamRun.SharedFormatString) return run is TrainerPokemonTeamRun;
}
return false;
}
diff --git a/src/HexManiac.Core/Models/Runs/TrainerPokemonTeamRun.cs b/src/HexManiac.Core/Models/Runs/TrainerPokemonTeamRun.cs
new file mode 100644
index 00000000..f13a3e29
--- /dev/null
+++ b/src/HexManiac.Core/Models/Runs/TrainerPokemonTeamRun.cs
@@ -0,0 +1,285 @@
+using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace HavenSoft.HexManiac.Core.Models.Runs {
+ public class TrainerPokemonTeamRun : BaseRun, IStreamRun, ITableRun {
+ public const int TrainerFormat_StructTypeOffset = 0;
+ public const int TrainerFormat_PokemonCountOffset = 32;
+ public const int TrainerFormat_PointerOffset = 36;
+ public const byte INCLUDE_MOVES = 1;
+ public const byte INCLUDE_ITEM = 2;
+
+ public const int PokemonFormat_FixedIVStart = 0;
+ public const int PokemonFormat_LevelStart = 2;
+ public const int PokemonFormat_PokemonStart = 4;
+ public const int PokemonFormat_MoveStart = 6;
+
+ public static readonly string SharedFormatString = AsciiRun.StreamDelimeter + "tpt" + AsciiRun.StreamDelimeter;
+
+ private readonly IDataModel model;
+
+ public byte StructType { get; }
+
+ public TrainerPokemonTeamRun(IDataModel model, int start, IReadOnlyList sources) : base(start, sources) {
+ this.model = model;
+
+ // trainer format (abbreviated):
+ // 0 1 2 3 4-15 16 18 20 22 24 28 32 36 40 total
+ // [structType. class. introMusic. sprite. name\"\"12 item1: item2: item3: item4: doubleBattle:: ai:: pokemonCount:: pokemon<>]
+ if (sources.Count < 1) throw new ArgumentException("Trainer Pokemon Team streams must be pointed to!");
+ var source = sources.First();
+ StructType = model[source - TrainerFormat_PointerOffset];
+ ElementCount = model[source - TrainerFormat_PointerOffset + TrainerFormat_PokemonCountOffset];
+
+ var segments = new List();
+ segments.Add(new ArrayRunElementSegment("ivSpread", ElementContentType.Integer, 2));
+ segments.Add(new ArrayRunElementSegment("level", ElementContentType.Integer, 2));
+ segments.Add(new ArrayRunEnumSegment("mon", 2, EggMoveRun.PokemonNameTable));
+ if ((StructType & INCLUDE_MOVES) != 0) {
+ segments.Add(new ArrayRunEnumSegment("move1", 2, EggMoveRun.MoveNamesTable));
+ segments.Add(new ArrayRunEnumSegment("move2", 2, EggMoveRun.MoveNamesTable));
+ segments.Add(new ArrayRunEnumSegment("move3", 2, EggMoveRun.MoveNamesTable));
+ segments.Add(new ArrayRunEnumSegment("move4", 2, EggMoveRun.MoveNamesTable));
+ }
+ if ((StructType & INCLUDE_ITEM) != 0) {
+ segments.Add(new ArrayRunEnumSegment("item", 2, HardcodeTablesModel.ItemsTableName));
+ } else {
+ segments.Add(new ArrayRunElementSegment("padding", ElementContentType.Integer, 2));
+ }
+
+ ElementContent = segments;
+ ElementLength = ElementContent.Sum(segment => segment.Length);
+ }
+
+ public IEnumerable Search(string parentArrayName, int id) {
+ for (int i = 0; i < ElementCount; i++) {
+ int start = Start + i * ElementLength;
+ if (parentArrayName == EggMoveRun.MoveNamesTable && (StructType & INCLUDE_MOVES) != 0) {
+ for (int j = 0; j < 4; j++) {
+ var index = start + PokemonFormat_MoveStart + j * 2;
+ var moveID = model.ReadMultiByteValue(index, 2);
+ if (moveID == id) yield return index;
+ }
+ } else if (parentArrayName == EggMoveRun.PokemonNameTable) {
+ var index = start + PokemonFormat_PokemonStart;
+ var pokemonID = model.ReadMultiByteValue(index, 2);
+ if (pokemonID == id) yield return index;
+ } else if (parentArrayName == HardcodeTablesModel.ItemsTableName && (StructType & INCLUDE_ITEM) != 0) {
+ var index = start + ElementLength - 2;
+ var itemID = model.ReadMultiByteValue(index, 2);
+ if (itemID == id) yield return index;
+ }
+ }
+ }
+
+ #region BaseRun
+
+ public override int Length => ElementLength * ElementCount;
+
+ public override string FormatString => SharedFormatString;
+
+ public override IDataFormat CreateDataFormat(IDataModel data, int index) => this.CreateSegmentDataFormat(data, index);
+
+ protected override BaseRun Clone(IReadOnlyList newPointerSources) => new TrainerPokemonTeamRun(model, Start, newPointerSources);
+
+ #endregion
+
+ #region ITableRun
+
+ public int ElementCount { get; }
+
+ public int ElementLength { get; }
+
+ public IReadOnlyList ElementNames { get; } = new List();
+
+ public IReadOnlyList ElementContent { get; }
+
+ public ITableRun Append(ModelDelta token, int length) {
+ var totalLength = ElementLength * (ElementCount + length);
+ var workingRun = this;
+ if (totalLength > workingRun.Length) workingRun = (TrainerPokemonTeamRun)model.RelocateForExpansion(token, workingRun, totalLength);
+
+ // delete old elements
+ for (int i = -1; i >= length; i--) {
+ var start = workingRun.Start + workingRun.Length + i * ElementLength;
+ for (int j = 0; j < ElementLength; j++) token.ChangeData(model, start + j, 0xFF);
+ }
+
+ // add new elements
+ for (int i = 0; i < length; i++) {
+ var start = workingRun.Start + workingRun.Length + i * ElementLength;
+ for (int j = 0; j < ElementLength; j++) token.ChangeData(model, start + j, 0x00);
+ }
+
+ // update parent
+ var parent = workingRun.PointerSources[0] - TrainerFormat_PointerOffset;
+ model.WriteMultiByteValue(parent + TrainerFormat_PokemonCountOffset, 4, token, ElementCount + length);
+ return new TrainerPokemonTeamRun(model, workingRun.Start, workingRun.PointerSources);
+ }
+
+ #endregion
+
+ #region IStreamRun
+
+ // example serialized pokemon:
+ //
+ // 50 Butterfree (31)@"Silk Powder"
+ // - "Stun Spore"
+ // - "Super Sonic"
+ // - "Aerial Ace"
+ // - "Silver Wind"
+
+ public IStreamRun DeserializeRun(string content, ModelDelta token) {
+ var lines = content.Split(Environment.NewLine).Select(line => line.Trim()).ToArray();
+
+ // step 1: parse it into some data containers
+ var data = new TeamData(ModelCacheScope.GetCache(model), lines);
+
+ // step 2: figure out what I need based on the data
+ var elementLength = data.MovesIncluded ? 16 : 8;
+ var totalLength = elementLength * data.Pokemon.Count;
+ var workingRun = this;
+ if (totalLength > workingRun.Length) workingRun = (TrainerPokemonTeamRun)model.RelocateForExpansion(token, workingRun, totalLength);
+
+ // step 3: write the run data
+ for (int i = 0; i < data.Pokemon.Count; i++) {
+ int start = workingRun.Start + elementLength * i;
+ model.WriteMultiByteValue(start + 0, 2, token, data.Levels[i]);
+ model.WriteMultiByteValue(start + 2, 2, token, data.Pokemon[i]);
+ model.WriteMultiByteValue(start + 4, 2, token, data.IVs[i]);
+ start += 6;
+ if (data.MovesIncluded) {
+ for (int j = 0; j < 4; j++) model.WriteMultiByteValue(start + j * 2, 2, token, data.Moves[i * 4 + j]);
+ start += 8;
+ }
+ model.WriteMultiByteValue(start, 2, token, data.Items[i]);
+ }
+
+ // step 4: write the parent data
+ var structType = (data.ItemsIncluded ? INCLUDE_ITEM : 0) ^ (data.MovesIncluded ? INCLUDE_MOVES : 0);
+ var parent = workingRun.PointerSources[0] - TrainerFormat_PointerOffset;
+ model.WriteMultiByteValue(parent + TrainerFormat_StructTypeOffset, 1, token, structType);
+ model.WriteMultiByteValue(parent + TrainerFormat_PokemonCountOffset, 4, token, data.Pokemon.Count);
+
+ return new TrainerPokemonTeamRun(model, workingRun.Start, workingRun.PointerSources);
+ }
+
+ public string SerializeRun() {
+ var cache = ModelCacheScope.GetCache(model);
+ var buffer = new StringBuilder();
+ for (int i = 0; i < ElementCount; i++) {
+ var start = Start + ElementLength * i;
+ var ivSpread = model.ReadMultiByteValue(start + 0, 2) * 31 / 255;
+ var level = model.ReadMultiByteValue(start + 2, 2);
+ var pokeID = model.ReadMultiByteValue(start + 4, 2);
+ var pokemonNames = cache.GetOptions(EggMoveRun.PokemonNameTable);
+ var pokemon = pokemonNames.Count > pokeID ? pokemonNames[pokeID] : pokeID.ToString();
+ var item = string.Empty;
+ if ((StructType & INCLUDE_ITEM) != 0) {
+ var itemID = model.ReadMultiByteValue(start + ElementLength - 2, 2);
+ var itemNames = cache.GetOptions(HardcodeTablesModel.ItemsTableName);
+ item = itemNames.Count > itemID ? itemNames[itemID] : itemID.ToString();
+ item = "@" + item;
+ }
+ buffer.AppendLine($"{level} {pokemon} ({ivSpread}){item}");
+ if ((StructType & INCLUDE_MOVES) != 0) {
+ var moveNames = cache.GetOptions(EggMoveRun.MoveNamesTable);
+ for(int j = 0; j < 4; j++) {
+ var moveID = model.ReadMultiByteValue(start + 6 + j * 2, 2);
+ var move = moveNames.Count > moveID ? moveNames[moveID] : moveID.ToString();
+ buffer.AppendLine($"- {move}");
+ }
+ }
+ if (i + 1 < ElementCount) buffer.AppendLine();
+ }
+ return buffer.ToString();
+ }
+
+ private class TeamData {
+ private readonly List levels = new List();
+ private readonly List pokemons = new List();
+ private readonly List ivs = new List();
+ private readonly List items = new List();
+ private readonly List moves = new List();
+
+ public bool ItemsIncluded { get; private set; }
+ public bool MovesIncluded { get; private set; }
+
+ public IReadOnlyList Levels => levels;
+ public IReadOnlyList Pokemon => pokemons;
+ public IReadOnlyList IVs => ivs;
+ public IReadOnlyList Items => items;
+ public IReadOnlyList Moves => moves;
+
+ public TeamData(ModelCacheScope cache, string[] lines) {
+ var currentPokemonMoveCount = 0;
+ var moveNames = cache.GetOptions(EggMoveRun.MoveNamesTable);
+ var itemNames = cache.GetOptions(HardcodeTablesModel.ItemsTableName);
+ var pokemonNames = cache.GetOptions(EggMoveRun.PokemonNameTable);
+
+ foreach (var line in lines) {
+ if (line is "") continue;
+ if (line.StartsWith("-")) {
+ if (pokemons.Count == 0) continue;
+ if (currentPokemonMoveCount > 3) continue;
+ MovesIncluded = true;
+ var move = line.Substring(1).Trim();
+ var moveIndex = moveNames.IndexOfPartial(move);
+ if (moveIndex < 0) moveIndex = 0;
+ moves[(pokemons.Count - 1) * 4 + currentPokemonMoveCount] = moveIndex;
+ currentPokemonMoveCount++;
+ } else {
+ if (pokemons.Count == 6) continue;
+
+ var levelTokenized = line.Split(new[] { ' ' }, 2);
+ if (levelTokenized.Length != 2) continue;
+ if (!int.TryParse(levelTokenized[0], out int level)) continue;
+ levels.Add(level);
+
+ var itemTokenized = line.Split(new[] { '@' }, 2);
+ AddItem(itemNames, items, itemTokenized);
+
+ var ivTokenized = line.Split(new[] { '(' }, 2);
+ AddIV(ivs, ivTokenized);
+
+ var pokemon = ivTokenized[0].Trim();
+ var pokemonIndex = pokemonNames.IndexOfPartial(pokemon);
+ if (pokemonIndex < 0) pokemonIndex = 0;
+ pokemons.Add(pokemonIndex);
+ moves.AddRange(new[] { 0, 0, 0, 0 });
+ currentPokemonMoveCount = 0;
+ }
+ }
+ }
+
+ private void AddIV(List ivs, string[] ivTokenized) {
+ if (ivTokenized.Length == 2) {
+ ivTokenized[1] = ivTokenized[1].Replace(")", "").Trim();
+ if (int.TryParse(ivTokenized[1], out int fixedIV)) {
+ ivs.Add(fixedIV * 255 / 31);
+ } else {
+ ivs.Add(0);
+ }
+ } else {
+ ivs.Add(0);
+ }
+ }
+
+ private void AddItem(IReadOnlyList itemNames, List items, string[] itemTokenized) {
+ if (itemTokenized.Length == 2) {
+ ItemsIncluded = true;
+ var itemIndex = itemNames.IndexOfPartial(itemTokenized[1]);
+ if (itemIndex < 0) itemIndex = 0;
+ items.Add(itemIndex);
+ } else {
+ items.Add(0);
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/HexManiac.Core/SystemExtensions.cs b/src/HexManiac.Core/SystemExtensions.cs
index 503ca170..cf836a43 100644
--- a/src/HexManiac.Core/SystemExtensions.cs
+++ b/src/HexManiac.Core/SystemExtensions.cs
@@ -44,7 +44,7 @@ namespace HavenSoft.HexManiac.Core {
return true;
}
- public static int IndexOfPartial(this IList names, string input) {
+ public static int IndexOfPartial(this IReadOnlyList names, string input) {
// perfect match first
var matchIndex = names.IndexOf(input);
if (matchIndex != -1) return matchIndex;
diff --git a/src/HexManiac.Core/ViewModels/QuickEditItems/MakeTmsExpandable.cs b/src/HexManiac.Core/ViewModels/QuickEditItems/MakeTmsExpandable.cs
index 5cc4a512..af1f29b1 100644
--- a/src/HexManiac.Core/ViewModels/QuickEditItems/MakeTmsExpandable.cs
+++ b/src/HexManiac.Core/ViewModels/QuickEditItems/MakeTmsExpandable.cs
@@ -201,7 +201,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.QuickEditItems {
// clear HMs from the TmMove table
table = (ArrayRun)model.GetNextRun(tmMovesAddress);
- table = table.Append(-8);
+ table = table.Append(token, -8);
model.ObserveAnchorWritten(token, TmMoves, table);
for (int i = 0; i < 8; i++) model.WriteMultiByteValue(table.Start + table.Length + i * 2, 2, token, 0);
diff --git a/src/HexManiac.Core/ViewModels/Tools/IArrayElementViewModel.cs b/src/HexManiac.Core/ViewModels/Tools/IArrayElementViewModel.cs
index ce343fc3..1f9e34e0 100644
--- a/src/HexManiac.Core/ViewModels/Tools/IArrayElementViewModel.cs
+++ b/src/HexManiac.Core/ViewModels/Tools/IArrayElementViewModel.cs
@@ -201,7 +201,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
get => selectedIndex;
set {
if (!TryUpdate(ref selectedIndex, value)) return;
- var run = (ArrayRun)Model.GetNextRun(Start);
+ var run = (ITableRun)Model.GetNextRun(Start);
var offsets = run.ConvertByteOffsetToArrayOffset(Start);
var segment = (ArrayRunEnumSegment)run.ElementContent[offsets.SegmentIndex];
@@ -217,7 +217,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
public ComboBoxArrayElementViewModel(Selection selection, ChangeHistory history, IDataModel model, string name, int start, int length) {
(this.history, Model, Name, Start, Length) = (history, model, name, start, length);
- var run = (ArrayRun)Model.GetNextRun(Start);
+ var run = (ITableRun)Model.GetNextRun(Start);
var offsets = run.ConvertByteOffsetToArrayOffset(start);
var segment = (ArrayRunEnumSegment)run.ElementContent[offsets.SegmentIndex];
var optionSource = model.GetAddressFromAnchor(history.CurrentChange, -1, segment.EnumName);
@@ -330,7 +330,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
Name = name;
this.start = start;
- var array = (ArrayRun)model.GetNextRun(start);
+ var array = (ITableRun)model.GetNextRun(start);
var offset = array.ConvertByteOffsetToArrayOffset(start);
segment = (ArrayRunBitArraySegment)array.ElementContent[offset.SegmentIndex];
var optionSource = model.GetAddressFromAnchor(history.CurrentChange, -1, segment.SourceArrayName);
diff --git a/src/HexManiac.Core/ViewModels/Tools/PCSTool.cs b/src/HexManiac.Core/ViewModels/Tools/PCSTool.cs
index 07cd3468..585545da 100644
--- a/src/HexManiac.Core/ViewModels/Tools/PCSTool.cs
+++ b/src/HexManiac.Core/ViewModels/Tools/PCSTool.cs
@@ -292,7 +292,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
}
if (newRun.ElementCount != lines.Length) {
- newRun = newRun.Append(lines.Length - newRun.ElementCount);
+ newRun = newRun.Append(history.CurrentChange, lines.Length - newRun.ElementCount);
model.ObserveRunWritten(history.CurrentChange, newRun);
history.CurrentChange.AddRun(newRun);
}
diff --git a/src/HexManiac.Core/ViewModels/Tools/TableTool.cs b/src/HexManiac.Core/ViewModels/Tools/TableTool.cs
index aa81c0d4..a26d3f8c 100644
--- a/src/HexManiac.Core/ViewModels/Tools/TableTool.cs
+++ b/src/HexManiac.Core/ViewModels/Tools/TableTool.cs
@@ -58,7 +58,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
set {
if (TryUpdate(ref address, value)) {
var run = model.GetNextRun(value);
- if (run.Start > value || !(run is ArrayRun array)) {
+ if (run.Start > value || !(run is ITableRun)) {
Enabled = false;
CommandCanExecuteChanged();
return;
@@ -95,11 +95,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
previous = new StubCommand {
CanExecute = parameter => {
- var array = model.GetNextRun(address) as ArrayRun;
+ var array = model.GetNextRun(address) as ITableRun;
return array != null && array.Start < address;
},
Execute = parameter => {
- var array = (ArrayRun)model.GetNextRun(address);
+ var array = (ITableRun)model.GetNextRun(address);
selection.SelectionStart = selection.Scroll.DataIndexToViewPoint(Address - array.ElementLength);
selection.SelectionEnd = selection.Scroll.DataIndexToViewPoint(selection.Scroll.ViewPointToDataIndex(selection.SelectionStart) + array.ElementLength - 1);
}
@@ -107,11 +107,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
next = new StubCommand {
CanExecute = parameter => {
- var array = model.GetNextRun(address) as ArrayRun;
+ var array = model.GetNextRun(address) as ITableRun;
return array != null && array.Start + array.Length > address + array.ElementLength;
},
Execute = parameter => {
- var array = (ArrayRun)model.GetNextRun(address);
+ var array = (ITableRun)model.GetNextRun(address);
selection.SelectionStart = selection.Scroll.DataIndexToViewPoint(Address + array.ElementLength);
selection.SelectionEnd = selection.Scroll.DataIndexToViewPoint(selection.Scroll.ViewPointToDataIndex(selection.SelectionStart) + array.ElementLength - 1);
}
@@ -119,12 +119,12 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
append = new StubCommand {
CanExecute = parameter => {
- var array = model.GetNextRun(address) as ArrayRun;
+ var array = model.GetNextRun(address) as ITableRun;
return array != null && array.Start + array.Length == address + array.ElementLength;
},
Execute = parameter => {
using (ModelCacheScope.CreateScope(model)) {
- var array = (ArrayRun)model.GetNextRun(address);
+ var array = (ITableRun)model.GetNextRun(address);
var originalArray = array;
var error = model.CompleteArrayExtension(viewPort.CurrentChange, ref array);
if (array.Start != originalArray.Start) {
@@ -151,7 +151,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
foreach (var child in Children) child.DataChanged -= ForwardModelChanged;
Children.Clear();
- var array = model.GetNextRun(Address) as ArrayRun;
+ var array = model.GetNextRun(Address) as ITableRun;
if (array == null) {
CurrentElementName = "The Table tool only works if your cursor is on table data.";
return;
@@ -161,6 +161,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
TryUpdate(ref selectedTableIndex, model.Arrays.IndexOf(array), nameof(SelectedTableIndex));
var basename = model.GetAnchorFromAddress(-1, array.Start);
+ if (string.IsNullOrEmpty(basename)) basename = array.Start.ToString("X6");
var index = (Address - array.Start) / array.ElementLength;
if (array.ElementNames.Count > index) {
CurrentElementName = $"{basename}/{index}" + Environment.NewLine + $"{basename}/{array.ElementNames[index]}";
@@ -168,18 +169,18 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
CurrentElementName = $"{basename}/{index}";
}
- if (!string.IsNullOrEmpty(array.LengthFromAnchor)) basename = array.LengthFromAnchor; // basename is now a 'parent table' name, if there is one
-
- // add content from this table
AddChildrenFromTable(array, index);
- // add content from related tables
- foreach (var currentArray in model.Arrays) {
- if (currentArray == array) continue;
- var currentArrayName = model.GetAnchorFromAddress(-1, currentArray.Start);
- if (currentArray.LengthFromAnchor == basename || currentArrayName == basename) {
- Children.Add(new SplitterArrayElementViewModel(currentArrayName));
- AddChildrenFromTable(currentArray, index);
+ if (array is ArrayRun arrayRun) {
+ if (!string.IsNullOrEmpty(arrayRun.LengthFromAnchor)) basename = arrayRun.LengthFromAnchor; // basename is now a 'parent table' name, if there is one
+
+ foreach (var currentArray in model.Arrays) {
+ if (currentArray == arrayRun) continue;
+ var currentArrayName = model.GetAnchorFromAddress(-1, currentArray.Start);
+ if (currentArray.LengthFromAnchor == basename || currentArrayName == basename) {
+ Children.Add(new SplitterArrayElementViewModel(currentArrayName));
+ AddChildrenFromTable(currentArray, index);
+ }
}
}
@@ -210,7 +211,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
}
}
- private void AddChildrenFromTable(ArrayRun table, int index) {
+ private void AddChildrenFromTable(ITableRun table, int index) {
var itemAddress = table.Start + table.ElementLength * index;
foreach (var item in table.ElementContent) {
IArrayElementViewModel viewModel = null;
@@ -231,9 +232,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
viewModel.DataChanged += ForwardModelChanged;
if (item is ArrayRunPointerSegment pointerSegment) {
var destination = model.ReadPointer(itemAddress);
- if (destination != Pointer.NULL && model.GetNextRun(destination) is IStreamRun && pointerSegment.DestinationDataMatchesPointerFormat(model, new NoDataChangeDeltaModel(), destination)) {
- if (pointerSegment.InnerFormat == PCSRun.SharedFormatString || pointerSegment.InnerFormat == PLMRun.SharedFormatString) {
- var streamElement = new StreamArrayElementViewModel(viewPort, (FieldArrayElementViewModel)viewModel, model, item.Name, itemAddress);
+ if (destination != Pointer.NULL && model.GetNextRun(destination) is IStreamRun && pointerSegment.DestinationDataMatchesPointerFormat(model, new NoDataChangeDeltaModel(), itemAddress, destination)) {
+ if (pointerSegment.InnerFormat == PCSRun.SharedFormatString || pointerSegment.InnerFormat == PLMRun.SharedFormatString || pointerSegment.InnerFormat == TrainerPokemonTeamRun.SharedFormatString) {
+ var streamElement = new StreamArrayElementViewModel(history, (FieldArrayElementViewModel)viewModel, model, item.Name, itemAddress);
int parentIndex = Children.Count - 1;
var streamElementName = item.Name;
var streamAddress = itemAddress;
diff --git a/src/HexManiac.Core/ViewModels/ViewPort.cs b/src/HexManiac.Core/ViewModels/ViewPort.cs
index bcccf231..5371baa5 100644
--- a/src/HexManiac.Core/ViewModels/ViewPort.cs
+++ b/src/HexManiac.Core/ViewModels/ViewPort.cs
@@ -174,7 +174,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
var run = Model.GetNextRun(dataIndex);
using (ModelCacheScope.CreateScope(Model)) {
- if (run.Start <= dataIndex && run is ArrayRun array) {
+ if (run.Start <= dataIndex && run is ITableRun array) {
var offsets = array.ConvertByteOffsetToArrayOffset(dataIndex);
Tools.StringTool.Address = offsets.SegmentStart - offsets.ElementIndex * array.ElementLength;
Tools.TableTool.Address = array.Start + array.ElementLength * offsets.ElementIndex;
@@ -206,7 +206,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
var result = "Address: " + left.ToString("X6");
var run = Model.GetNextRun(left);
- if (run is ArrayRun array1 && array1.Start <= left) {
+ if (run is ITableRun array1 && array1.Start <= left) {
var index = array1.ConvertByteOffsetToArrayOffset(left).ElementIndex;
var basename = Model.GetAnchorFromAddress(-1, array1.Start);
if (array1.ElementNames.Count > index) {
@@ -216,7 +216,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
}
} else if (run.PointerSources != null && run.PointerSources.Count > 0 && string.IsNullOrEmpty(Model.GetAnchorFromAddress(-1, run.Start))) {
var sourceRun = Model.GetNextRun(run.PointerSources[0]);
- if (sourceRun is ArrayRun array2) {
+ if (sourceRun is ITableRun array2) {
// we are an anchor that's pointed to from an array
var offset = array2.ConvertByteOffsetToArrayOffset(run.PointerSources[0]);
var index = offset.ElementIndex;
@@ -442,7 +442,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
var right = Math.Max(selectionStart, selectionEnd);
var startRun = Model.GetNextRun(left);
var endRun = Model.GetNextRun(right);
- if (startRun == endRun && startRun.Start <= left && (startRun.Start < left || startRun.Start + startRun.Length - 1 > right) && startRun is ArrayRun arrayRun) {
+ if (startRun == endRun && startRun.Start <= left && (startRun.Start < left || startRun.Start + startRun.Length - 1 > right) && startRun is ITableRun arrayRun) {
for (int i = 0; i < arrayRun.ElementCount; i++) {
var start = arrayRun.Start + arrayRun.ElementLength * i;
if (start + arrayRun.ElementLength <= left) continue;
@@ -558,7 +558,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
public bool IsTable(Point point) {
var search = scroll.ViewPointToDataIndex(point);
var run = Model.GetNextRun(search);
- return run.Start <= search && run is ArrayRun;
+ return run.Start <= search && run is ITableRun;
}
public void Refresh() => RefreshBackingData();
@@ -604,11 +604,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
TryCompleteEdit(point);
return;
}
- if (key == ConsoleKey.Enter && run is ArrayRun arrayRun1) {
+ if (key == ConsoleKey.Enter && run is ITableRun arrayRun1) {
var offsets = arrayRun1.ConvertByteOffsetToArrayOffset(offset);
SilentScroll(offsets.SegmentStart + arrayRun1.ElementLength);
}
- if (key == ConsoleKey.Tab && run is ArrayRun arrayRun2) {
+ if (key == ConsoleKey.Tab && run is ITableRun arrayRun2) {
var offsets = arrayRun2.ConvertByteOffsetToArrayOffset(offset);
SilentScroll(offsets.SegmentStart + arrayRun2.ElementContent[offsets.SegmentIndex].Length);
}
@@ -702,7 +702,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
var cellToText = new ConvertCellToText(Model, run.Start);
var cell = currentView[point.X, point.Y];
- if (run is ArrayRun array) {
+ if (run is ITableRun array) {
var offsets = array.ConvertByteOffsetToArrayOffset(index);
if (array.ElementContent[offsets.SegmentIndex].Type == ElementContentType.PCS) {
for (int i = index + 1; i < offsets.SegmentStart + array.ElementContent[offsets.SegmentIndex].Length; i++) history.CurrentChange.ChangeData(Model, i, 0x00);
@@ -868,6 +868,12 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
if (child is PLMRun plmRun && parentArrayName == EggMoveRun.MoveNamesTable) {
foreach (var result in plmRun.Search(parentArrayName, offsets.ElementIndex)) yield return result;
}
+ // option 3: the value is a move used by trainer teams
+ if (child is TrainerPokemonTeamRun team) {
+ foreach (var result in team.Search(parentArrayName, offsets.ElementIndex)) {
+ yield return (result, result + 1);
+ }
+ }
}
}
@@ -1014,7 +1020,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
if (currentRun is IStreamRun) {
Tools.StringTool.Address = currentRun.Start;
Tools.SelectedIndex = Tools.IndexOf(Tools.StringTool);
- } else if (currentRun is ArrayRun array) {
+ } else if (currentRun is ITableRun array) {
var offsets = array.ConvertByteOffsetToArrayOffset(byteOffset);
if (format is PCS) {
Tools.StringTool.Address = offsets.SegmentStart - offsets.ElementIndex * array.ElementLength;
@@ -1031,7 +1037,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
var index = scroll.ViewPointToDataIndex(SelectionStart);
var run = Model.GetNextRun(index);
if (run.Start > index) return;
- if (run is ArrayRun array) {
+ if (run is ITableRun array) {
var offsets = array.ConvertByteOffsetToArrayOffset(index);
if (array.ElementContent[offsets.SegmentIndex].Type == ElementContentType.Pointer) {
FollowLink(x, y);
@@ -1150,7 +1156,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
} else if (newText.StartsWith(GotoMarker.ToString())) {
return Model.GetNewPointerAutocompleteOptions(newText, selectedIndex);
} else if (originalFormat is IntegerEnum intEnum) {
- var array = (ArrayRun)Model.GetNextRun(intEnum.Source);
+ var array = (ITableRun)Model.GetNextRun(intEnum.Source);
var segment = (ArrayRunEnumSegment)array.ElementContent[array.ConvertByteOffsetToArrayOffset(intEnum.Source).SegmentIndex];
var options = segment.GetOptions(Model).Select(option => option + " "); // autocomplete needs to complete after selection, so add a space
return AutoCompleteSelectionItem.Generate(options.Where(option => option.MatchesPartial(newText)), selectedIndex);
@@ -1252,7 +1258,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
if (even) return pair(index, index + 1);
return pair(index - 1, index);
}
- if (!(run is ArrayRun array)) return (p, p);
+ if (!(run is ITableRun array)) return (p, p);
var offset = array.ConvertByteOffsetToArrayOffset(index);
var type = array.ElementContent[offset.SegmentIndex].Type;
@@ -1322,8 +1328,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
var run = Model.GetNextRun(completeEditOperation.NewDataIndex);
if (run.Start > completeEditOperation.NewDataIndex) run = new NoInfoRun(Model.Count);
if (completeEditOperation.DataMoved) UpdateToolsFromSelection(run.Start);
- if (run is ArrayRun) Tools.Schedule(Tools.TableTool.DataForCurrentRunChanged);
- if (run is ArrayRun || run is PCSRun || run is PLMRun || run is EggMoveRun) Tools.Schedule(Tools.StringTool.DataForCurrentRunChanged);
+ if (run is ITableRun) Tools.Schedule(Tools.TableTool.DataForCurrentRunChanged);
+ if (run is ITableRun || run is IStreamRun) Tools.Schedule(Tools.StringTool.DataForCurrentRunChanged);
if (completeEditOperation.MessageText != null) OnMessage?.Invoke(this, completeEditOperation.MessageText);
if (completeEditOperation.ErrorText != null) OnError?.Invoke(this, completeEditOperation.ErrorText);
}
@@ -1493,8 +1499,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
RefreshBackingData();
}
- if (run is ArrayRun && sender != Tools.StringTool && Model.GetNextRun(Tools.StringTool.Address).Start == run.Start) Tools.StringTool.DataForCurrentRunChanged();
- if (run is ArrayRun && sender != Tools.TableTool && Model.GetNextRun(Tools.TableTool.Address).Start == run.Start) Tools.TableTool.DataForCurrentRunChanged();
+ if (run is ITableRun && sender != Tools.StringTool && Model.GetNextRun(Tools.StringTool.Address).Start == run.Start) Tools.StringTool.DataForCurrentRunChanged();
+ if (run is ITableRun && sender != Tools.TableTool && Model.GetNextRun(Tools.TableTool.Address).Start == run.Start) Tools.TableTool.DataForCurrentRunChanged();
}
private void ModelDataMovedByTool(object sender, (int originalLocation, int newLocation) locations) {
diff --git a/src/HexManiac.Core/ViewModels/Visitors/CompleteEditOperation.cs b/src/HexManiac.Core/ViewModels/Visitors/CompleteEditOperation.cs
index 605a041c..73993461 100644
--- a/src/HexManiac.Core/ViewModels/Visitors/CompleteEditOperation.cs
+++ b/src/HexManiac.Core/ViewModels/Visitors/CompleteEditOperation.cs
@@ -55,7 +55,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
public void Visit(Pointer pointer, byte data) {
var run = Model.GetNextRun(memoryLocation);
- if (run is ArrayRun && CurrentText[0] != PointerStart) {
+ if (run is ITableRun && CurrentText[0] != PointerStart) {
ErrorText = "Pointers in tables cannot be removed without removing the table.";
return;
}
@@ -215,7 +215,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
return;
}
- var run = (ArrayRun)Model.GetNextRun(memoryLocation);
+ var run = (ITableRun)Model.GetNextRun(memoryLocation);
var offsets = run.ConvertByteOffsetToArrayOffset(memoryLocation);
int length = run.ElementContent[offsets.SegmentIndex].Length;
for (int i = 0; i < length; i++) {
@@ -228,7 +228,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
}
private void CompleteIntegerEnumEdit(IntegerEnum integer) {
- var array = (ArrayRun)Model.GetNextRun(memoryLocation);
+ var array = (ITableRun)Model.GetNextRun(memoryLocation);
var offsets = array.ConvertByteOffsetToArrayOffset(memoryLocation);
var segment = (ArrayRunEnumSegment)array.ElementContent[offsets.SegmentIndex];
if (segment.TryParse(Model, CurrentText, out int value)) {
@@ -259,7 +259,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
var currentRun = Model.GetNextRun(memoryLocation);
if (currentRun.Start > memoryLocation) currentRun = null;
- bool inArray = currentRun is ArrayRun && currentRun.Start <= memoryLocation;
+ bool inArray = currentRun is ITableRun && currentRun.Start <= memoryLocation;
var sources = currentRun?.PointerSources;
if (!inArray) {
@@ -284,7 +284,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
if (fullValue == Pointer.NULL || (0 <= fullValue && fullValue < Model.Count)) {
if (inArray) {
- UpdateArrayPointer((ArrayRun)currentRun, fullValue);
+ UpdateArrayPointer((ITableRun)currentRun, fullValue);
} else {
Model.WritePointer(CurrentChange, memoryLocation, fullValue);
Model.ObserveRunWritten(CurrentChange, new PointerRun(memoryLocation, sources));
@@ -318,7 +318,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
var newRunLength = PCSString.ReadString(Model, run.Start, true);
Model.ObserveRunWritten(CurrentChange, new PCSRun(Model, run.Start, newRunLength, run.PointerSources));
}
- } else if (run is ArrayRun arrayRun) {
+ } else if (run is ITableRun arrayRun) {
var offsets = arrayRun.ConvertByteOffsetToArrayOffset(memoryLocation);
CurrentChange.ChangeData(Model, memoryLocation, 0xFF);
memoryLocation++;
@@ -365,7 +365,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
run = new PCSRun(Model, run.Start, run.Length + extraBytesNeeded, run.PointerSources);
Model.ObserveRunWritten(CurrentChange, run);
}
- } else if (run is ArrayRun arrayRun) {
+ } else if (run is ITableRun arrayRun) {
// if the last characet is being edited for an array, truncate
var offsets = arrayRun.ConvertByteOffsetToArrayOffset(memoryLocation);
if (arrayRun.ElementContent[offsets.SegmentIndex].Length == position + 1) {
@@ -457,11 +457,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
}
}
- private void UpdateArrayPointer(ArrayRun run, int pointerDestination) {
+ private void UpdateArrayPointer(ITableRun run, int pointerDestination) {
var offsets = run.ConvertByteOffsetToArrayOffset(memoryLocation);
var segment = run.ElementContent[offsets.SegmentIndex];
if (segment is ArrayRunPointerSegment pointerSegment) {
- if (!pointerSegment.DestinationDataMatchesPointerFormat(Model, CurrentChange, pointerDestination)) {
+ if (!pointerSegment.DestinationDataMatchesPointerFormat(Model, CurrentChange, offsets.SegmentStart, pointerDestination)) {
ErrorText = $"This pointer must point to {pointerSegment.InnerFormat} data.";
return;
}
diff --git a/src/HexManiac.Core/ViewModels/Visitors/ContextItemFactory.cs b/src/HexManiac.Core/ViewModels/Visitors/ContextItemFactory.cs
index c508054b..cfdcb13c 100644
--- a/src/HexManiac.Core/ViewModels/Visitors/ContextItemFactory.cs
+++ b/src/HexManiac.Core/ViewModels/Visitors/ContextItemFactory.cs
@@ -63,7 +63,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
Results.Add(new ContextItem("Repoint to New Copy", arg => ViewPort.RepointToNewCopy(pointerAddress)));
}
- var arrayRun = ViewPort.Model.GetNextRun(pointerAddress) as ArrayRun;
+ var arrayRun = ViewPort.Model.GetNextRun(pointerAddress) as ITableRun;
if (arrayRun != null && arrayRun.Start <= pointerAddress) Results.AddRange(GetTableChildren(arrayRun));
else Results.AddRange(GetFormattedChildren());
}
@@ -101,7 +101,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
Results.Add(new ContextItem("Copy Selection", ViewPort.Copy.Execute) { ShortcutText = "Ctrl+C" });
var address = ViewPort.ConvertViewPointToAddress(point);
- var arrayRun = ViewPort.Model.GetNextRun(address) as ArrayRun;
+ var arrayRun = ViewPort.Model.GetNextRun(address) as ITableRun;
if (arrayRun != null && arrayRun.Start <= address) Results.AddRange(GetTableChildren(arrayRun));
else Results.AddRange(GetFormattedChildren());
}
@@ -113,12 +113,12 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
public void Visit(Ascii ascii, byte data) => Results.AddRange(GetFormattedChildren());
public void Visit(Integer integer, byte data) {
- var arrayRun = (ArrayRun)ViewPort.Model.GetNextRun(ViewPort.Tools.TableTool.Address);
+ var arrayRun = (ITableRun)ViewPort.Model.GetNextRun(ViewPort.Tools.TableTool.Address);
Results.AddRange(GetTableChildren(arrayRun));
}
public void Visit(IntegerEnum integer, byte data) {
- var arrayRun = (ArrayRun)ViewPort.Model.GetNextRun(ViewPort.Tools.TableTool.Address);
+ var arrayRun = (ITableRun)ViewPort.Model.GetNextRun(ViewPort.Tools.TableTool.Address);
Results.AddRange(GetTableChildren(arrayRun));
}
@@ -137,13 +137,13 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
}
public void Visit(BitArray array, byte data) {
- var arrayRun = (ArrayRun)ViewPort.Model.GetNextRun(ViewPort.Tools.TableTool.Address);
+ var arrayRun = (ITableRun)ViewPort.Model.GetNextRun(ViewPort.Tools.TableTool.Address);
Results.AddRange(GetTableChildren(arrayRun));
}
public void Visit(MatchedWord word, byte data) => Results.AddRange(GetFormattedChildren());
- private IEnumerable GetTableChildren(ArrayRun array) {
+ private IEnumerable GetTableChildren(ITableRun array) {
if (ViewPort.Tools.TableTool.Append.CanExecute(null)) {
yield return new ContextItem("Extend Table", ViewPort.Tools.TableTool.Append.Execute);
}
diff --git a/src/HexManiac.Core/ViewModels/Visitors/StartCellEdit.cs b/src/HexManiac.Core/ViewModels/Visitors/StartCellEdit.cs
index 44860dee..7cc79f13 100644
--- a/src/HexManiac.Core/ViewModels/Visitors/StartCellEdit.cs
+++ b/src/HexManiac.Core/ViewModels/Visitors/StartCellEdit.cs
@@ -97,7 +97,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
public void Visit(PCS pcs, byte data) {
// don't let it start with a space unless it's in quotes (for copy/paste)
var run = Model.GetNextRun(MemoryLocation);
- if (run is ArrayRun array) {
+ if (run is ITableRun array) {
var offsets = array.ConvertByteOffsetToArrayOffset(MemoryLocation);
if (offsets.SegmentStart == MemoryLocation && Input == ' ') return;
}
@@ -124,7 +124,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Visitors {
public void Visit(IntegerEnum integer, byte data) {
if (!integer.CanStartWithCharacter(Input)) return;
- var arrayRun = (ArrayRun)Model.GetNextRun(MemoryLocation);
+ var arrayRun = (ITableRun)Model.GetNextRun(MemoryLocation);
var offsets = arrayRun.ConvertByteOffsetToArrayOffset(MemoryLocation);
var segment = (ArrayRunEnumSegment)arrayRun.ElementContent[offsets.SegmentIndex];
var allOptions = segment.GetOptions(Model).Select(option => option + " ");
diff --git a/src/HexManiac.Tests/TableTests.cs b/src/HexManiac.Tests/TableTests.cs
index 52198413..db89e3e2 100644
--- a/src/HexManiac.Tests/TableTests.cs
+++ b/src/HexManiac.Tests/TableTests.cs
@@ -1,5 +1,7 @@
using HavenSoft.HexManiac.Core.Models;
+using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.Tools;
+using System;
using System.Linq;
using Xunit;
@@ -17,5 +19,52 @@ namespace HavenSoft.HexManiac.Tests {
// a combobox is used for numeric limit fields
ViewPort.Tools.TableTool.Children.Single(child => child is ComboBoxArrayElementViewModel);
}
+
+ [Fact]
+ public void TrainerPokemonTeamEnumSelectionSelectsEntireEnum() {
+ ArrangeTrainerPokemonTeamData(0, 1);
+
+ ViewPort.SelectionStart = new Point(0, 6);
+
+ Assert.Equal(new Point(1, 6), ViewPort.SelectionEnd);
+ }
+
+ [Fact]
+ public void HexEditingWorksForTrainerPokemon() {
+ ArrangeTrainerPokemonTeamData(0, 1);
+
+ ViewPort.SelectionStart = new Point(TrainerPokemonTeamRun.PokemonFormat_PokemonStart, 6);
+ ViewPort.Edit("C ");
+
+ Assert.Equal(2, Model[0x60 + TrainerPokemonTeamRun.PokemonFormat_PokemonStart]);
+ }
+
+ [Fact]
+ public void CanExtendTrainerTeamViaStream() {
+ ArrangeTrainerPokemonTeamData(0, 1);
+ Model[0x6C] = 0x0B; // add a random data value so that extending will cause moving
+
+ ViewPort.SelectionStart = new Point(0, 6);
+ var streamTool = ViewPort.Tools.StringTool;
+ streamTool.Content = $"10 A{Environment.NewLine}10 B";
+
+ Assert.NotEqual(0x60, Model.ReadPointer(0x24));
+ }
+
+ private void ArrangeTrainerPokemonTeamData(byte structType, byte pokemonCount) {
+ CreateTextTable(EggMoveRun.PokemonNameTable, 0x100, "ABCDEFGHIJKLMNOP".Select(c => c.ToString()).ToArray());
+ CreateTextTable(EggMoveRun.MoveNamesTable, 0x140, "abcdefghijklmnop".Select(c => c.ToString()).ToArray());
+ CreateTextTable(HardcodeTablesModel.ItemsTableName, 0x180, "0123456789".Select(c => c.ToString()).ToArray());
+
+ Model[TrainerPokemonTeamRun.TrainerFormat_StructTypeOffset] = structType;
+ Model[TrainerPokemonTeamRun.TrainerFormat_PokemonCountOffset] = pokemonCount;
+ Model.WritePointer(new ModelDelta(), TrainerPokemonTeamRun.TrainerFormat_PointerOffset, 0x60);
+
+ ViewPort.Goto.Execute("00");
+ ViewPort.SelectionStart = new Point(4, 2);
+ ViewPort.Edit($"^trainertable[team<{TrainerPokemonTeamRun.SharedFormatString}>]1 ");
+
+ ViewPort.Goto.Execute("00");
+ }
}
}