273 lines
6.4 KiB
C#
273 lines
6.4 KiB
C#
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
#nullable enable
|
|
|
|
namespace UnrealGameSync
|
|
{
|
|
class PrefixedTextWriter : ILogger
|
|
{
|
|
readonly string _prefix;
|
|
readonly ILogger _inner;
|
|
|
|
public PrefixedTextWriter(string inPrefix, ILogger inInner)
|
|
{
|
|
_prefix = inPrefix;
|
|
_inner = inInner;
|
|
}
|
|
|
|
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => _inner.BeginScope(state);
|
|
|
|
public bool IsEnabled(LogLevel logLevel) => _inner.IsEnabled(logLevel);
|
|
|
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
|
{
|
|
_inner.Log(logLevel, eventId, state, exception, (state, exception) => _prefix + formatter(state, exception));
|
|
}
|
|
}
|
|
|
|
public class ProgressValue
|
|
{
|
|
Tuple<string, float> _state = null!;
|
|
readonly Stack<Tuple<float, float>> _ranges = new Stack<Tuple<float, float>>();
|
|
|
|
public ProgressValue()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
_state = new Tuple<string, float>("Starting...", 0.0f);
|
|
|
|
_ranges.Clear();
|
|
_ranges.Push(new Tuple<float, float>(0.0f, 1.0f));
|
|
}
|
|
|
|
public Tuple<string, float> Current => _state;
|
|
|
|
public void Set(string message)
|
|
{
|
|
if (_ranges.Count == 1)
|
|
{
|
|
_state = new Tuple<string, float>(message, _state.Item2);
|
|
}
|
|
}
|
|
|
|
public void Set(string message, float fraction)
|
|
{
|
|
if (_ranges.Count == 1)
|
|
{
|
|
_state = new Tuple<string, float>(message, RelativeToAbsoluteFraction(fraction));
|
|
}
|
|
else
|
|
{
|
|
_state = new Tuple<string, float>(_state.Item1, RelativeToAbsoluteFraction(fraction));
|
|
}
|
|
}
|
|
|
|
public void Set(float fraction)
|
|
{
|
|
_state = new Tuple<string, float>(_state.Item1, RelativeToAbsoluteFraction(fraction));
|
|
}
|
|
|
|
public void Increment(float fraction)
|
|
{
|
|
Set(_state.Item2 + RelativeToAbsoluteFraction(fraction));
|
|
}
|
|
|
|
public void Push(float maxFraction)
|
|
{
|
|
_ranges.Push(new Tuple<float, float>(_state.Item2, RelativeToAbsoluteFraction(maxFraction)));
|
|
}
|
|
|
|
public void Pop()
|
|
{
|
|
if (_ranges.Count > 1)
|
|
{
|
|
_state = new Tuple<string, float>(_state.Item1, _ranges.Pop().Item2);
|
|
}
|
|
}
|
|
|
|
float RelativeToAbsoluteFraction(float fraction)
|
|
{
|
|
Tuple<float, float> range = _ranges.Peek();
|
|
return range.Item1 + (range.Item2 - range.Item1) * fraction;
|
|
}
|
|
}
|
|
|
|
static class ProgressTextWriter
|
|
{
|
|
const string DirectivePrefix = "@progress ";
|
|
|
|
public static string? ParseLine(string line, ProgressValue value)
|
|
{
|
|
string trimLine = line.Trim();
|
|
if (trimLine.StartsWith(DirectivePrefix, StringComparison.Ordinal))
|
|
{
|
|
// Line that just contains a progress directive
|
|
bool skipLine = false;
|
|
ProcessInternal(trimLine.Substring(DirectivePrefix.Length), ref skipLine, value);
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
bool skipLine = false;
|
|
string remainingLine = line;
|
|
|
|
// Look for a progress directive at the end of a line, in square brackets
|
|
if (trimLine.EndsWith("]", StringComparison.Ordinal))
|
|
{
|
|
for (int lastIdx = trimLine.Length - 2; lastIdx >= 0 && trimLine[lastIdx] != ']'; lastIdx--)
|
|
{
|
|
if (trimLine[lastIdx] == '[')
|
|
{
|
|
string directiveSubstring = trimLine.Substring(lastIdx + 1, trimLine.Length - lastIdx - 2);
|
|
if (directiveSubstring.StartsWith(DirectivePrefix, StringComparison.Ordinal))
|
|
{
|
|
ProcessInternal(directiveSubstring.Substring(DirectivePrefix.Length), ref skipLine, value);
|
|
remainingLine = line.Substring(0, lastIdx).TrimEnd();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (skipLine)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
return remainingLine;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ProcessInternal(string line, ref bool skipLine, ProgressValue value)
|
|
{
|
|
List<string> tokens = ParseTokens(line);
|
|
for (int tokenIdx = 0; tokenIdx < tokens.Count;)
|
|
{
|
|
float fraction;
|
|
if (ReadFraction(tokens, ref tokenIdx, out fraction))
|
|
{
|
|
value.Set(fraction);
|
|
}
|
|
else if (tokens[tokenIdx] == "push")
|
|
{
|
|
tokenIdx++;
|
|
if (ReadFraction(tokens, ref tokenIdx, out fraction))
|
|
{
|
|
value.Push(fraction);
|
|
}
|
|
}
|
|
else if (tokens[tokenIdx] == "pop")
|
|
{
|
|
tokenIdx++;
|
|
value.Pop();
|
|
}
|
|
else if (tokens[tokenIdx] == "increment")
|
|
{
|
|
tokenIdx++;
|
|
if (ReadFraction(tokens, ref tokenIdx, out fraction))
|
|
{
|
|
value.Increment(fraction);
|
|
}
|
|
}
|
|
else if (tokens[tokenIdx] == "skipline")
|
|
{
|
|
tokenIdx++;
|
|
skipLine = true;
|
|
}
|
|
else if (tokens[tokenIdx].Length >= 2 && (tokens[tokenIdx][0] == '\'' || tokens[tokenIdx][0] == '\"') && tokens[tokenIdx].Last() == tokens[tokenIdx].First())
|
|
{
|
|
string message = tokens[tokenIdx++];
|
|
value.Set(message.Substring(1, message.Length - 2));
|
|
}
|
|
else
|
|
{
|
|
tokenIdx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static List<string> ParseTokens(string line)
|
|
{
|
|
List<string> tokens = new List<string>();
|
|
for (int idx = 0; ;)
|
|
{
|
|
// Skip whitespace
|
|
while (idx < line.Length && Char.IsWhiteSpace(line[idx]))
|
|
{
|
|
idx++;
|
|
}
|
|
if (idx == line.Length)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Read the next token
|
|
if (Char.IsLetterOrDigit(line[idx]))
|
|
{
|
|
int startIdx = idx++;
|
|
while (idx < line.Length && Char.IsLetterOrDigit(line[idx]))
|
|
{
|
|
idx++;
|
|
}
|
|
tokens.Add(line.Substring(startIdx, idx - startIdx));
|
|
}
|
|
else if (line[idx] == '\'' || line[idx] == '\"')
|
|
{
|
|
int startIdx = idx++;
|
|
while (idx < line.Length && line[idx] != line[startIdx])
|
|
{
|
|
idx++;
|
|
}
|
|
tokens.Add(line.Substring(startIdx, ++idx - startIdx));
|
|
}
|
|
else
|
|
{
|
|
tokens.Add(line.Substring(idx++, 1));
|
|
}
|
|
}
|
|
return tokens;
|
|
}
|
|
|
|
static bool ReadFraction(List<string> tokens, ref int tokenIdx, out float fraction)
|
|
{
|
|
// Read a fraction in the form x%
|
|
if (tokenIdx + 2 <= tokens.Count && tokens[tokenIdx + 1] == "%")
|
|
{
|
|
int numerator;
|
|
if (Int32.TryParse(tokens[tokenIdx], out numerator))
|
|
{
|
|
fraction = (float)numerator / 100.0f;
|
|
tokenIdx += 2;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Read a fraction in the form x/y
|
|
if (tokenIdx + 3 <= tokens.Count && tokens[tokenIdx + 1] == "/")
|
|
{
|
|
int numerator, denominator;
|
|
if (Int32.TryParse(tokens[tokenIdx], out numerator) && Int32.TryParse(tokens[tokenIdx + 2], out denominator))
|
|
{
|
|
fraction = (float)numerator / (float)denominator;
|
|
tokenIdx += 3;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
fraction = 0.0f;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|