Example #1
0
        public override void UpdateNewRunFromPointerFormat(IDataModel model, ModelDelta token, string name, ref IFormattedRun run)
        {
            var length = PCSString.ReadString(model, run.Start, true);

            if (length > 0)
            {
                var newRun = new PCSRun(model, run.Start, length, run.PointerSources);
                if (!newRun.Equals(run))
                {
                    model.ClearFormat(token, newRun.Start, newRun.Length);
                }
                run = newRun;
            }
        }
Example #2
0
        public override void UpdateNewRunFromPointerFormat(IDataModel model, ModelDelta token, string name, IReadOnlyList <ArrayRunElementSegment> sourceSegments, int parentIndex, ref IFormattedRun run)
        {
            var length = PCSString.ReadString(model, run.Start, true);

            if (length > 0)
            {
                var newRun = new PCSRun(model, run.Start, length, run.PointerSources);
                if (!newRun.Equals(run))
                {
                    model.ClearFormat(token, newRun.Start, newRun.Length);
                }
                run = newRun;
            }
        }
Example #3
0
        public override ErrorInfo TryParseData(IDataModel model, string name, int dataIndex, ref IFormattedRun run)
        {
            var length = PCSString.ReadString(model, dataIndex, true);

            if (length < 0)
            {
                return(new ErrorInfo($"Format was specified as a string, but no string was recognized."));
            }
            else if (PokemonModel.SpanContainsAnchor(model, dataIndex, length))
            {
                return(new ErrorInfo($"Format was specified as a string, but a string would overlap the next anchor."));
            }
            run = new PCSRun(model, dataIndex, length);

            return(ErrorInfo.NoError);
        }
Example #4
0
        public void UsingBackspaceMidStringMakesTheStringEndThere()
        {
            SetFullModel(0xFF);
            var(model, viewPort) = (Model, ViewPort);
            for (int i = 0; i < 0x10; i++)
            {
                model[i] = 0x00;
            }

            // add an anchor with some data on the 2nd line
            viewPort.SelectionStart = new Point(0, 1);
            viewPort.Edit("^bob\"\" \"Hello World!\"");
            viewPort.SelectionStart = new Point(6, 1);
            viewPort.Edit(ConsoleKey.Backspace);

            Assert.Equal("\"Hello \"", PCSString.Convert(model, 0x10, PCSString.ReadString(model, 0x10, true)));
        }
Example #5
0
        public override bool TryAddFormatAtDestination(IDataModel owner, ModelDelta token, int source, int destination, string name, IReadOnlyList <ArrayRunElementSegment> sourceSegments)
        {
            var length = PCSString.ReadString(owner, destination, true);

            if (length < 1)
            {
                return(false);
            }

            // our token will be a no-change token if we're in the middle of exploring the data.
            // If so, don't actually add the run. It's enough to know that we _can_ add the run.
            if (!(token is NoDataChangeDeltaModel))
            {
                owner.ObserveRunWritten(token, new PCSRun(owner, destination, length));
            }

            // even if we didn't add the format, we're _capable_ of adding it... so return true
            return(true);
        }
        public void UsingBackspaceMidStringMakesTheStringEndThere()
        {
            var buffer = Enumerable.Repeat((byte)0xFF, 0x200).ToArray();

            for (int i = 0; i < 0x10; i++)
            {
                buffer[i] = 0x00;
            }
            var model    = new PokemonModel(buffer);
            var viewPort = new ViewPort("test.txt", model)
            {
                Width = 0x10, Height = 0x10
            };

            // add an anchor with some data on the 2nd line
            viewPort.SelectionStart = new Point(0, 1);
            viewPort.Edit("^bob\"\" \"Hello World!\"");
            viewPort.SelectionStart = new Point(6, 1);
            viewPort.Edit(ConsoleKey.Backspace);

            Assert.Equal("\"Hello \"", PCSString.Convert(model, 0x10, PCSString.ReadString(model, 0x10, true)));
        }
Example #7
0
        private void Decapitalize(IDataModel model, ModelDelta token, int address)
        {
            if (address < 0 || address >= model.Count)
            {
                return;
            }
            var textLength = PCSString.ReadString(model, address, true);

            if (textLength < 3)
            {
                return;
            }
            var text = model.TextConverter.Convert(model, address, textLength).ToCharArray();

            for (int i = 1; i < text.Length; i++)
            {
                if (IsLetter(text[i - 1]) && IsCap(text[i]))
                {
                    text[i] += (char)('a' - 'A');
                }
            }
            token.ChangeData(model, address, model.TextConverter.Convert(new string(text), out var _));
        }
Example #8
0
        public static string ReadString(IReadOnlyList <byte> data, int start)
        {
            var length = PCSString.ReadString(data, start, true);

            return(PCSString.Convert(data, start, length));
        }
        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();
            }
        }