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();
        }
Example #2
0
        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();
            }
        }