mirror of
https://github.com/haven1433/HexManiacAdvance.git
synced 2026-10-02 13:16:35 -05:00
Merge branch 'trainer-pokemon-teams' into usability-updates-3-2
This commit is contained in:
@@ -59,6 +59,7 @@
|
||||
<Compile Include="Models\Runs\HeaderRow.cs" />
|
||||
<Compile Include="Models\Runs\ModelCacheScope.cs" />
|
||||
<Compile Include="Models\Runs\PLMRun.cs" />
|
||||
<Compile Include="Models\Runs\TrainerPokemonTeamRun.cs" />
|
||||
<Compile Include="Models\Runs\WordRun.cs" />
|
||||
<Compile Include="ViewModels\AutoCompleteSelectionItem.cs" />
|
||||
<Compile Include="ViewModels\QuickEditItems\IQuickEditItem.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);
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<string>() { model.GetAnchorFromAddress(-1, arrayRun.Start) };
|
||||
var visitedAddress = new List<int>() { arrayRun.Start };
|
||||
var visitedNames = new List<string>() { model.GetAnchorFromAddress(-1, table.Start) };
|
||||
var visitedAddress = new List<int>() { 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<string>();
|
||||
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<int, IFormattedRun> runsToRemove, IReadOnlyDictionary<int, IFormattedRun> runsToAdd, IReadOnlyDictionary<int, string> namesToRemove, IReadOnlyDictionary<int, string> namesToAdd, IReadOnlyDictionary<int, string> unmappedPointersToRemove, IReadOnlyDictionary<int, string> unmappedPointersToAdd, IReadOnlyDictionary<int, string> matchedWordsToRemove, IReadOnlyDictionary<int, string> matchedWordsToAdd) { }
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
@@ -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<string> ElementNames { get; }
|
||||
IReadOnlyList<ArrayRunElementSegment> 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 {
|
||||
/// <summary>
|
||||
/// 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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
285
src/HexManiac.Core/Models/Runs/TrainerPokemonTeamRun.cs
Normal file
285
src/HexManiac.Core/Models/Runs/TrainerPokemonTeamRun.cs
Normal file
@@ -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<int> 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<ArrayRunElementSegment>();
|
||||
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<int> 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<int> newPointerSources) => new TrainerPokemonTeamRun(model, Start, newPointerSources);
|
||||
|
||||
#endregion
|
||||
|
||||
#region ITableRun
|
||||
|
||||
public int ElementCount { get; }
|
||||
|
||||
public int ElementLength { get; }
|
||||
|
||||
public IReadOnlyList<string> ElementNames { get; } = new List<string>();
|
||||
|
||||
public IReadOnlyList<ArrayRunElementSegment> 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<int> levels = new List<int>();
|
||||
private readonly List<int> pokemons = new List<int>();
|
||||
private readonly List<int> ivs = new List<int>();
|
||||
private readonly List<int> items = new List<int>();
|
||||
private readonly List<int> moves = new List<int>();
|
||||
|
||||
public bool ItemsIncluded { get; private set; }
|
||||
public bool MovesIncluded { get; private set; }
|
||||
|
||||
public IReadOnlyList<int> Levels => levels;
|
||||
public IReadOnlyList<int> Pokemon => pokemons;
|
||||
public IReadOnlyList<int> IVs => ivs;
|
||||
public IReadOnlyList<int> Items => items;
|
||||
public IReadOnlyList<int> 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<int> 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<string> itemNames, List<int> 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
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ namespace HavenSoft.HexManiac.Core {
|
||||
|
||||
return true;
|
||||
}
|
||||
public static int IndexOfPartial(this IList<string> names, string input) {
|
||||
public static int IndexOfPartial(this IReadOnlyList<string> names, string input) {
|
||||
// perfect match first
|
||||
var matchIndex = names.IndexOf(input);
|
||||
if (matchIndex != -1) return matchIndex;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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<ModelDelta> 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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<IContextItem> GetTableChildren(ArrayRun array) {
|
||||
private IEnumerable<IContextItem> GetTableChildren(ITableRun array) {
|
||||
if (ViewPort.Tools.TableTool.Append.CanExecute(null)) {
|
||||
yield return new ContextItem("Extend Table", ViewPort.Tools.TableTool.Append.Execute);
|
||||
}
|
||||
|
||||
@@ -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 + " ");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user