Esempio n. 1
0
        /// <summary>
        /// Loads title keys from a <see cref="TextReader"/> into an existing <see cref="KeySet"/>.
        /// </summary>
        /// <param name="keySet">The <see cref="KeySet"/> where the loaded keys will be placed.</param>
        /// <param name="reader">A <see cref="Stream"/> containing the keys to load.</param>
        /// <param name="logger">An optional logger that key-parsing errors will be written to.</param>
        public static void ReadTitleKeys(KeySet keySet, Stream reader, IProgressReport logger = null)
        {
            if (reader == null)
            {
                return;
            }

            using var streamReader = new StreamReader(reader);
            Span <char> buffer = stackalloc char[ReadBufferSize];
            var         ctx    = new KvPairReaderContext(streamReader, buffer);

            // Estimate the number of keys by assuming each line is about 69 bytes.
            // Subtract 2 from that so we estimate slightly high.
            keySet.ExternalKeySet.EnsureCapacity((int)reader.Length / 67);

            while (true)
            {
                ReaderStatus status = GetKeyValuePair(ref ctx);

                if (status == ReaderStatus.Error)
                {
                    logger?.LogMessage($"Invalid line in key data: \"{ctx.CurrentKey.ToString()}\"");
                    Debugger.Break();
                }
                else if (status == ReaderStatus.ReadKey)
                {
                    if (ctx.CurrentKey.Length != TitleKeySize * 2)
                    {
                        logger?.LogMessage($"Rights ID {ctx.CurrentKey.ToString()} has incorrect size {ctx.CurrentKey.Length}. (Expected {TitleKeySize * 2})");
                        continue;
                    }

                    if (ctx.CurrentValue.Length != TitleKeySize * 2)
                    {
                        logger?.LogMessage($"Title key {ctx.CurrentValue.ToString()} has incorrect size {ctx.CurrentValue.Length}. (Expected {TitleKeySize * 2})");
                        continue;
                    }

                    var rightsId = new RightsId();
                    var titleKey = new AccessKey();

                    if (!StringUtils.TryFromHexString(ctx.CurrentKey, SpanHelpers.AsByteSpan(ref rightsId)))
                    {
                        logger?.LogMessage($"Invalid rights ID \"{ctx.CurrentKey.ToString()}\" in title key file");
                        continue;
                    }

                    if (!StringUtils.TryFromHexString(ctx.CurrentValue, SpanHelpers.AsByteSpan(ref titleKey)))
                    {
                        logger?.LogMessage($"Invalid title key \"{ctx.CurrentValue.ToString()}\" in title key file");
                        continue;
                    }

                    keySet.ExternalKeySet.Add(rightsId, titleKey).ThrowIfFailure();
                }
                else if (status == ReaderStatus.Finished)
                {
                    break;
                }
            }
        }
Esempio n. 2
0
        private static ReaderStatus GetKeyValuePair(ref KvPairReaderContext reader)
        {
            Span <char> buffer = reader.Buffer;

            if (reader.BufferPos == buffer.Length && reader.HasReadEndOfFile)
            {
                // There is no more text to parse. Return that we've finished.
                return(ReaderStatus.Finished);
            }

            if (reader.NeedFillBuffer)
            {
                // Move unread text to the front of the buffer
                buffer.Slice(reader.BufferPos).CopyTo(buffer);

                int charsRead = reader.Reader.ReadBlock(buffer.Slice(buffer.Length - reader.BufferPos));

                // ReadBlock will only read less than the buffer size if there's nothing left to read
                if (charsRead != reader.BufferPos)
                {
                    buffer                  = buffer.Slice(0, buffer.Length - reader.BufferPos + charsRead);
                    reader.Buffer           = buffer;
                    reader.HasReadEndOfFile = true;
                }

                reader.NeedFillBuffer = false;
                reader.BufferPos      = 0;
            }

            if (reader.SkipNextLine)
            {
                while (reader.BufferPos < buffer.Length && !IsEndOfLine(buffer[reader.BufferPos]))
                {
                    reader.BufferPos++;
                }

                // Stop skipping once we reach a new line
                if (reader.BufferPos < buffer.Length)
                {
                    reader.SkipNextLine = false;
                }
            }

            // Skip any empty lines
            while (reader.BufferPos < buffer.Length && IsEndOfLine(buffer[reader.BufferPos]))
            {
                reader.BufferPos++;
            }

            var state       = ReaderState.Initial;
            int keyOffset   = -1;
            int keyLength   = -1;
            int valueOffset = -1;
            int valueLength = -1;
            int i;

            for (i = reader.BufferPos; i < buffer.Length; i++)
            {
                char c = buffer[i];

                switch (state)
                {
                case ReaderState.Initial when IsWhiteSpace(c):
                    continue;

                case ReaderState.Initial when IsValidNameChar(c):
                    state = ReaderState.Key;

                    keyOffset = i;

                    // Skip the next few rounds through the state machine since we know we should be
                    // encountering a string of name characters
                    do
                    {
                        ToLower(ref buffer[i]);
                        i++;
                    } while (i < buffer.Length && IsValidNameChar(buffer[i]));

                    // Decrement so we can process this character the next round through the state machine
                    i--;
                    continue;

                case ReaderState.Initial when c == '#':
                    state     = ReaderState.Comment;
                    keyOffset = i;
                    continue;

                case ReaderState.Initial when IsEndOfLine(c):
                    // The line was empty. Update the buffer position to indicate a new line
                    reader.BufferPos = i;

                    continue;

                case ReaderState.Key when IsWhiteSpace(c):
                    state = ReaderState.WhiteSpace1;

                    keyLength = i - keyOffset;
                    continue;

                case ReaderState.Key when IsDelimiter(c):
                    state = ReaderState.Delimiter;

                    keyLength = i - keyOffset;
                    continue;

                case ReaderState.WhiteSpace1 when IsWhiteSpace(c):
                    continue;

                case ReaderState.WhiteSpace1 when IsDelimiter(c):
                    state = ReaderState.Delimiter;

                    continue;

                case ReaderState.Delimiter when IsWhiteSpace(c):
                    continue;

                case ReaderState.Delimiter when StringUtils.IsHexDigit((byte)c):
                    state = ReaderState.Value;

                    valueOffset = i;

                    do
                    {
                        i++;
                    } while (i < buffer.Length && !IsEndOfLine(buffer[i]) && !IsWhiteSpace(buffer[i]));

                    i--;
                    continue;

                case ReaderState.Value when IsEndOfLine(c):
                    state = ReaderState.Success;

                    valueLength = i - valueOffset;
                    break;

                case ReaderState.Value when IsWhiteSpace(c):
                    state = ReaderState.WhiteSpace2;

                    valueLength = i - valueOffset;
                    continue;

                case ReaderState.WhiteSpace2 when IsWhiteSpace(c):
                    continue;

                case ReaderState.WhiteSpace2 when IsEndOfLine(c):
                    state = ReaderState.Success;

                    break;

                case ReaderState.Comment when IsEndOfLine(c):
                    keyLength = i - keyOffset;

                    state = ReaderState.CommentSuccess;
                    break;

                case ReaderState.Comment:
                    continue;

                // If none of the expected characters were found while in these states, the
                // line is considered invalid.
                case ReaderState.Initial:
                case ReaderState.Key:
                case ReaderState.WhiteSpace1:
                case ReaderState.Delimiter:
                    state = ReaderState.Error;
                    continue;

                case ReaderState.Error when !IsEndOfLine(c):
                    continue;
                }

                // We've exited the state machine for one reason or another
                break;
            }

            // First check if hit the end of the buffer or read the entire buffer without seeing a new line
            if (i == buffer.Length && !reader.HasReadEndOfFile)
            {
                reader.NeedFillBuffer = true;

                // If the entire buffer is part of a single long line
                if (reader.BufferPos == 0 || reader.SkipNextLine)
                {
                    reader.BufferPos = i;

                    // The line might continue past the end of the current buffer, so skip the
                    // remainder of the line after the buffer is refilled.
                    reader.SkipNextLine = true;
                    return(ReaderStatus.LineTooLong);
                }

                return(ReaderStatus.NoKeyRead);
            }

            // The only way we should exit the loop in the "Value" or "WhiteSpace2" state is if we reached
            // the end of the buffer in that state, meaning i == buffer.Length.
            // Running out of buffer when we haven't read the end of the file will have been handled by the
            // previous "if" block. If we get to this point in those states, we should be at the very end
            // of the file which will be treated as the end of a line.
            if (state == ReaderState.Value || state == ReaderState.WhiteSpace2)
            {
                Assert.SdkEqual(i, buffer.Length);
                Assert.SdkAssert(reader.HasReadEndOfFile);

                // WhiteSpace2 will have already set this value
                if (state == ReaderState.Value)
                {
                    valueLength = i - valueOffset;
                }

                state = ReaderState.Success;
            }

            // Same situation as the two above states
            if (state == ReaderState.Comment)
            {
                Assert.SdkEqual(i, buffer.Length);
                Assert.SdkAssert(reader.HasReadEndOfFile);

                keyLength = i - keyOffset;
                state     = ReaderState.CommentSuccess;
            }

            // Same as the above states except the final line was empty or whitespace.
            if (state == ReaderState.Initial)
            {
                Assert.SdkEqual(i, buffer.Length);
                Assert.SdkAssert(reader.HasReadEndOfFile);

                reader.BufferPos = i;
                return(ReaderStatus.NoKeyRead);
            }

            // If we successfully read both the key and value
            if (state == ReaderState.Success)
            {
                reader.CurrentKey   = reader.Buffer.Slice(keyOffset, keyLength);
                reader.CurrentValue = reader.Buffer.Slice(valueOffset, valueLength);
                reader.BufferPos    = i;

                return(ReaderStatus.ReadKey);
            }

            if (state == ReaderState.CommentSuccess)
            {
                reader.CurrentKey = reader.Buffer.Slice(keyOffset, keyLength);
                reader.BufferPos  = i;

                return(ReaderStatus.ReadComment);
            }

            // A bad line was encountered if we're in any of the other states
            // Return the line as "CurrentKey"
            reader.CurrentKey = reader.Buffer.Slice(reader.BufferPos, i - reader.BufferPos);
            reader.BufferPos  = i;

            return(ReaderStatus.Error);
Esempio n. 3
0
        /// <summary>
        /// Loads non-title keys from a <see cref="TextReader"/> into an existing <see cref="KeySet"/>.
        /// Missing keys will not be derived.
        /// </summary>
        /// <param name="keySet">The <see cref="KeySet"/> where the loaded keys will be placed.</param>
        /// <param name="reader">A <see cref="Stream"/> containing the keys to load.</param>
        /// <param name="keyList">A list of all the keys that will be loaded into the key set.
        /// <see cref="DefaultKeySet.CreateKeyList"/> will create a list containing all loadable keys.</param>
        /// <param name="logger">An optional logger that key-parsing errors will be written to.</param>
        public static void ReadMainKeys(KeySet keySet, Stream reader, List <KeyInfo> keyList,
                                        IProgressReport logger = null)
        {
            if (reader == null)
            {
                return;
            }

            using var streamReader = new StreamReader(reader);
            Span <char> buffer = stackalloc char[ReadBufferSize];
            var         ctx    = new KvPairReaderContext(streamReader, buffer);

            while (true)
            {
                ReaderStatus status = GetKeyValuePair(ref ctx);

                if (status == ReaderStatus.Error)
                {
                    logger?.LogMessage($"Invalid line in key data: \"{ctx.CurrentKey.ToString()}\"");
                }
                else if (status == ReaderStatus.ReadKey)
                {
                    if (!TryGetKeyInfo(out SpecificKeyInfo info, keyList, ctx.CurrentKey))
                    {
                        logger?.LogMessage($"Failed to match key {ctx.CurrentKey.ToString()}");
                        continue;
                    }

                    Span <byte> key;

                    // Get the dev key in the key set if needed.
                    if (info.IsDev && keySet.CurrentMode == KeySet.Mode.Prod)
                    {
                        keySet.SetMode(KeySet.Mode.Dev);
                        key = info.Key.Getter(keySet, info.Index);
                        keySet.SetMode(KeySet.Mode.Prod);
                    }
                    else
                    {
                        key = info.Key.Getter(keySet, info.Index);
                    }

                    if (ctx.CurrentValue.Length != key.Length * 2)
                    {
                        logger?.LogMessage($"Key {ctx.CurrentKey.ToString()} has incorrect size {ctx.CurrentValue.Length}. Must be {key.Length * 2} hex digits.");
                        continue;
                    }

                    if (!StringUtils.TryFromHexString(ctx.CurrentValue, key))
                    {
                        key.Clear();

                        logger?.LogMessage($"Key {ctx.CurrentKey.ToString()} has an invalid value. Must be {key.Length * 2} hex digits.");
                    }
                }
                else if (status == ReaderStatus.Finished)
                {
                    break;
                }
            }
        }