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(); }
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(); }
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); }