コード例 #1
0
        public void PokeStatsAreFound(string game)
        {
            var model    = LoadModel(game);
            var noChange = new NoDataChangeDeltaModel();

            var address = model.GetAddressFromAnchor(noChange, -1, "pokestats");
            var run     = (ArrayRun)model.GetNextAnchor(address);

            var firstPokemonStats = model.Skip(run.Start + run.ElementLength).Take(6).ToArray();
            var compareSet        = new[] { 45, 49, 49, 45, 65, 65 }; // Bulbasaur

            if (game.Contains("Vega"))
            {
                compareSet = new[] { 42, 53, 40, 70, 63, 40 }
            }
            ;                                                                      // Nimbleaf
            if (game.Contains("Clover"))
            {
                compareSet = new[] { 56, 60, 55, 50, 47, 50 }
            }
            ;                                                                        // Grasshole
            for (int i = 0; i < compareSet.Length; i++)
            {
                Assert.Equal(compareSet[i], firstPokemonStats[i]);
            }
        }
コード例 #2
0
        public void AbilitiyDescriptionsAreFound(string game)
        {
            var model    = LoadModel(game);
            var noChange = new NoDataChangeDeltaModel();

            var address = model.GetAddressFromAnchor(noChange, -1, "abilitydescriptions");
            var run     = (ArrayRun)model.GetNextAnchor(address);

            if (game.Contains("Clover"))
            {
                Assert.Equal(156, run.ElementCount);
            }
            else if (game.Contains("Gaia"))
            {
                Assert.Equal(188, run.ElementCount);
            }
            else
            {
                Assert.Equal(78, run.ElementCount);
            }

            if (game.Contains("Gaia"))
            {
                return;                     // don't validate description text in Gaia, it's actually invalid.
            }
            for (var i = 0; i < run.ElementCount; i++)
            {
                address = model.ReadPointer(run.Start + i * 4);
                var childRun = model.GetNextRun(address);
                Assert.IsType <PCSRun>(childRun);
            }
        }
コード例 #3
0
        public void MovesNamesAreFound(string game)
        {
            var model    = LoadModel(game);
            var noChange = new NoDataChangeDeltaModel();

            var address = model.GetAddressFromAnchor(noChange, -1, EggMoveRun.MoveNamesTable);
            var run     = (ArrayRun)model.GetNextAnchor(address);

            if (game.Contains("Vega"))
            {
                Assert.Equal(512, run.ElementCount);
            }
            else if (game.Contains("Clover"))
            {
                Assert.Equal(512, run.ElementCount);
            }
            else if (game.Contains("Gaia"))
            {
                Assert.Equal(511, run.ElementCount);
            }
            else
            {
                Assert.Equal(355, run.ElementCount);
            }
        }
コード例 #4
0
        public void TutorsAreFound(string game)
        {
            var model    = LoadModel(game);
            var noChange = new NoDataChangeDeltaModel();

            var movesLocation         = model.GetAddressFromAnchor(noChange, -1, AutoSearchModel.MoveTutors);
            var compatibilityLocation = model.GetAddressFromAnchor(noChange, -1, AutoSearchModel.TutorCompatibility);

            // ruby and sapphire have no tutors
            // Gaia has move tutors, but it does a bunch of custom stuff (multiple tables) so I don't feel bad about not supporting it by default.
            if (game.Contains("Ruby") || game.Contains("Sapphire") || game.Contains("Gaia"))
            {
                Assert.Equal(Pointer.NULL, movesLocation);
                Assert.Equal(Pointer.NULL, compatibilityLocation);
                return;
            }

            var moves         = (ArrayRun)model.GetNextRun(movesLocation);
            var compatibility = (ArrayRun)model.GetNextRun(compatibilityLocation);

            var expectedMoves = game.Contains("Emerald") || game.Contains("Altair") ? 30 : 15;
            var compatibilityElementLength = (int)Math.Ceiling(expectedMoves / 8.0);

            Assert.Equal(expectedMoves, moves.ElementCount);
            Assert.Equal(compatibilityElementLength, compatibility.ElementContent[0].Length);
        }
コード例 #5
0
ファイル: Sprites.cs プロジェクト: haven1433/HexManiacAdvance
        /// <summary>
        /// Uses the hint, as well as this sprite's table location (if any), to find palettes that can be applied to this sprite.
        /// (1) if the sprite's hint is the name of a palette, return that. Example: title screen pokemon sprite/palette pair.
        /// (2) if the sprite's hint is the name of an enum table, use that enum's source as a list of palettes and get the appropriate one from the matching index of the enum table. Example: pokemon icons
        /// (3) if the sprite's hint is a table name followed by a key=value pair, go grab the a palette from the element within that table such that it's key equals that value. Example: Overworld sprites
        /// (4) if the sprite's hint is a table name, return all palettes within the matching index of that table. Example: trainer sprites/palettes.
        /// (5) if the sprite has no hint, return all palettes in arrays with matching length from the same index. Example: pokemon sprites. Leaving it empty allows both normal and shiny palettes to match.
        /// </summary>
        public static IReadOnlyList <IPaletteRun> FindRelatedPalettes(this ISpriteRun spriteRun, IDataModel model, int primarySource = -1, string hint = null, bool includeAllTableIndex = false)
        {
            // find all palettes that could be applied to this sprite run
            var noChange = new NoDataChangeDeltaModel();
            var results  = new List <IPaletteRun>();

            if (spriteRun?.SpriteFormat.BitsPerPixel < 4)
            {
                return(results);                                       // 1- and 2-bit sprites don't have palettes
            }
            hint = hint ?? spriteRun?.SpriteFormat.PaletteHint;
            if (primarySource == -1)
            {
                var pointerCount = spriteRun?.PointerSources?.Count ?? 0;
                for (int i = 0; i < pointerCount; i++)
                {
                    if (!(model.GetNextRun(spriteRun.PointerSources[i]) is ArrayRun))
                    {
                        continue;
                    }
                    primarySource = spriteRun.PointerSources[i];
                    break;
                }
            }
            var spriteTable = model.GetNextRun(primarySource) as ITableRun;
            var offset      = spriteTable?.ConvertByteOffsetToArrayOffset(primarySource) ?? new ArrayOffset(-1, -1, -1, -1);

            if (primarySource < 0)
            {
                offset = new ArrayOffset(-1, -1, -1, -1);
            }

            if (!string.IsNullOrEmpty(hint))
            {
                var address = model.GetAddressFromAnchor(noChange, -1, hint);
                var run     = model.GetNextRun(address);
                if (run is IPaletteRun palRun && palRun.Start == address)
                {
                    // option 1: hint is to a palette
                    results.Add(palRun);
                    return(results);
                }
                else if (run is ArrayRun enumArray && enumArray.ElementContent.Count == 1 && enumArray.ElementContent[0] is ArrayRunEnumSegment enumSegment)
                {
                    // option 2: hint is to index into paletteTable, and I'm in a table
                    var paletteTable = model.GetNextRun(model.GetAddressFromAnchor(noChange, -1, enumSegment.EnumName)) as ITableRun;
                    if (offset.ElementIndex != -1 && paletteTable != null)
                    {
                        var paletteIndex = model.ReadMultiByteValue(enumArray.Start + enumArray.ElementLength * offset.ElementIndex, enumArray.ElementLength);
                        var destination  = model.ReadPointer(paletteTable.Start + paletteTable.ElementLength * paletteIndex);
                        var tempRun      = model.GetNextRun(destination);
                        if (tempRun is IPaletteRun pRun && pRun.Start == destination)
                        {
                            results.Add(pRun);
                        }
                    }
                }
コード例 #6
0
        public void LvlUpMovesAreFound(string game)
        {
            var model    = LoadModel(game);
            var noChange = new NoDataChangeDeltaModel();

            var address = model.GetAddressFromAnchor(noChange, -1, "lvlmoves");
            var run     = (ArrayRun)model.GetNextAnchor(address);

            Assert.NotNull(run);
        }
コード例 #7
0
        public ErrorInfo Run(IViewPort viewPortInterface)
        {
            var noChange = new NoDataChangeDeltaModel();
            var viewPort = (ViewPort)viewPortInterface;
            var model    = viewPort.Model;
            var token    = viewPort.CurrentChange;

            Run(model, token);

            return(ErrorInfo.NoError);
        }
コード例 #8
0
        public void ItemsAreFound(string game)
        {
            var model    = LoadModel(game);
            var noChange = new NoDataChangeDeltaModel();

            var address = model.GetAddressFromAnchor(noChange, -1, "items");
            var run     = (ArrayRun)model.GetNextAnchor(address);

            if (game.Contains("Altair"))
            {
                Assert.Equal(377, run.ElementCount);
            }
            else if (game.Contains("Emerald"))
            {
                Assert.Equal(377, run.ElementCount);
            }
            else if (game.Contains("FireRed"))
            {
                Assert.Equal(375, run.ElementCount);
            }
            else if (game.Contains("DarkRisingKAIZO"))
            {
                Assert.Equal(375, run.ElementCount);
            }
            else if (game.Contains("LeafGreen"))
            {
                Assert.Equal(375, run.ElementCount);
            }
            else if (game.Contains("Ruby"))
            {
                Assert.Equal(349, run.ElementCount);
            }
            else if (game.Contains("Sapphire"))
            {
                Assert.Equal(349, run.ElementCount);
            }
            else if (game.Contains("Vega"))
            {
                Assert.Equal(375, run.ElementCount);
            }
            else if (game.Contains("Clover"))
            {
                Assert.Equal(375, run.ElementCount);
            }
            else if (game.Contains("Gaia"))
            {
                Assert.Equal(375, run.ElementCount);
            }
            else
            {
                throw new NotImplementedException();
            }
        }
コード例 #9
0
        public bool CanRun(IViewPort viewPortInterface)
        {
            // require that I have a tab with real data, not a search tab or a diff tab or something
            if (!(viewPortInterface is ViewPort viewPort))
            {
                return(false);
            }

            // require that we fan find the specials table and that it's long enough
            var token           = new NoDataChangeDeltaModel();
            var specialsAddress = viewPort.Model.GetAddressFromAnchor(token, -1, HardcodeTablesModel.SpecialsTable);

            if (specialsAddress < 0 || specialsAddress > viewPort.Model.Count)
            {
                return(false);
            }
            var specials = viewPort.Model.GetNextRun(specialsAddress) as ITableRun;

            if (specials == null)
            {
                return(false);
            }
            if (specials.ElementCount < 397)
            {
                return(false);
            }

            // require that this data actually supports this change
            var model    = viewPort.Model;
            var gameCode = model.GetGameCode();

            var(getTutorMove, canPokemonLearnTutorMove, _, _) = GetOffsets(gameCode);
            if (getTutorMove < 0 || canPokemonLearnTutorMove < 0)
            {
                return(false);
            }

            // require that this data has a tutormoves and tutorcompatibility table, since we're messing with those
            var tutormoves         = model.GetAddressFromAnchor(token, -1, MoveTutors);
            var tutorcompatibility = model.GetAddressFromAnchor(token, -1, TutorCompatibility);

            if (tutormoves == Pointer.NULL || tutorcompatibility == Pointer.NULL)
            {
                return(false);
            }

            // if the patch has already been applied, you can't apply it again
            if (viewPort.Model.GetNextRun(canPokemonLearnTutorMove + 0x20) is WordRun)
            {
                return(false);
            }
            return(true);
        }
コード例 #10
0
        public void MoveDataFound(string game)
        {
            var model    = LoadModel(game);
            var noChange = new NoDataChangeDeltaModel();

            var address = model.GetAddressFromAnchor(noChange, -1, "movedata");
            var run     = (ArrayRun)model.GetNextAnchor(address);

            var poundStats = model.Skip(run.Start + run.ElementLength).Take(8).ToArray();
            var compareSet = new[] { 0, 40, 0, 100, 35, 0, 0, 0 };

            for (int i = 0; i < compareSet.Length; i++)
            {
                Assert.Equal(compareSet[i], poundStats[i]);
            }
        }
コード例 #11
0
        public bool CanRun(IViewPort viewPortInterface)
        {
            var noChange = new NoDataChangeDeltaModel();
            var viewPort = viewPortInterface as ViewPort;

            if (viewPort == null)
            {
                return(false);
            }
            var model = viewPort.Model;

            ArrayRun get(string name) => model.GetNextRun(model.GetAddressFromAnchor(noChange, -1, name)) as ArrayRun;

            var regional = get(HardcodeTablesModel.RegionalDexTableName);
            var national = get(HardcodeTablesModel.NationalDexTableName);
            var convert  = get(HardcodeTablesModel.ConversionDexTableName);

            if (regional is null || national is null || convert is null)
            {
                return(false);
            }
            if (regional.ElementContent.Count != 1 || national.ElementContent.Count != 1 || convert.ElementContent.Count != 1)
            {
                return(false);
            }
            if (regional.ElementCount != national.ElementCount || regional.ElementCount != convert.ElementCount)
            {
                return(false);
            }
            if (regional.ElementLength != 2 || national.ElementLength != 2 || convert.ElementLength != 2)
            {
                return(false);
            }

            for (int i = 0; i < regional.ElementCount; i++)
            {
                var regionalIndex   = model.ReadMultiByteValue(regional.Start + i * 2, 2);
                var nationalIndex   = model.ReadMultiByteValue(national.Start + i * 2, 2);
                var conversionIndex = model.ReadMultiByteValue(convert.Start + (regionalIndex - 1) * 2, 2);
                if (nationalIndex != conversionIndex)
                {
                    return(true);
                }
            }

            return(false);
        }
コード例 #12
0
        public void TrainerClassNamesAreFound(string game)
        {
            var model    = LoadModel(game);
            var noChange = new NoDataChangeDeltaModel();

            var address = model.GetAddressFromAnchor(noChange, -1, "trainerclassnames");
            var run     = (ArrayRun)model.GetNextAnchor(address);

            if (game.Contains("Altair"))
            {
                Assert.Equal(66, run.ElementCount);
            }
            else if (game.Contains("Emerald"))
            {
                Assert.Equal(66, run.ElementCount);
            }
            else if (game.Contains("FireRed"))
            {
                Assert.Equal(107, run.ElementCount);
            }
            else if (game.Contains("DarkRisingKAIZO"))
            {
                Assert.Equal(107, run.ElementCount);
            }
            else if (game.Contains("LeafGreen"))
            {
                Assert.Equal(107, run.ElementCount);
            }
            else if (game.Contains("Ruby"))
            {
                Assert.Equal(58, run.ElementCount);
            }
            else if (game.Contains("Sapphire"))
            {
                Assert.Equal(58, run.ElementCount);
            }
            else if (game.Contains("Vega"))
            {
                Assert.Equal(107, run.ElementCount);
            }
        }
コード例 #13
0
        public void AbilitiyNamesAreFound(string game)
        {
            var model    = LoadModel(game);
            var noChange = new NoDataChangeDeltaModel();

            var address = model.GetAddressFromAnchor(noChange, -1, "abilitynames");
            var run     = (ArrayRun)model.GetNextAnchor(address);

            if (game.Contains("Clover"))
            {
                Assert.Equal(156, run.ElementCount);
            }
            else if (game.Contains("Gaia"))
            {
                Assert.Equal(188, run.ElementCount);
            }
            else
            {
                Assert.Equal(78, run.ElementCount);
            }
        }
コード例 #14
0
        public void EggMoveDataFound(string game)
        {
            var model    = LoadModel(game);
            var noChange = new NoDataChangeDeltaModel();

            var address = model.GetAddressFromAnchor(noChange, -1, "eggmoves");
            var run     = (EggMoveRun)model.GetNextAnchor(address);

            if (game.Contains("Vega"))
            {
                Assert.Equal(3, run.PointerSources.Count);                     // there's a false positive in Vega... for now! Would be nice if this were 2, but it doesn't much matter.
            }
            else
            {
                Assert.Equal(2, run.PointerSources.Count);
            }
            var expectedLastElement = model.ReadMultiByteValue(run.PointerSources[1] - 4, 4);
            var expectedLength      = expectedLastElement + 1;
            var actualLength        = run.Length / 2 - 1; // remove the closing element.

            Assert.InRange(actualLength, 790, expectedLength);
        }
コード例 #15
0
        public bool CanRun(IViewPort viewPort)
        {
            if (!File.Exists(ExpandLevelUpMovesCode))
            {
                return(false);
            }
            if (!(viewPort is IEditableViewPort editableViewPort))
            {
                return(false);
            }

            var noChange = new NoDataChangeDeltaModel();

            if (viewPort.Model.GetAddressFromAnchor(noChange, -1, LevelMovesTableName) == Pointer.NULL)
            {
                return(false);
            }
            if (viewPort.Model.GetAddressFromAnchor(noChange, -1, MoveDataTable) == Pointer.NULL)
            {
                return(false);
            }
            return(true);
        }
コード例 #16
0
        public async Task <ErrorInfo> Run(IViewPort viewPortInterface)
        {
            var viewPort = (ViewPort)viewPortInterface;
            var model    = viewPort.Model;
            var token    = new NoDataChangeDeltaModel();
            var gameCode = model.GetGameCode();

            var(getTutorMove, canPokemonLearnTutorMove, getTutorMove_Length, canPokemonLearnTutorMove_Length) = GetOffsets(gameCode);
            var specialsAddress = model.GetAddressFromAnchor(token, -1, SpecialsTable);
            var tutorSpecial    = model.ReadPointer(specialsAddress + 397 * 4); // Emerald tutors is actually special 477, but we don't need to edit it so it doesn't matter.

            tutorSpecial -= 1;                                                  // the pointer is to thumb code, so it's off by one.

            var tutormoves         = model.GetAddressFromAnchor(token, -1, MoveTutors);
            var tutorcompatibility = model.GetAddressFromAnchor(token, -1, TutorCompatibility);

            InsertRoutine_GetTutorMove(viewPort, getTutorMove, getTutorMove_Length);
            InsertRoutine_CanPokemonLearnTutorMove(viewPort, canPokemonLearnTutorMove, canPokemonLearnTutorMove_Length);
            UpdateRoutine_TutorSpecial(viewPort, tutorSpecial, gameCode);

            CanRunChanged?.Invoke(this, EventArgs.Empty);

            return(ErrorInfo.NoError);
        }
コード例 #17
0
        private static int KnownLengthSearch(IDataModel data, List <ArrayRunElementSegment> elementContent, int elementLength, string lengthToken, out int bestLength, Func <IFormattedRun, bool> runFilter)
        {
            var noChange = new NoDataChangeDeltaModel();

            if (!int.TryParse(lengthToken, out bestLength))
            {
                var matchedArrayName    = lengthToken;
                var matchedArrayAddress = data.GetAddressFromAnchor(noChange, -1, matchedArrayName);
                if (matchedArrayAddress == Pointer.NULL)
                {
                    return(Pointer.NULL);
                }
                var matchedRun = data.GetNextRun(matchedArrayAddress) as ArrayRun;
                if (matchedRun == null)
                {
                    return(Pointer.NULL);
                }
                bestLength = matchedRun.ElementCount;
            }

            FormatMatchFlags flags = default;

            if (elementContent.Count == 1)
            {
                flags |= FormatMatchFlags.IsSingleSegment;
            }

            for (var run = data.GetNextRun(0); run.Start < data.Count; run = data.GetNextRun(run.Start + run.Length + 1))
            {
                if (!(run is PointerRun))
                {
                    continue;
                }
                var targetRun = data.GetNextRun(data.ReadPointer(run.Start));
                if (targetRun is ArrayRun)
                {
                    continue;
                }

                // some searches allow special conditions on the run. For example, we could only be intersted in runs with >100 pointers leading to it.
                if (runFilter != null && !runFilter(targetRun))
                {
                    continue;
                }

                // tolerate a few errors in the data. We know what length we're looking for, so if most of the elements match, then
                // most likely we're just looking at the right collection but with some user-created bugs.
                int  errorsToTolerate = bestLength / 80;
                int  encounterErrors  = 0;
                int  lastGoodLength   = 0;
                int  currentLength    = 0;
                int  currentAddress   = targetRun.Start;
                bool earlyExit        = false;
                for (int i = 0; i < bestLength; i++)
                {
                    var  nextArray = data.GetNextAnchor(currentAddress + 1);
                    bool match     = DataMatchesElementFormat(data, currentAddress, elementContent, flags, nextArray);
                    currentLength++;
                    currentAddress += elementLength;
                    if (match)
                    {
                        lastGoodLength = currentLength;
                    }
                    else
                    {
                        encounterErrors++;
                        if (encounterErrors > errorsToTolerate)
                        {
                            // as long as this array is at least 80% of the passed in array, we're fine and can say that these are matched.
                            // (the other one might have bad data at the end that needs to be removed) (example: see Gaia)
                            earlyExit = bestLength * .8 > lastGoodLength;
                            break;
                        }
                    }
                }
                currentLength = lastGoodLength;

                if (!earlyExit)
                {
                    var dataEmpty = Enumerable.Range(targetRun.Start, currentLength * elementLength).Select(i => data[i]).All(d => d == 0xFF || d == 0x00);
                    if (dataEmpty)
                    {
                        continue;       // don't accept the run if it contains no data
                    }
                    bestLength = currentLength;
                    return(targetRun.Start);
                }
            }

            return(Pointer.NULL);
        }
コード例 #18
0
        /// <summary>
        /// Uses the hint, as well as this sprite's table location (if any), to find palettes that can be applied to this sprite.
        /// (1) if the sprite's hint is the name of a palette, return that. Example: title screen pokemon sprite/palette pair.
        /// (2) if the sprite's hint is the name of an enum table, use that enum's source as a list of palettes and get the appropriate one from the matching index of the enum table. Example: pokemon icons
        /// (3) if the sprite's hint is a table name followed by a key=value pair, go grab the a palette from the element within that table such that it's key equals that value. Example: Overworld sprites
        /// (4) if the sprite's hint is a table name, return all palettes within the matching index of that table. Example: trainer sprites/palettes.
        /// (5) if the sprite has no hint, return all palettes in arrays with matching length from the same index. Example: pokemon sprites. Leaving it empty allows both normal and shiny palettes to match.
        /// </summary>
        public static IReadOnlyList <IPaletteRun> FindRelatedPalettes(this ISpriteRun spriteRun, IDataModel model, int primarySource = -1, string hint = null)
        {
            // find all palettes that could be applied to this sprite run
            var noChange = new NoDataChangeDeltaModel();
            var results  = new List <IPaletteRun>();

            hint = hint ?? spriteRun?.SpriteFormat.PaletteHint;
            if (primarySource == -1)
            {
                var pointerCount = spriteRun?.PointerSources?.Count ?? 0;
                for (int i = 0; i < pointerCount; i++)
                {
                    if (!(model.GetNextRun(spriteRun.PointerSources[i]) is ArrayRun))
                    {
                        continue;
                    }
                    primarySource = spriteRun.PointerSources[i];
                    break;
                }
            }
            var spriteTable = model.GetNextRun(primarySource) as ArrayRun;
            var offset      = spriteTable?.ConvertByteOffsetToArrayOffset(primarySource) ?? new ArrayOffset(-1, -1, -1, -1);

            if (primarySource < 0)
            {
                offset = new ArrayOffset(-1, -1, -1, -1);
            }

            if (!string.IsNullOrEmpty(hint))
            {
                var run = model.GetNextRun(model.GetAddressFromAnchor(noChange, -1, hint));
                if (run is IPaletteRun palRun)
                {
                    // option 1: hint is to a palette
                    results.Add(palRun);
                    return(results);
                }
                else if (run is ArrayRun enumArray && enumArray.ElementContent.Count == 1 && enumArray.ElementContent[0] is ArrayRunEnumSegment enumSegment)
                {
                    // option 2: hint is to index into paletteTable, and I'm in a table
                    var paletteTable = model.GetNextRun(model.GetAddressFromAnchor(noChange, -1, enumSegment.EnumName)) as ArrayRun;
                    if (offset.ElementIndex != -1)
                    {
                        var paletteIndex = model.ReadMultiByteValue(enumArray.Start + enumArray.ElementLength * offset.ElementIndex, enumArray.ElementLength);
                        if (model.GetNextRun(model.ReadPointer(paletteTable.Start + paletteTable.ElementLength * paletteIndex)) is IPaletteRun pRun)
                        {
                            results.Add(pRun);
                        }
                    }
                }
                else if (hint.Contains(":"))
                {
                    // option 3: hint is a table name, followed by a identifier=value pair
                    var tableKeyPair        = hint.Split(':');
                    var identifierValuePair = tableKeyPair.Length == 2 ? tableKeyPair[1].Split("=") : new string[0];
                    if (identifierValuePair.Length == 2)
                    {
                        var paletteTable = model.GetNextRun(model.GetAddressFromAnchor(noChange, -1, tableKeyPair[0])) as ITableRun;
                        var segment      = paletteTable?.ElementContent.FirstOrDefault(seg => seg.Name == identifierValuePair[0]);
                        var pSegment     = paletteTable?.ElementContent.FirstOrDefault(seg => seg is ArrayRunPointerSegment pSeg && PaletteRun.TryParsePaletteFormat(pSeg.InnerFormat, out var _));
                        var rawValue     = identifierValuePair[1];
                        int keyValue;
                        if (segment is ArrayRunEnumSegment eSegment)
                        {
                            keyValue = eSegment.GetOptions(model).ToList().IndexOf(rawValue);
                        }
                        else if (segment is ArrayRunHexSegment hSegment)
                        {
                            int.TryParse(rawValue, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out keyValue);
                        }
                        else
                        {
                            int.TryParse(rawValue, out keyValue);
                        }
                        if (segment != null)
                        {
                            var segmentOffset  = paletteTable.ElementContent.Until(seg => seg == segment).Sum(seg => seg.Length);
                            var pSegmentOffset = paletteTable.ElementContent.Until(seg => seg == pSegment).Sum(seg => seg.Length);
                            var tableIndex     = Enumerable.Range(0, paletteTable.ElementCount).FirstOrDefault(i => model.ReadMultiByteValue(paletteTable.Start + i * paletteTable.ElementLength + segmentOffset, segment.Length) == keyValue);
                            var paletteStart   = model.ReadPointer(paletteTable.Start + tableIndex * paletteTable.ElementLength + pSegmentOffset);
                            if (model.GetNextRun(paletteStart) is IPaletteRun pRun)
                            {
                                results.Add(pRun);
                            }
                        }
                    }
                }

                // option 4: I'm in a table, and my hint is a table name
                if (offset.ElementIndex == -1)
                {
                    return(results);
                }
                if (!(run is ArrayRun array))
                {
                    return(results);
                }
                results.AddRange(model.GetPointedChildren <IPaletteRun>(array, offset.ElementIndex));
            }