public override IDataFormat CreateDataFormat(IDataModel data, int index) { var offsets = ConvertByteOffsetToArrayOffset(index); var currentSegment = ElementContent[offsets.SegmentIndex]; if (currentSegment.Type == ElementContentType.PCS) { if (currentCachedStartIndex != offsets.SegmentStart || currentCachedIndex > offsets.SegmentOffset) { currentCachedStartIndex = offsets.SegmentStart; currentCachedIndex = offsets.SegmentOffset; cachedCurrentString = PCSString.Convert(data, offsets.SegmentStart, currentSegment.Length); } return(PCSRun.CreatePCSFormat(data, offsets.SegmentStart, index, cachedCurrentString)); } var position = index - offsets.SegmentStart; if (currentSegment.Type == ElementContentType.Integer) { if (currentSegment is ArrayRunEnumSegment enumSegment) { var value = enumSegment.ToText(data, index); return(new IntegerEnum(offsets.SegmentStart, position, value, currentSegment.Length)); } else { var value = ArrayRunElementSegment.ToInteger(data, offsets.SegmentStart, currentSegment.Length); return(new Integer(offsets.SegmentStart, position, value, currentSegment.Length)); } } if (currentSegment.Type == ElementContentType.Pointer) { var destination = data.ReadPointer(offsets.SegmentStart); var destinationName = data.GetAnchorFromAddress(offsets.SegmentStart, destination); return(new Pointer(offsets.SegmentStart, position, destination, destinationName)); } if (currentSegment.Type == ElementContentType.BitArray) { return(new BitArray(offsets.SegmentStart, position, currentSegment.Length)); } throw new NotImplementedException(); }
public TilemapTableRun(IDataModel model, string anchorName, ArrayRunElementSegment segment, TilemapMargins margins, int start, SortedSpan <int> sources = null) : base(start, sources) { this.model = model; TilemapAnchor = anchorName; Segment = segment; Margins = margins; var tilemapAddress = model.GetAddressFromAnchor(new NoDataChangeDeltaModel(), -1, anchorName); var tilemap = model.GetNextRun(tilemapAddress) as ITilemapRun; if (tilemap != null) { var height = tilemap.SpriteFormat.TileHeight + margins.Top + margins.Bottom; var width = tilemap.SpriteFormat.TileWidth + margins.Left + margins.Right; ElementCount = height * margins.LengthMultiplier; ElementLength = width * Segment.Length; ElementContent = width.Range().Select(i => segment).ToArray(); } Length = ElementCount * ElementLength; FormatString = $"[{segment.SerializeFormat}]{anchorName}{Margins}"; }
private static bool DataMatchesSegmentFormat(IDataModel owner, int start, ArrayRunElementSegment segment, FormatMatchFlags flags, IFormattedRun nextAnchor) { if (start + segment.Length > nextAnchor.Start && nextAnchor is ArrayRun) { return(false); // don't blap over existing arrays } switch (segment.Type) { case ElementContentType.PCS: int readLength = PCSString.ReadString(owner, start, true, segment.Length); if (readLength < 2) { return(false); } if (readLength > segment.Length) { return(false); } if (Enumerable.Range(start, segment.Length).All(i => owner[i] == 0xFF)) { return(false); } // if we end with a space, and the next one starts with a space, we probably have the data width wrong. // We might be the start of a different data segment that is no longer pointed to. (Example: Vega/pokenames) // only do this check if the current element seems useful var isBlank = Enumerable.Range(start, segment.Length).All(i => owner[i] == 0x00 || owner[i] == 0xFF); if (!isBlank && flags.HasFlag(FormatMatchFlags.IsSingleSegment) && start % 4 == 0 && owner[start + segment.Length - 1] == 0x00 && owner[start + segment.Length] == 0x00) { // if the next one starts on a 4-byte boundary, then we probably just skipped a few bytes between different data types, and _this_ section is still part of the _last_ run (example, Emerald Ability names) // if the next one doesn't start on a 4-byte boundary, then we probably have the length wrong var nextWordStart = (start + segment.Length + 3) / 4 * 4; if (Enumerable.Range(start + segment.Length, nextWordStart - start - segment.Length).Any(i => owner[i] != 0x00) || owner[nextWordStart] == 0x00) { return(false); } } // require that the overall thing still ends with 'FF' or '00' to avoid finding text of the wrong width. // the width check is less important if we have more complex data, so relax the condition (example: Clover) // the width check is less important if we're already known to be in a long run (example: Gaia moves) var lastByteInText = owner[start + segment.Length - 1]; var lastByteIsReasonablEnd = lastByteInText == 0x00 || lastByteInText == 0xFF; if (!flags.HasFlag(FormatMatchFlags.AllowJunkAfterText) && !lastByteIsReasonablEnd && flags.HasFlag(FormatMatchFlags.IsSingleSegment)) { return(false); } return(true); case ElementContentType.Integer: if (segment is ArrayRunEnumSegment enumSegment) { return(owner.ReadMultiByteValue(start, segment.Length) < enumSegment.GetOptions(owner).Count); } else { return(true); } case ElementContentType.Pointer: var destination = owner.ReadPointer(start); if (destination == Pointer.NULL) { return(true); } if (0 > destination || destination > owner.Count) { return(false); } if (segment is ArrayRunPointerSegment pointerSegment) { if (!pointerSegment.DestinationDataMatchesPointerFormat(owner, new NoDataChangeDeltaModel(), destination)) { return(false); } } return(true); case ElementContentType.BitArray: var bitArraySegment = (ArrayRunBitArraySegment)segment; var bits = bitArraySegment.GetOptions(owner).Count; bits %= 8; if (bits == 0) { return(true); } var finalByte = owner[start + bitArraySegment.Length - 1]; finalByte >>= bits; return(finalByte == 0); // all the unneeded bits should be set to zero default: throw new NotImplementedException(); } }