/// <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);
                        }
                    }
                }
Exemple #2
0
        private static ISpriteRun Resize(IDataModel model, ModelDelta token, ISpriteRun spriteRun, int tileWidth, int tileHeight)
        {
            if (spriteRun == null)
            {
                return(spriteRun);
            }
            spriteRun = model.RelocateForExpansion(token, spriteRun, tileWidth * tileHeight * TileSize);
            var format = spriteRun.SpriteFormat;

            // extract existing tile data
            var existingTiles = new byte[format.TileWidth, format.TileHeight][];

            for (int x = 0; x < format.TileWidth; x++)
            {
                for (int y = 0; y < format.TileHeight; y++)
                {
                    var tileIndex = y * format.TileWidth + x;
                    existingTiles[x, y] = new byte[TileSize];
                    Array.Copy(model.RawData, spriteRun.Start + tileIndex * TileSize, existingTiles[x, y], 0, TileSize);
                }
            }

            // rewrite it with the new dimensions
            for (int x = 0; x < tileWidth; x++)
            {
                for (int y = 0; y < tileHeight; y++)
                {
                    var tileIndex = y * tileWidth + x;
                    var start     = spriteRun.Start + tileIndex * TileSize;
                    for (int i = 0; i < TileSize; i++)
                    {
                        if (x < format.TileWidth && y < format.TileHeight)
                        {
                            token.ChangeData(model, start + i, existingTiles[x, y][i]);
                        }
                        else
                        {
                            token.ChangeData(model, start + i, 0);
                        }
                    }
                }
            }

            return(spriteRun);
        }
Exemple #3
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));
            }