diff --git a/src/HexManiac.Core/Models/Code/constantReference.txt b/src/HexManiac.Core/Models/Code/constantReference.txt index 21c01d28..d6b84fe0 100644 --- a/src/HexManiac.Core/Models/Code/constantReference.txt +++ b/src/HexManiac.Core/Models/Code/constantReference.txt @@ -25,14 +25,14 @@ BPEE0.scripts.shiny.odds-1 067C56,06E76C,06E7E2,06EBE4,172F46 # -1: GetHoennPokedexCount,GetPokedexRatingText # -2: GetPokedexRatingText # -3: HasAllHoennMons,GetPokedexRatingText -AXPV0.scripts.pokedex.regional.length 0406FA,08D6BA,08D6E2,10D5E8 -AXPV0.scripts.pokedex.regional.length-1 090FB4,10D5A8 -AXPV0.scripts.pokedex.regional.length-2 10D570 -AXPV0.scripts.pokedex.regional.length-3 090FE6,10D56C -AXPV1.scripts.pokedex.regional.length 04071A,08D6DA,08D702,10D608 -AXPV1.scripts.pokedex.regional.length-1 090FD4,10D5C8 -AXPV1.scripts.pokedex.regional.length-2 10D590 -AXPV1.scripts.pokedex.regional.length-3 091006,10D58C +AXVE0.scripts.pokedex.regional.length 0406FA,08D6BA,08D6E2,10D5E8 +AXVE0.scripts.pokedex.regional.length-1 090FB4,10D5A8 +AXVE0.scripts.pokedex.regional.length-2 10D570 +AXVE0.scripts.pokedex.regional.length-3 090FE6,10D56C +AXVE1.scripts.pokedex.regional.length 04071A,08D6DA,08D702,10D608 +AXVE1.scripts.pokedex.regional.length-1 090FD4,10D5C8 +AXVE1.scripts.pokedex.regional.length-2 10D590 +AXVE1.scripts.pokedex.regional.length-3 091006,10D58C AXPE0.scripts.pokedex.regional.length 0406FA,08D6BA,08D6E2,10D5E8 AXPE0.scripts.pokedex.regional.length-1 090FB4,10D5A8 AXPE0.scripts.pokedex.regional.length-2 10D570 @@ -213,3 +213,4 @@ AXVE1.data.pokemon.type.length*8 0A0214,0A0310 AXPE1.data.pokemon.type.length+5 3C1235 AXPE1.data.pokemon.type.length*8 0A0214,0A0310 + diff --git a/src/HexManiac.Core/ViewModels/QuickEditItems/MakeMovesExpandable.cs b/src/HexManiac.Core/ViewModels/QuickEditItems/MakeMovesExpandable.cs index 2bdceb75..2809dc0a 100644 --- a/src/HexManiac.Core/ViewModels/QuickEditItems/MakeMovesExpandable.cs +++ b/src/HexManiac.Core/ViewModels/QuickEditItems/MakeMovesExpandable.cs @@ -23,28 +23,88 @@ namespace HavenSoft.HexManiac.Core.ViewModels.QuickEditItems { return viewPort is IEditableViewPort; } + public static IReadOnlyDictionary GetNumberOfRelearnableMoves = new Dictionary { + { "AXVE0", 0x040574 }, + { "AXPE0", 0x040574 }, + { "AXVE1", 0x040594 }, + { "AXPE1", 0x040594 }, + { "BPRE0", 0x043E2C }, + { "BPGE0", 0x043E2C }, + { "BPRE1", 0x043E40 }, + { "BPGE1", 0x043E40 }, + { "BPEE0", 0x06e25c }, + }; + public static IReadOnlyDictionary MaxLevelUpMoveCountLocations = new Dictionary { // each of these stores the max number of level-up moves, minus 1 + { "AXVE0", new[] { 0x0404E8, 0x040556, 0x0406A4 } }, + { "AXPE0", new[] { 0x0404E8, 0x040556, 0x0406A4 } }, + { "AXVE1", new[] { 0x040508, 0x040576, 0x0406C4 } }, + { "AXPE1", new[] { 0x040508, 0x040576, 0x0406C4 } }, + { "BPRE0", new[] { 0x043DA0, 0x043E0E, 0x043F5C } }, + { "BPGE0", new[] { 0x043DA0, 0x043E0E, 0x043F5C } }, + { "BPRE1", new[] { 0x043DB4, 0x043E22, 0x043F70 } }, + { "BPGE1", new[] { 0x043DB4, 0x043E22, 0x043F70 } }, + { "BPEE0", new[] { 0x06E1D0, 0x06E23E, 0x06E38C } }, + }; + public ErrorInfo Run(IViewPort viewPortInterface) { var viewPort = (IEditableViewPort)viewPortInterface; + var model = viewPort.Model; var token = viewPort.ChangeHistory.CurrentChange; var parser = viewPort.Tools.CodeTool.Parser; - // fix limit for move effects - var error = RefactorMoveByteFieldInTable(parser, viewPort.Model, token, MoveDataTable, "power", 9); - if (error.HasError) return error; - error = RefactorByteToHalfWordInTable(parser, viewPort.Model, token, MoveDataTable, "effect"); + var error = ExpandMoveEffects(parser, model, token); if (error.HasError) return error; - // update limiters - ReplaceAll(parser, viewPort.Model, token, + // update limiters for move names + error = ReplaceAll(parser, viewPort.Model, token, new[] { "mov r0, #177", "lsl r0, r0, #1", "cmp r1, r0" }, new[] { "mov r0, #177", "lsl r0, r0, #9", "cmp r1, r0" }); + if (error.HasError) return error; + + // update max level-up moves from 20 to 40 + var code = model.GetGameCode(); + error = AddStackSpace(parser, viewPort.Model, token, GetNumberOfRelearnableMoves[code], 48, 40); + if (error.HasError) return error; + foreach (var address in MaxLevelUpMoveCountLocations[code]) token.ChangeData(viewPort.Model, address, 40 - 1); // TODO update levelup moves + var table = model.GetTable(LevelMovesTableName) as ArrayRun; viewPort.Refresh(); return ErrorInfo.NoError; } + public static ErrorInfo ExpandMoveEffects(ThumbParser parser, IDataModel model, ModelDelta token) { + // make move effects 2 bytes instead of 1 byte + var table = model.GetTable(MoveDataTable); + var fieldNames = table.ElementContent.Select(seg => seg.Name).ToArray(); + Func shiftField(int i) => () => RefactorMoveByteFieldInTable(parser, model, token, MoveDataTable, fieldNames[i], i + 1); + var error = ChainErrors( + shiftField(8), shiftField(7), shiftField(6), shiftField(5), + shiftField(4), shiftField(3), shiftField(2), shiftField(1), + () => RefactorByteToHalfWordInTable(parser, model, token, MoveDataTable, fieldNames[0])); + if (error.HasError) return error; + + // update offset pointers (because the PP field moved) + foreach (OffsetPointerRun pointerRun in table.PointerSources + .Select(address => model.GetNextRun(address)) + .Where(pointer => pointer is OffsetPointerRun) + ) { + model.WritePointer(token, pointerRun.Start, table.Start + 5); + model.ObserveRunWritten(token, new OffsetPointerRun(pointerRun.Start, 5)); + } + + return ErrorInfo.NoError; + } + + public static ErrorInfo ChainErrors(params Func[] actions) { + foreach (var action in actions) { + var result = action(); + if (result.HasError) return result; + } + return ErrorInfo.NoError; + } + public static ErrorInfo RefactorMoveByteFieldInTable(ThumbParser parser, IDataModel model, ModelDelta token, string tableName, string fieldName, int newOffset) { // setup var table = model.GetTable(tableName) as ArrayRun; @@ -135,6 +195,31 @@ namespace HavenSoft.HexManiac.Core.ViewModels.QuickEditItems { return ErrorInfo.NoError; } + public static ErrorInfo AddStackSpace(ThumbParser parser, IDataModel model, ModelDelta token, int funcStart, int stackAddOffset, int stackAddCount) { + for (int i = funcStart; true; i += 2) { + var commandLine = parser.Parse(model, i, 2).Trim().SplitLines().Last().Trim(); + if (commandLine.Contains("[sp, ")) { + if (commandLine.StartsWith("str ") || commandLine.StartsWith("ldr ")) { + var currentValue = model[i] * 4; + if (currentValue >= stackAddOffset) { + var newValue = (currentValue + stackAddCount) / 4; + if (newValue > 255) return new ErrorInfo($"{i:X6}: Could not add {stackAddCount}, the result would be larger than 1020."); + token.ChangeData(model, i, (byte)newValue); + } + } + } + if (commandLine.Contains(" sp, ")) { + var writer = new TupleSegment(default, 7); + var value = writer.Read(model, i, 0) * 4; + value += stackAddCount; + writer.Write(model, token, i, 0, value / 4); + } + if (commandLine.StartsWith("bx ")) break; + } + + return ErrorInfo.NoError; + } + public static ErrorInfo ReplaceAll(ThumbParser parser, IDataModel model, ModelDelta token, string[] inputCode, string[] outputCode) { var search = parser.Compile(model, 0, inputCode); var replace = parser.Compile(model, 0, outputCode);