public OverworldSpriteListRun(IDataModel model, IReadOnlyList <ArrayRunElementSegment> parent, int start, SortedSpan <int> sources = null) : base(start, sources)
        {
            this.model  = model;
            this.parent = parent;
            var segments = new List <ArrayRunElementSegment> {
                new ArrayRunPointerSegment("sprite", "`ucs4x1x2`"),
                new ArrayRunElementSegment("length", ElementContentType.Integer, 4),
            };

            ElementContent = segments;
            ElementCount   = 1;
            Length         = ElementLength;
            SpriteFormat   = new SpriteFormat(4, 1, 1, string.Empty);

            if (sources == null || sources.Count == 0)
            {
                return;
            }

            // initialize format from parent info
            var listOffset   = GetOffset <ArrayRunPointerSegment>(parent, pSeg => pSeg.InnerFormat == SharedFormatString);
            var widthOffset  = GetOffset(parent, seg => seg.Name == "width");
            var heightOffset = GetOffset(parent, seg => seg.Name == "height");
            var keyOffset    = GetOffset(parent, seg => seg.Name == "paletteid");

            var elementStart = sources[0] - listOffset;
            var width        = Math.Max(1, model.ReadMultiByteValue(elementStart + widthOffset, 2));
            var height       = Math.Max(1, model.ReadMultiByteValue(elementStart + heightOffset, 2));
            var tileWidth    = (int)Math.Max(1, Math.Ceiling(width / 8.0));
            var tileHeight   = (int)Math.Max(1, Math.Ceiling(height / 8.0));
            var key          = model.ReadMultiByteValue(elementStart + keyOffset, 2);
            var hint         = $"overworld.palettes:id={key:X4}";

            var format = $"`ucs4x{width / 8}x{height / 8}|{hint}`";

            segments[0] = new ArrayRunPointerSegment("sprite", format);

            // calculate the element count
            var byteLength      = tileWidth * tileHeight * TileSize;
            var nextAnchorStart = model.GetNextAnchor(Start + 1).Start;

            ElementCount = 0;
            Length       = 0;
            while (Start + Length < nextAnchorStart)
            {
                if (model[start + Length + 3] != 0x08)
                {
                    break;
                }
                if (model.ReadMultiByteValue(start + Length + 4, 4) != byteLength)
                {
                    break;
                }
                ElementCount += 1;
                Length       += ElementLength;
            }

            SpriteFormat = new SpriteFormat(4, tileWidth * ElementCount, tileHeight, hint);
            ElementNames = Enumerable.Range(0, ElementCount).Select(i => string.Empty).ToList();
        }
예제 #2
0
        public OverworldSpriteListRun(IDataModel model, IReadOnlyList <ArrayRunElementSegment> parent, string paletteHint, int runIndex, int start, SortedSpan <int> sources = null) : base(start, sources)
        {
            this.model  = model;
            this.parent = parent;
            PaletteHint = paletteHint;
            RunIndex    = runIndex;

            var nextStartBuilder = new List <int>();
            int parentLength     = parent.Sum(seg => seg.Length);

            if (parent != null && sources != null && sources.Count > 0)
            {
                for (int nextSource = sources[0] + parentLength; true; nextSource += parentLength)
                {
                    var nextDest = model.ReadPointer(nextSource);
                    if (nextDest < 0 || nextDest >= model.Count)
                    {
                        break;
                    }
                    nextStartBuilder.Add(nextDest);
                }
            }
            var nextStart = nextStartBuilder.ToArray();

            var segments = new List <ArrayRunElementSegment> {
                new ArrayRunPointerSegment("sprite", "`ucs4x1x2`"),
                new ArrayRunElementSegment("length", ElementContentType.Integer, 4),
            };

            ElementContent = segments;
            ElementCount   = 1;
            Length         = ElementLength;
            SpriteFormat   = new SpriteFormat(4, 1, 1, string.Empty);

            if (sources == null || sources.Count == 0)
            {
                return;
            }

            // initialize format from parent info
            var listOffset   = GetOffset <ArrayRunPointerSegment>(parent, pSeg => pSeg.InnerFormat.StartsWith("`osl"));
            var widthOffset  = GetOffset(parent, seg => seg.Name == "width");
            var heightOffset = GetOffset(parent, seg => seg.Name == "height");
            var keyOffset    = GetOffset(parent, seg => seg.Name == "paletteid");

            if (widthOffset == parentLength)
            {
                widthOffset = -1;
            }
            if (heightOffset == parentLength)
            {
                heightOffset = -1;
            }
            if (keyOffset == parentLength)
            {
                keyOffset = -1;
            }

            var elementStart = sources[0] - listOffset;
            var width        = widthOffset >= 0 ? Math.Max(1, model.ReadMultiByteValue(elementStart + widthOffset, 2)) : 0;
            var height       = heightOffset >= 0 ? Math.Max(1, model.ReadMultiByteValue(elementStart + heightOffset, 2)) : 0;
            // if there was no height/width found, assume that it's square and based on the first element length
            var  pixelCount       = model.ReadMultiByteValue(start + 4, 4) * 2; // number of pixels is twice the number of bytes for all OW sprites
            bool adjustDimensions = true;

            if (width == 0)
            {
                width = (int)Math.Sqrt(pixelCount); adjustDimensions = true;
            }
            if (height == 0)
            {
                height = width; adjustDimensions = true;
            }
            var tileWidth  = (int)Math.Max(1, Math.Ceiling(width / 8.0));
            var tileHeight = (int)Math.Max(1, Math.Ceiling(height / 8.0));

            while (adjustDimensions)
            {
                adjustDimensions = false;
                while (tileWidth * tileHeight * 64 > pixelCount)
                {
                    adjustDimensions = true;
                    tileHeight      -= 1;
                }
                if (tileHeight == 0)
                {
                    break;
                }
                while (tileWidth * tileHeight * 64 < pixelCount)
                {
                    if (tileWidth > 500)
                    {
                        break;
                    }
                    adjustDimensions = true;
                    tileWidth       += 1;
                }
            }

            var key  = model.ReadMultiByteValue(elementStart + keyOffset, 2);
            var hint = $"{HardcodeTablesModel.OverworldPalettes}:id={key:X4}";

            if (!string.IsNullOrEmpty(paletteHint))
            {
                hint = PaletteHint + $"={runIndex:X4}";
            }
            if (paletteHint != null && paletteHint.Contains("="))
            {
                hint = PaletteHint + $"{key:X4}";
            }

            var format = $"`ucs4x{tileWidth}x{tileHeight}|{hint}`";

            if (keyOffset == -1 && string.IsNullOrEmpty(paletteHint))
            {
                format = $"`ucs4x{tileWidth}x{tileHeight}`";
                hint   = string.Empty;
            }
            segments[0] = new ArrayRunPointerSegment("sprite", format);

            // calculate the element count
            var byteLength      = tileWidth * tileHeight * TileSize;
            var nextAnchorStart = model.GetNextAnchor(Start + 1).Start;

            ElementCount = 0;
            Length       = 0;
            while (Start + Length < nextAnchorStart)
            {
                if (model[start + Length + 3] != 0x08)
                {
                    break;
                }
                if (model.ReadMultiByteValue(start + Length + 4, 4) != byteLength)
                {
                    break;
                }
                var nextRun = model.GetNextRun(start + Length);
                if (Length > 0 && (start + Length).IsAny(nextStart))
                {
                    break;                                              // metric: if there's a pointer in the parent table that points here, then it's the next list, not this list.
                }
                ElementCount += 1;
                Length       += ElementLength;
                if (ElementCount == MaxOverworldSprites)
                {
                    break;                                  // overworld sprite lists can only have so many elements
                }
            }

            SpriteFormat = new SpriteFormat(4, tileWidth, tileHeight, hint);
            ElementNames = ElementCount.Range().Select(i => string.Empty).ToList();
        }
예제 #3
0
        private static List <ArrayRunElementSegment> ParseSegments(string segments, IDataModel model)
        {
            var list = new List <ArrayRunElementSegment>();

            segments = segments.Trim();
            while (segments.Length > 0)
            {
                int nameEnd = 0;
                while (nameEnd < segments.Length && char.IsLetterOrDigit(segments[nameEnd]))
                {
                    nameEnd++;
                }
                var name = segments.Substring(0, nameEnd);
                if (name == string.Empty)
                {
                    throw new ArrayRunParseException("expected name, but none was found: " + segments);
                }
                segments = segments.Substring(nameEnd);
                var(format, formatLength, segmentLength) = ExtractSingleFormat(segments, model);

                // check to see if a name or length is part of the format
                if (format == ElementContentType.Integer && segments.Length > formatLength && segments[formatLength] != ' ')
                {
                    segments = segments.Substring(formatLength);
                    if (int.TryParse(segments, out var maxValue))
                    {
                        throw new NotImplementedException();
                    }
                    else
                    {
                        var endOfToken = segments.IndexOf(' ');
                        if (endOfToken == -1)
                        {
                            endOfToken = segments.Length;
                        }
                        var enumName = segments.Substring(0, endOfToken);
                        segments = segments.Substring(endOfToken).Trim();
                        list.Add(new ArrayRunEnumSegment(name, segmentLength, enumName));
                    }
                }
                else if (format == ElementContentType.Pointer && formatLength > 2)
                {
                    var pointerSegment = new ArrayRunPointerSegment(name, segments.Substring(1, formatLength - 2));
                    if (!pointerSegment.IsInnerFormatValid)
                    {
                        throw new ArrayRunParseException($"pointer format '{pointerSegment.InnerFormat}' was not understood.");
                    }
                    list.Add(pointerSegment);
                    segments = segments.Substring(formatLength).Trim();
                }
                else if (format == ElementContentType.BitArray)
                {
                    var sourceName = segments.Substring(BitArray.SharedFormatString.Length, formatLength - BitArray.SharedFormatString.Length);
                    segments = segments.Substring(formatLength).Trim();
                    list.Add(new ArrayRunBitArraySegment(name, segmentLength, sourceName));
                }
                else
                {
                    segments = segments.Substring(formatLength).Trim();
                    if (format == ElementContentType.Unknown)
                    {
                        // default to single byte integer
                        format        = ElementContentType.Integer;
                        segmentLength = 1;
                    }
                    list.Add(new ArrayRunElementSegment(name, format, segmentLength));
                }
            }

            return(list);
        }