private static TokenText ParseSpeakerText(IReadOnlyList <byte> bytes, FormatVersion version, Encoding encoding)
        {
            var line = new TokenText();

            int bufferIndex = 0;

            while (bufferIndex < bytes.Count)
            {
                if (!TryParseTokens(bytes, ref bufferIndex, out var tokens, version, encoding))
                {
                    break;
                }

                line.Tokens.AddRange(tokens);
            }

            return(line);
        }
        private static void ParsePages(IDialog message, IReadOnlyList <int> lineStartAddresses, IReadOnlyList <byte> buffer, FormatVersion version, Encoding encoding)
        {
            if (lineStartAddresses.Count == 0 || buffer.Count == 0)
            {
                return;
            }

            // The addresses are not relative to the start of the buffer
            // so we rebase the addresses first
            int lineStartAddressBase = lineStartAddresses[0];

            int[] rebasedLineStartAddresses = new int[lineStartAddresses.Count];

            for (int i = 0; i < lineStartAddresses.Count; i++)
            {
                rebasedLineStartAddresses[i] = lineStartAddresses[i] - lineStartAddressBase;
            }

            for (int lineIndex = 0; lineIndex < rebasedLineStartAddresses.Length; lineIndex++)
            {
                // Initialize a new line
                var line = new TokenText();

                // Now that the line start addresses have been rebased, we can use them as indices into the buffer
                int bufferIndex = rebasedLineStartAddresses[lineIndex];

                // Calculate the line end index
                int lineEndIndex = (lineIndex + 1) != rebasedLineStartAddresses.Length ? rebasedLineStartAddresses[lineIndex + 1] : buffer.Count;

                // Loop over the buffer until we find a 0 byte or have reached the end index
                while (bufferIndex < lineEndIndex)
                {
                    if (!TryParseTokens(buffer, ref bufferIndex, out var tokens, version, encoding))
                    {
                        break;
                    }

                    line.Tokens.AddRange(tokens);
                }

                // Add line to list of lines
                message.Lines.Add(line);
            }
        }
        // TODO: maybe move the parsing functions to a seperate class
        /// <summary>
        /// Creates a <see cref="MessageScript"/> from a <see cref="MessageScriptBinary"/>.
        /// </summary>
        public static MessageScript FromBinary(MessageScriptBinary binary, FormatVersion version = FormatVersion.Detect, Encoding encoding = null)
        {
            if (binary == null)
            {
                throw new ArgumentNullException(nameof(binary));
            }

            if (binary.DialogHeaders == null)
            {
                throw new ArgumentNullException(nameof(binary));
            }

            // Create new script instance & set user id, format version
            var instance = new MessageScript
            {
                Id            = binary.Header.UserId,
                FormatVersion = version == FormatVersion.Detect ? ( FormatVersion)binary.FormatVersion : version,
                Encoding      = encoding
            };

            // Convert the binary messages to their counterpart
            var labelOccurences = new Dictionary <string, int>();

            foreach (var messageHeader in binary.DialogHeaders)
            {
                IDialog              message;
                IReadOnlyList <int>  pageStartAddresses;
                IReadOnlyList <byte> buffer;
                int pageCount;

                switch (messageHeader.Kind)
                {
                case BinaryDialogKind.Message:
                {
                    var binaryMessage = (BinaryMessageDialog)messageHeader.Data.Value;
                    pageStartAddresses = binaryMessage.PageStartAddresses;
                    buffer             = binaryMessage.TextBuffer;
                    pageCount          = binaryMessage.PageCount;

                    // check for duplicates
                    var name = ResolveName(labelOccurences, binaryMessage.Name);

                    if (binaryMessage.SpeakerId == 0xFFFF)
                    {
                        message = new MessageDialog(name);
                    }
                    else if ((binaryMessage.SpeakerId & 0x8000) == 0x8000)
                    {
                        Trace.WriteLine(binaryMessage.SpeakerId.ToString("X4"));

                        message = new MessageDialog(name, new VariableSpeaker(binaryMessage.SpeakerId & 0x0FFF));
                    }
                    else
                    {
                        if (binary.SpeakerTableHeader.SpeakerNameArray.Value == null)
                        {
                            throw new InvalidDataException("Speaker name array is null while being referenced");
                        }

                        TokenText speakerName = null;
                        if (binaryMessage.SpeakerId < binary.SpeakerTableHeader.SpeakerCount)
                        {
                            speakerName = ParseSpeakerText(binary.SpeakerTableHeader.SpeakerNameArray
                                                           .Value[binaryMessage.SpeakerId].Value, instance.FormatVersion, encoding == null ? Encoding.ASCII : encoding);
                        }

                        message = new MessageDialog(name, new NamedSpeaker(speakerName));
                    }
                }
                break;

                case BinaryDialogKind.Selection:
                {
                    var binaryMessage = ( BinarySelectionDialog )messageHeader.Data.Value;
                    pageStartAddresses = binaryMessage.OptionStartAddresses;
                    buffer             = binaryMessage.TextBuffer;
                    pageCount          = binaryMessage.OptionCount;
                    var name = ResolveName(labelOccurences, binaryMessage.Name);
                    message = new SelectionDialog(name, (SelectionDialogPattern)binaryMessage.Pattern);
                }
                break;

                default:
                    throw new InvalidDataException("Unknown message type");
                }

                if (pageCount != 0)
                {
                    // Parse the line data
                    ParsePages(message, pageStartAddresses, buffer, instance.FormatVersion, encoding == null ? Encoding.ASCII : encoding);
                }

                // Add it to the message list
                instance.Dialogs.Add(message);
            }

            return(instance);
        }
 public NamedSpeaker(string name)
 {
     Name = new TokenTextBuilder()
            .AddString(name)
            .Build();
 }
 /// <summary>
 /// Constructs a new speaker.
 /// </summary>
 /// <param name="name">The name of the speaker.</param>
 public NamedSpeaker(TokenText name)
 {
     Name = name;
 }