M Core/NosSmooth.Core/NosSmooth.Core.csproj => Core/NosSmooth.Core/NosSmooth.Core.csproj +1 -1
@@ 3,7 3,7 @@
<PropertyGroup>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
- <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
+ <TargetFrameworks>net6.0;netstandard2.1</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
M Core/NosSmooth.Language/NosSmooth.Language.csproj => Core/NosSmooth.Language/NosSmooth.Language.csproj +1 -1
@@ 4,7 4,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
- <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
+ <TargetFrameworks>net6.0;netstandard2.1</TargetFrameworks>
</PropertyGroup>
</Project>
M Core/NosSmooth.PacketSerializersGenerator/ConverterDeserializationGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/ConverterDeserializationGenerator.cs +1 -1
@@ 94,7 94,7 @@ public class ConverterDeserializationGenerator
_textWriter.WriteLine($@"while ({_stringEnumeratorVariable}.IsOnLastToken() == false)");
_textWriter.WriteLine("{");
_textWriter.Indent++;
- _textWriter.WriteLine($"{_stringEnumeratorVariable}.GetNextToken();");
+ _textWriter.WriteLine($"{_stringEnumeratorVariable}.GetNextToken(out _);");
_textWriter.Indent--;
_textWriter.WriteLine("}");
}
M Core/NosSmooth.PacketSerializersGenerator/Extensions/ParameterInfoExtensions.cs => Core/NosSmooth.PacketSerializersGenerator/Extensions/ParameterInfoExtensions.cs +10 -0
@@ 38,6 38,16 @@ public static class ParameterInfoExtensions
}
/// <summary>
+ /// Gets the name of the token variable.
+ /// </summary>
+ /// <param name="parameterInfo">The parameter.</param>
+ /// <returns>The name of the token variable.</returns>
+ public static string GetTokenVariableName(this ParameterInfo parameterInfo)
+ {
+ return $"{parameterInfo.Name}Token";
+ }
+
+ /// <summary>
/// Gets the name of the error variable.
/// </summary>
/// <param name="parameterInfo">The parameter.</param>
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BasicInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BasicInlineConverterGenerator.cs +7 -7
@@ 57,7 57,7 @@ public class BasicInlineConverterGenerator : IInlineConverterGenerator
throw new Exception("TypeSyntax or TypeSymbol has to be non null.");
}
- textWriter.WriteLine($"{Constants.HelperClass}.ParseBasic{type}(typeConverter, stringEnumerator);");
+ textWriter.WriteLine($"{Constants.HelperClass}.ParseBasic{type}(typeConverter, ref stringEnumerator);");
return null;
}
@@ 67,23 67,23 @@ public class BasicInlineConverterGenerator : IInlineConverterGenerator
foreach (var type in HandleTypes)
{
textWriter.WriteMultiline($@"
-public static Result<{type}?> ParseBasic{type}(IStringConverter typeConverter, PacketStringEnumerator stringEnumerator)
+public static Result<{type}?> ParseBasic{type}(IStringConverter typeConverter, ref PacketStringEnumerator stringEnumerator)
{{
- var tokenResult = stringEnumerator.GetNextToken();
+ var tokenResult = stringEnumerator.GetNextToken(out var packetToken);
if (!tokenResult.IsSuccess)
{{
return Result<{type}?>.FromError(tokenResult);
}}
- var token = tokenResult.Entity.Token;
- if (token == ""-"")
+ var token = packetToken.Token;
+ if (token[0] == '-' && token.Length == 1)
{{
return Result<{type}?>.FromSuccess(null);
}}
if (!{type}.TryParse(token, out var val))
{{
- return new CouldNotConvertError(typeConverter, token, ""Could not convert as {type} in inline converter"");
+ return new CouldNotConvertError(typeConverter, token.ToString(), ""Could not convert as {type} in inline converter"");
}}
return val;
@@ 91,4 91,4 @@ public static Result<{type}?> ParseBasic{type}(IStringConverter typeConverter, P
");
}
}
-}>
\ No newline at end of file
+}
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BoolInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BoolInlineConverterGenerator.cs +7 -7
@@ 44,7 44,7 @@ public class BoolInlineConverterGenerator : IInlineConverterGenerator
/// <inheritdoc />
public IError? CallDeserialize(IndentedTextWriter textWriter, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
{
- textWriter.WriteLine($"{Constants.HelperClass}.ParseBool(stringEnumerator);");
+ textWriter.WriteLine($"{Constants.HelperClass}.ParseBool(ref stringEnumerator);");
return null;
}
@@ 52,22 52,22 @@ public class BoolInlineConverterGenerator : IInlineConverterGenerator
public void GenerateHelperMethods(IndentedTextWriter textWriter)
{
textWriter.WriteLine(@"
-public static Result<bool?> ParseBool(PacketStringEnumerator stringEnumerator)
+public static Result<bool?> ParseBool(ref PacketStringEnumerator stringEnumerator)
{{
- var tokenResult = stringEnumerator.GetNextToken();
+ var tokenResult = stringEnumerator.GetNextToken(out var packetToken);
if (!tokenResult.IsSuccess)
{{
return Result<bool?>.FromError(tokenResult);
}}
- var token = tokenResult.Entity.Token;
- if (token == ""-"")
+ var token = packetToken.Token;
+ if (token[0] == '-')
{{
return Result<bool?>.FromSuccess(null);
}}
- return token == ""1"" ? true : false;
+ return token[0] == '1' ? true : false;
}}
");
}
-}>
\ No newline at end of file
+}
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/EnumInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/EnumInlineConverterGenerator.cs +7 -7
@@ 66,7 66,7 @@ public class EnumInlineConverterGenerator : IInlineConverterGenerator
textWriter.WriteLine
(
- $"{Constants.HelperClass}.ParseEnum{typeSymbol?.ToString().TrimEnd('?').Replace('.', '_')}(typeConverter, stringEnumerator);"
+ $"{Constants.HelperClass}.ParseEnum{typeSymbol?.ToString().TrimEnd('?').Replace('.', '_')}(typeConverter, ref stringEnumerator);"
);
return null;
}
@@ 80,23 80,23 @@ public class EnumInlineConverterGenerator : IInlineConverterGenerator
textWriter.WriteMultiline
(
$@"
-public static Result<{type}?> ParseEnum{type.ToString().Replace('.', '_')}(IStringConverter typeConverter, PacketStringEnumerator stringEnumerator)
+public static Result<{type}?> ParseEnum{type.ToString().Replace('.', '_')}(IStringConverter typeConverter, ref PacketStringEnumerator stringEnumerator)
{{
- var tokenResult = stringEnumerator.GetNextToken();
+ var tokenResult = stringEnumerator.GetNextToken(out var packetToken);
if (!tokenResult.IsSuccess)
{{
return Result<{type}?>.FromError(tokenResult);
}}
- var token = tokenResult.Entity.Token;
- if (token == ""-"")
+ var token = packetToken.Token;
+ if (token[0] == '-')
{{
return Result<{type}?>.FromSuccess(null);
}}
if (!{underlyingType}.TryParse(token, out var val))
{{
- return new CouldNotConvertError(typeConverter, token, ""Could not convert as {type} in inline converter"");
+ return new CouldNotConvertError(typeConverter, token.ToString(), ""Could not convert as {type} in inline converter"");
}}
return ({type}?)val;
@@ 105,4 105,4 @@ public static Result<{type}?> ParseEnum{type.ToString().Replace('.', '_')}(IStri
);
}
}
-}>
\ No newline at end of file
+}
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/FallbackInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/FallbackInlineConverterGenerator.cs +2 -2
@@ 48,7 48,7 @@ public class FallbackInlineConverterGenerator : IInlineConverterGenerator
{
textWriter.WriteLine
(
- $"_stringSerializer.Deserialize<{(typeSyntax?.ToString() ?? typeSymbol!.ToString()).TrimEnd('?')}?>(stringEnumerator);"
+ $"_stringSerializer.Deserialize<{(typeSyntax?.ToString() ?? typeSymbol!.ToString()).TrimEnd('?')}?>(ref stringEnumerator);"
);
return null;
}
@@ 58,4 58,4 @@ public class FallbackInlineConverterGenerator : IInlineConverterGenerator
{
// ignore
}
-}>
\ No newline at end of file
+}
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/ListInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/ListInlineConverterGenerator.cs +3 -3
@@ 89,7 89,7 @@ public class ListInlineConverterGenerator : IInlineConverterGenerator
}
textWriter.WriteLine
- ($"{Constants.HelperClass}.{GetMethodName(genericArgument)}(typeConverter, _stringSerializer, stringEnumerator);");
+ ($"{Constants.HelperClass}.{GetMethodName(genericArgument)}(typeConverter, _stringSerializer, ref stringEnumerator);");
return null;
}
@@ 107,7 107,7 @@ public class ListInlineConverterGenerator : IInlineConverterGenerator
textWriter.WriteLine
(
@$"
-public static Result<IReadOnlyList<{type.GetActualType()}>> {GetMethodName(type)}(IStringConverter typeConverter, IStringSerializer _stringSerializer, PacketStringEnumerator stringEnumerator)
+public static Result<IReadOnlyList<{type.GetActualType()}>> {GetMethodName(type)}(IStringConverter typeConverter, IStringSerializer _stringSerializer, ref PacketStringEnumerator stringEnumerator)
{{
var data = new List<{type.GetActualType()}>();
@@ 129,7 129,7 @@ public static Result<IReadOnlyList<{type.GetActualType()}>> {GetMethodName(type)
// or the packet has more fields.
while (stringEnumerator.IsOnLastToken() == false)
{{
- stringEnumerator.GetNextToken();
+ stringEnumerator.GetNextToken(out _);
}}
stringEnumerator.PopLevel();
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/StringInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/StringInlineConverterGenerator.cs +10 -5
@@ 30,7 30,7 @@ public class StringInlineConverterGenerator : IInlineConverterGenerator
/// <inheritdoc />
public IError? CallDeserialize(IndentedTextWriter textWriter, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
{
- textWriter.WriteLine($"{Constants.HelperClass}.ParseString(stringEnumerator);");
+ textWriter.WriteLine($"{Constants.HelperClass}.ParseString(ref stringEnumerator);");
return null;
}
@@ 40,17 40,22 @@ public class StringInlineConverterGenerator : IInlineConverterGenerator
textWriter.WriteLine
(
@"
-public static Result<string?> ParseString(PacketStringEnumerator stringEnumerator)
+public static Result<string?> ParseString(ref PacketStringEnumerator stringEnumerator)
{{
- var tokenResult = stringEnumerator.GetNextToken();
+ var tokenResult = stringEnumerator.GetNextToken(out var packetToken);
if (!tokenResult.IsSuccess)
{{
return Result<string?>.FromError(tokenResult);
}}
- return tokenResult.Entity.Token;
+ if (packetToken.Token[0] == '-')
+ {{
+ return Result<string?>.FromSuccess(null);
+ }}
+
+ return packetToken.Token.ToString();
}}
"
);
}
-}>
\ No newline at end of file
+}
M Core/NosSmooth.PacketSerializersGenerator/PacketConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/PacketConverterGenerator.cs +3 -3
@@ 95,7 95,7 @@ public override Result Serialize({_packetInfo.Name}? obj, PacketStringBuilder bu
}}
/// <inheritdoc />
-public override Result<{_packetInfo.Name}?> Deserialize(PacketStringEnumerator stringEnumerator)
+public override Result<{_packetInfo.Name}?> Deserialize(ref PacketStringEnumerator stringEnumerator)
{{
var typeConverter = this;
"
@@ 176,7 176,7 @@ public override Result<{_packetInfo.Name}?> Deserialize(PacketStringEnumerator s
textWriter.WriteLine("IResultError? skipError;");
skipped = true;
}
- textWriter.WriteLine($@"skipResult = stringEnumerator.GetNextToken();");
+ textWriter.WriteLine($@"skipResult = stringEnumerator.GetNextToken(out _);");
textWriter.WriteLine
("skipError = CheckDeserializationResult(result, \"None\", stringEnumerator, false);");
textWriter.WriteMultiline
@@ 246,4 246,4 @@ public override Result<{_packetInfo.Name}?> Deserialize(PacketStringEnumerator s
return null;
}
-}>
\ No newline at end of file
+}
M Core/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs +5 -0
@@ 130,6 130,11 @@ public class SourceGenerator : ISourceGenerator
$"{packetRecord.GetPrefix()}.{packetRecord.Identifier.NormalizeWhitespace().ToFullString()}Converter.g.cs",
stringWriter.GetStringBuilder().ToString()
);
+ File.WriteAllText
+ (
+ $"/tmp/{packetRecord.GetPrefix()}.{packetRecord.Identifier.NormalizeWhitespace().ToFullString()}Converter.g.cs",
+ stringWriter.GetStringBuilder().ToString()
+ );
}
}
M Core/NosSmooth.Packets/Converters/BaseStringConverter.cs => Core/NosSmooth.Packets/Converters/BaseStringConverter.cs +4 -4
@@ 19,12 19,12 @@ public abstract class BaseStringConverter<TParseType> : IStringConverter<TParseT
public abstract Result Serialize(TParseType? obj, PacketStringBuilder builder);
/// <inheritdoc />
- public abstract Result<TParseType?> Deserialize(PacketStringEnumerator stringEnumerator);
+ public abstract Result<TParseType?> Deserialize(ref PacketStringEnumerator stringEnumerator);
/// <inheritdoc/>
- Result<object?> IStringConverter.Deserialize(PacketStringEnumerator stringEnumerator)
+ Result<object?> IStringConverter.Deserialize(ref PacketStringEnumerator stringEnumerator)
{
- var result = Deserialize(stringEnumerator);
+ var result = Deserialize(ref stringEnumerator);
if (!result.IsSuccess)
{
return Result<object?>.FromError(result);
@@ 43,4 43,4 @@ public abstract class BaseStringConverter<TParseType> : IStringConverter<TParseT
return Serialize(parseType, builder);
}
-}>
\ No newline at end of file
+}
M Core/NosSmooth.Packets/Converters/Basic/BasicTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/BasicTypeConverter.cs +5 -5
@@ 23,20 23,20 @@ public abstract class BasicTypeConverter<TBasicType> : BaseStringConverter<TBasi
}
/// <inheritdoc />
- public override Result<TBasicType?> Deserialize(PacketStringEnumerator stringEnumerator)
+ public override Result<TBasicType?> Deserialize(ref PacketStringEnumerator stringEnumerator)
{
- var nextTokenResult = stringEnumerator.GetNextToken();
+ var nextTokenResult = stringEnumerator.GetNextToken(out var packetToken);
if (!nextTokenResult.IsSuccess)
{
return Result<TBasicType?>.FromError(nextTokenResult);
}
- if (nextTokenResult.Entity.Token == "-")
+ if (packetToken.Token[0] == '-' && packetToken.Token.Length == 1)
{
return Result<TBasicType?>.FromSuccess(default);
}
- return Deserialize(nextTokenResult.Entity.Token);
+ return Deserialize(packetToken.Token);
}
/// <summary>
@@ 44,5 44,5 @@ public abstract class BasicTypeConverter<TBasicType> : BaseStringConverter<TBasi
/// </summary>
/// <param name="value">The value to deserialize.</param>
/// <returns>The deserialized value or an error.</returns>
- protected abstract Result<TBasicType?> Deserialize(string value);
+ protected abstract Result<TBasicType?> Deserialize(ReadOnlySpan<char> value);
}=
\ No newline at end of file
M Core/NosSmooth.Packets/Converters/Basic/BoolStringConverter.cs => Core/NosSmooth.Packets/Converters/Basic/BoolStringConverter.cs +3 -8
@@ 22,19 22,14 @@ public class BoolStringConverter : BaseStringConverter<bool>
}
/// <inheritdoc />
- public override Result<bool> Deserialize(PacketStringEnumerator stringEnumerator)
+ public override Result<bool> Deserialize(ref PacketStringEnumerator stringEnumerator)
{
- var nextTokenResult = stringEnumerator.GetNextToken();
+ var nextTokenResult = stringEnumerator.GetNextToken(out var packetToken);
if (!nextTokenResult.IsSuccess)
{
return Result<bool>.FromError(nextTokenResult);
}
- if (nextTokenResult.Entity.Token == "-")
- {
- return Result<bool>.FromSuccess(default);
- }
-
- return nextTokenResult.Entity.Token == "1" ? true : false;
+ return packetToken.Token[0] == '1' ? true : false;
}
}=
\ No newline at end of file
M Core/NosSmooth.Packets/Converters/Basic/ByteStringConverter.cs => Core/NosSmooth.Packets/Converters/Basic/ByteStringConverter.cs +3 -2
@@ 4,6 4,7 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using NosSmooth.Packets.Errors;
using Remora.Results;
@@ 15,11 16,11 @@ namespace NosSmooth.Packets.Converters.Basic;
public class ByteStringConverter : BasicTypeConverter<byte>
{
/// <inheritdoc />
- protected override Result<byte> Deserialize(string value)
+ protected override Result<byte> Deserialize(ReadOnlySpan<char> value)
{
if (!byte.TryParse(value, out var parsed))
{
- return new CouldNotConvertError(this, value, "Could not parse as an byte.");
+ return new CouldNotConvertError(this, value.ToString(), "Could not parse as an byte.");
}
return parsed;
M Core/NosSmooth.Packets/Converters/Basic/CharStringConverter.cs => Core/NosSmooth.Packets/Converters/Basic/CharStringConverter.cs +3 -2
@@ 4,6 4,7 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using NosSmooth.Packets.Errors;
using Remora.Results;
@@ 15,11 16,11 @@ namespace NosSmooth.Packets.Converters.Basic;
public class CharStringConverter : BasicTypeConverter<char>
{
/// <inheritdoc />
- protected override Result<char> Deserialize(string value)
+ protected override Result<char> Deserialize(ReadOnlySpan<char> value)
{
if (value.Length != 1)
{
- return new CouldNotConvertError(this, value, "The token is not one character long.");
+ return new CouldNotConvertError(this, value.ToString(), "The token is not one character long.");
}
return value[0];
M Core/NosSmooth.Packets/Converters/Basic/IntStringConverter.cs => Core/NosSmooth.Packets/Converters/Basic/IntStringConverter.cs +3 -2
@@ 4,6 4,7 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using NosSmooth.Packets.Errors;
using Remora.Results;
@@ 15,11 16,11 @@ namespace NosSmooth.Packets.Converters.Basic;
public class IntStringConverter : BasicTypeConverter<int>
{
/// <inheritdoc />
- protected override Result<int> Deserialize(string value)
+ protected override Result<int> Deserialize(ReadOnlySpan<char> value)
{
if (!int.TryParse(value, out var parsed))
{
- return new CouldNotConvertError(this, value, "Could not parse as int.");
+ return new CouldNotConvertError(this, value.ToString(), "Could not parse as int.");
}
return parsed;
M Core/NosSmooth.Packets/Converters/Basic/LongStringConverter.cs => Core/NosSmooth.Packets/Converters/Basic/LongStringConverter.cs +3 -2
@@ 4,6 4,7 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using NosSmooth.Packets.Errors;
using Remora.Results;
@@ 15,11 16,11 @@ namespace NosSmooth.Packets.Converters.Basic;
public class LongStringConverter : BasicTypeConverter<long>
{
/// <inheritdoc />
- protected override Result<long> Deserialize(string value)
+ protected override Result<long> Deserialize(ReadOnlySpan<char> value)
{
if (!long.TryParse(value, out var parsed))
{
- return new CouldNotConvertError(this, value, "Could not parse as a long.");
+ return new CouldNotConvertError(this, value.ToString(), "Could not parse as a long.");
}
return parsed;
M Core/NosSmooth.Packets/Converters/Basic/ShortStringConverter.cs => Core/NosSmooth.Packets/Converters/Basic/ShortStringConverter.cs +3 -2
@@ 4,6 4,7 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using NosSmooth.Packets.Errors;
using Remora.Results;
@@ 15,11 16,11 @@ namespace NosSmooth.Packets.Converters.Basic;
public class ShortStringConverter : BasicTypeConverter<short>
{
/// <inheritdoc />
- protected override Result<short> Deserialize(string value)
+ protected override Result<short> Deserialize(ReadOnlySpan<char> value)
{
if (!short.TryParse(value, out var parsed))
{
- return new CouldNotConvertError(this, value, "Could not parse as short.");
+ return new CouldNotConvertError(this, value.ToString(), "Could not parse as short.");
}
return parsed;
M Core/NosSmooth.Packets/Converters/Basic/StringTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/StringTypeConverter.cs +3 -2
@@ 4,6 4,7 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using Remora.Results;
namespace NosSmooth.Packets.Converters.Basic;
@@ 14,8 15,8 @@ namespace NosSmooth.Packets.Converters.Basic;
public class StringTypeConverter : BasicTypeConverter<string>
{
/// <inheritdoc />
- protected override Result<string?> Deserialize(string value)
+ protected override Result<string?> Deserialize(ReadOnlySpan<char> value)
{
- return value;
+ return value.ToString();
}
}=
\ No newline at end of file
M Core/NosSmooth.Packets/Converters/Basic/UIntStringConverter.cs => Core/NosSmooth.Packets/Converters/Basic/UIntStringConverter.cs +3 -2
@@ 4,6 4,7 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using NosSmooth.Packets.Errors;
using Remora.Results;
@@ 15,11 16,11 @@ namespace NosSmooth.Packets.Converters.Basic;
public class UIntStringConverter : BasicTypeConverter<uint>
{
/// <inheritdoc />
- protected override Result<uint> Deserialize(string value)
+ protected override Result<uint> Deserialize(ReadOnlySpan<char> value)
{
if (!uint.TryParse(value, out var parsed))
{
- return new CouldNotConvertError(this, value, "Could not parse as uint");
+ return new CouldNotConvertError(this, value.ToString(), "Could not parse as uint");
}
return parsed;
M Core/NosSmooth.Packets/Converters/Basic/ULongStringConverter.cs => Core/NosSmooth.Packets/Converters/Basic/ULongStringConverter.cs +3 -2
@@ 4,6 4,7 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using NosSmooth.Packets.Errors;
using Remora.Results;
@@ 15,11 16,11 @@ namespace NosSmooth.Packets.Converters.Basic;
public class ULongStringConverter : BasicTypeConverter<ulong>
{
/// <inheritdoc />
- protected override Result<ulong> Deserialize(string value)
+ protected override Result<ulong> Deserialize(ReadOnlySpan<char> value)
{
if (!ulong.TryParse(value, out var parsed))
{
- return new CouldNotConvertError(this, value, "Could not parse as an ulong.");
+ return new CouldNotConvertError(this, value.ToString(), "Could not parse as an ulong.");
}
return parsed;
M Core/NosSmooth.Packets/Converters/Basic/UShortStringConverter.cs => Core/NosSmooth.Packets/Converters/Basic/UShortStringConverter.cs +3 -2
@@ 4,6 4,7 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using NosSmooth.Packets.Errors;
using Remora.Results;
@@ 15,11 16,11 @@ namespace NosSmooth.Packets.Converters.Basic;
public class UShortStringConverter : BasicTypeConverter<ushort>
{
/// <inheritdoc />
- protected override Result<ushort> Deserialize(string value)
+ protected override Result<ushort> Deserialize(ReadOnlySpan<char> value)
{
if (!ushort.TryParse(value, out var parsed))
{
- return new CouldNotConvertError(this, value, "Could not parse as an ushort.");
+ return new CouldNotConvertError(this, value.ToString(), "Could not parse as an ushort.");
}
return parsed;
M Core/NosSmooth.Packets/Converters/Common/NameStringConverter.cs => Core/NosSmooth.Packets/Converters/Common/NameStringConverter.cs +4 -4
@@ 29,19 29,19 @@ public class NameStringConverter : BaseStringConverter<NameString>
}
/// <inheritdoc />
- public override Result<NameString?> Deserialize(PacketStringEnumerator stringEnumerator)
+ public override Result<NameString?> Deserialize(ref PacketStringEnumerator stringEnumerator)
{
- var tokenResult = stringEnumerator.GetNextToken();
+ var tokenResult = stringEnumerator.GetNextToken(out var packetToken);
if (!tokenResult.IsSuccess)
{
return Result<NameString?>.FromError(tokenResult);
}
- if (tokenResult.Entity.Token == "-")
+ if (packetToken.Token[0] == '-' && packetToken.Token.Length == 1)
{
return Result<NameString?>.FromSuccess(null);
}
- return NameString.FromPacket(tokenResult.Entity.Token);
+ return NameString.FromPacket(packetToken.Token.ToString());
}
}=
\ No newline at end of file
M Core/NosSmooth.Packets/Converters/IStringConverter.cs => Core/NosSmooth.Packets/Converters/IStringConverter.cs +2 -2
@@ 18,7 18,7 @@ public interface IStringConverter
/// </summary>
/// <param name="stringEnumerator">The packet string enumerator with the current position.</param>
/// <returns>The parsed object or an error.</returns>
- public Result<object?> Deserialize(PacketStringEnumerator stringEnumerator);
+ public Result<object?> Deserialize(ref PacketStringEnumerator stringEnumerator);
/// <summary>
/// Serializes the given object to string by appending to the packet string builder.
@@ 43,7 43,7 @@ public interface IStringConverter<TParseType> : IStringConverter
/// </summary>
/// <param name="stringEnumerator">The packet string enumerator with the current position.</param>
/// <returns>The parsed object or an error.</returns>
- public new Result<TParseType?> Deserialize(PacketStringEnumerator stringEnumerator);
+ public new Result<TParseType?> Deserialize(ref PacketStringEnumerator stringEnumerator);
/// <summary>
/// Serializes the given object to string by appending to the packet string builder.
M Core/NosSmooth.Packets/Converters/IStringSerializer.cs => Core/NosSmooth.Packets/Converters/IStringSerializer.cs +2 -2
@@ 20,7 20,7 @@ public interface IStringSerializer
/// <param name="parseType">The type of the object to serialize.</param>
/// <param name="stringEnumerator">The packet string enumerator with the current position.</param>
/// <returns>The parsed object or an error.</returns>
- public Result<object?> Deserialize(Type parseType, PacketStringEnumerator stringEnumerator);
+ public Result<object?> Deserialize(Type parseType, ref PacketStringEnumerator stringEnumerator);
/// <summary>
/// Serializes the given object to string by appending to the packet string builder.
@@ 37,7 37,7 @@ public interface IStringSerializer
/// <param name="stringEnumerator">The packet string enumerator with the current position.</param>
/// <typeparam name="TParseType">The type of the object to serialize.</typeparam>
/// <returns>The parsed object or an error.</returns>
- public Result<TParseType?> Deserialize<TParseType>(PacketStringEnumerator stringEnumerator);
+ public Result<TParseType?> Deserialize<TParseType>(ref PacketStringEnumerator stringEnumerator);
/// <summary>
/// Serializes the given object to string by appending to the packet string builder.
M Core/NosSmooth.Packets/Converters/Packets/UpgradeRareSubPacketConverter.cs => Core/NosSmooth.Packets/Converters/Packets/UpgradeRareSubPacketConverter.cs +8 -8
@@ 28,31 28,31 @@ public class UpgradeRareSubPacketConverter : BaseStringConverter<UpgradeRareSubP
}
/// <inheritdoc />
- public override Result<UpgradeRareSubPacket?> Deserialize(PacketStringEnumerator stringEnumerator)
+ public override Result<UpgradeRareSubPacket?> Deserialize(ref PacketStringEnumerator stringEnumerator)
{
- var tokenResult = stringEnumerator.GetNextToken();
+ var tokenResult = stringEnumerator.GetNextToken(out var packetToken);
if (!tokenResult.IsSuccess)
{
return Result<UpgradeRareSubPacket?>.FromError(tokenResult);
}
+ var token = packetToken.Token;
- var token = tokenResult.Entity.Token;
if (token.Length > 3)
{
- return new CouldNotConvertError(this, token, "The string is not two/three characters long.");
+ return new CouldNotConvertError(this, token.ToString(), "The string is not two/three characters long.");
}
- var upgradeString = token.Substring(0, token.Length - 1);
- var rareString = token[token.Length - 1].ToString();
+ var upgradeString = token.Slice(0, token.Length - 1);
+ var rareString = token.Slice(token.Length - 1);
if (!byte.TryParse(upgradeString, out var upgrade))
{
- return new CouldNotConvertError(this, upgradeString, "Could not parse as byte");
+ return new CouldNotConvertError(this, upgradeString.ToString(), "Could not parse as byte");
}
if (!sbyte.TryParse(rareString, out var rare))
{
- return new CouldNotConvertError(this, rareString, "Could not parse as byte");
+ return new CouldNotConvertError(this, rareString.ToString(), "Could not parse as byte");
}
return new UpgradeRareSubPacket(upgrade, rare);
M Core/NosSmooth.Packets/Converters/Special/Converters/EnumStringConverter.cs => Core/NosSmooth.Packets/Converters/Special/Converters/EnumStringConverter.cs +2 -2
@@ 35,9 35,9 @@ public class EnumStringConverter<TEnum, TUnderlyingType> : BaseStringConverter<T
}
/// <inheritdoc />
- public override Result<TEnum?> Deserialize(PacketStringEnumerator stringEnumerator)
+ public override Result<TEnum?> Deserialize(ref PacketStringEnumerator stringEnumerator)
{
- var result = _serializer.Deserialize<TUnderlyingType>(stringEnumerator);
+ var result = _serializer.Deserialize<TUnderlyingType>(ref stringEnumerator);
if (!result.IsSuccess)
{
return Result<TEnum?>.FromError(result);
M Core/NosSmooth.Packets/Converters/Special/Converters/ListStringConverter.cs => Core/NosSmooth.Packets/Converters/Special/Converters/ListStringConverter.cs +3 -3
@@ 58,7 58,7 @@ public class ListStringConverter<TGeneric> : BaseStringConverter<IReadOnlyList<T
}
/// <inheritdoc />
- public override Result<IReadOnlyList<TGeneric>?> Deserialize(PacketStringEnumerator stringEnumerator)
+ public override Result<IReadOnlyList<TGeneric>?> Deserialize(ref PacketStringEnumerator stringEnumerator)
{
var list = new List<TGeneric>();
@@ 69,14 69,14 @@ public class ListStringConverter<TGeneric> : BaseStringConverter<IReadOnlyList<T
return new ArgumentInvalidError(nameof(stringEnumerator), "The string enumerator has to have a prepared level for all lists.");
}
- var result = _serializer.Deserialize<TGeneric>(stringEnumerator);
+ var result = _serializer.Deserialize<TGeneric>(ref stringEnumerator);
// If we know that we are not on the last token in the item level, just skip to the end of the item.
// Note that if this is the case, then that means the converter is either corrupted
// or the packet has more fields.
while (stringEnumerator.IsOnLastToken() == false)
{
- stringEnumerator.GetNextToken();
+ stringEnumerator.GetNextToken(out _);
}
stringEnumerator.PopLevel();
M Core/NosSmooth.Packets/Converters/Special/Converters/NullableStringConverter.cs => Core/NosSmooth.Packets/Converters/Special/Converters/NullableStringConverter.cs +2 -2
@@ 42,9 42,9 @@ public class NullableStringConverter<T> : BaseStringConverter<Nullable<T>>
}
/// <inheritdoc />
- public override Result<T?> Deserialize(PacketStringEnumerator stringEnumerator)
+ public override Result<T?> Deserialize(ref PacketStringEnumerator stringEnumerator)
{
- var result = _stringSerializer.Deserialize<T>(stringEnumerator);
+ var result = _stringSerializer.Deserialize<T>(ref stringEnumerator);
if (!result.IsSuccess)
{
return Result<T?>.FromError(result);
M Core/NosSmooth.Packets/Converters/Special/StringSerializer.cs => Core/NosSmooth.Packets/Converters/Special/StringSerializer.cs +4 -4
@@ 25,7 25,7 @@ public class StringSerializer : IStringSerializer
}
/// <inheritdoc />
- public Result<object?> Deserialize(Type parseType, PacketStringEnumerator stringEnumerator)
+ public Result<object?> Deserialize(Type parseType, ref PacketStringEnumerator stringEnumerator)
{
var converterResult = _converterRepository.GetTypeConverter(parseType);
if (!converterResult.IsSuccess)
@@ 33,7 33,7 @@ public class StringSerializer : IStringSerializer
return Result<object?>.FromError(converterResult);
}
- var deserializedResult = converterResult.Entity.Deserialize(stringEnumerator);
+ var deserializedResult = converterResult.Entity.Deserialize(ref stringEnumerator);
if (!deserializedResult.IsSuccess)
{
return Result<object?>.FromError(deserializedResult);
@@ 55,7 55,7 @@ public class StringSerializer : IStringSerializer
}
/// <inheritdoc />
- public Result<TParseType?> Deserialize<TParseType>(PacketStringEnumerator stringEnumerator)
+ public Result<TParseType?> Deserialize<TParseType>(ref PacketStringEnumerator stringEnumerator)
{
var converterResult = _converterRepository.GetTypeConverter<TParseType>();
if (!converterResult.IsSuccess)
@@ 63,7 63,7 @@ public class StringSerializer : IStringSerializer
return Result<TParseType?>.FromError(converterResult);
}
- return converterResult.Entity.Deserialize(stringEnumerator);
+ return converterResult.Entity.Deserialize(ref stringEnumerator);
}
/// <inheritdoc />
A Core/NosSmooth.Packets/Converters/TypeConverterRepository.cs => Core/NosSmooth.Packets/Converters/TypeConverterRepository.cs +0 -0
M Core/NosSmooth.Packets/Errors/CouldNotConvertError.cs => Core/NosSmooth.Packets/Errors/CouldNotConvertError.cs +1 -0
@@ 5,6 5,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
+using System.Buffers;
using NosSmooth.Packets.Converters;
using Remora.Results;
M Core/NosSmooth.Packets/NosSmooth.Packets.csproj => Core/NosSmooth.Packets/NosSmooth.Packets.csproj +1 -1
@@ 2,9 2,9 @@
<PropertyGroup>
<LangVersion>10</LangVersion>
- <TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<AssemblyName>NosSmooth.Packets</AssemblyName>
<RootNamespace>NosSmooth.Packets</RootNamespace>
+ <TargetFrameworks>net6.0;netstandard2.1</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
M Core/NosSmooth.Packets/PacketSerializer.cs => Core/NosSmooth.Packets/PacketSerializer.cs +3 -3
@@ 59,20 59,20 @@ public class PacketSerializer : IPacketSerializer
public Result<IPacket> Deserialize(string packetString, PacketSource preferredSource)
{
var packetStringEnumerator = new PacketStringEnumerator(packetString);
- var headerTokenResult = packetStringEnumerator.GetNextToken();
+ var headerTokenResult = packetStringEnumerator.GetNextToken(out var packetToken);
if (!headerTokenResult.IsSuccess)
{
return Result<IPacket>.FromError(headerTokenResult);
}
- var packetInfoResult = _packetTypesRepository.FindPacketInfo(headerTokenResult.Entity.Token, preferredSource);
+ var packetInfoResult = _packetTypesRepository.FindPacketInfo(packetToken.Token.ToString(), preferredSource);
if (!packetInfoResult.IsSuccess)
{
return Result<IPacket>.FromError(packetInfoResult);
}
var packetInfo = packetInfoResult.Entity;
- var deserializedResult = packetInfo.PacketConverter.Deserialize(packetStringEnumerator);
+ var deserializedResult = packetInfo.PacketConverter.Deserialize(ref packetStringEnumerator);
if (!deserializedResult.IsSuccess)
{
return Result<IPacket>.FromError(deserializedResult);
M Core/NosSmooth.Packets/PacketStringEnumerator.cs => Core/NosSmooth.Packets/PacketStringEnumerator.cs +47 -91
@@ 4,6 4,7 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using System.Collections.Generic;
using System.Text;
using NosSmooth.Packets.Errors;
@@ 14,15 15,18 @@ namespace NosSmooth.Packets;
/// <summary>
/// Enumerator for packet strings.
/// </summary>
-public struct PacketStringEnumerator
+public ref struct PacketStringEnumerator
{
- private readonly EnumeratorData _data;
+ private readonly ReadOnlySpan<char> _data;
private readonly Dictionary<char, ushort> _numberOfSeparators;
private EnumeratorLevel _currentLevel;
private (char Separator, uint? MaxTokens)? _preparedLevel;
- private PacketToken? _currentToken;
+ private bool _currentTokenRead;
+ private PacketToken _currentToken;
private bool _readToLast;
+ private int _cursor;
+
/// <summary>
/// Initializes a new instance of the <see cref="PacketStringEnumerator"/> struct.
/// </summary>
@@ 31,30 35,14 @@ public struct PacketStringEnumerator
public PacketStringEnumerator(string data, char separator = ' ')
{
_currentLevel = new EnumeratorLevel(null, separator);
- _data = new EnumeratorData(data);
+ _data = new ReadOnlySpan<char>(data.ToCharArray());
+ _cursor = 0;
_numberOfSeparators = new Dictionary<char, ushort>();
_numberOfSeparators.Add(separator, 1);
- _currentToken = null;
- _preparedLevel = null;
- _readToLast = false;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="PacketStringEnumerator"/> struct.
- /// </summary>
- /// <param name="data">The data of the enumerator.</param>
- /// <param name="level">The current enumerator level.</param>
- /// <param name="numberOfSeparators">The number of separators.</param>
- private PacketStringEnumerator(EnumeratorData data, EnumeratorLevel level, Dictionary<char, ushort> numberOfSeparators)
- {
- _currentLevel = level;
- _data = data;
-
- // TODO: use something less heavy than copying everything from the dictionary.
- _numberOfSeparators = new Dictionary<char, ushort>(numberOfSeparators);
- _currentToken = null;
+ _currentToken = new PacketToken(default, default, default, default);
_preparedLevel = null;
_readToLast = false;
+ _currentTokenRead = false;
}
/// <summary>
@@ 66,7 54,7 @@ public struct PacketStringEnumerator
/// <param name="separator">The separator to look for.</param>
public void SetAfterSeparatorOnce(char separator)
{
- _currentToken = null;
+ _currentTokenRead = false;
_currentLevel.SeparatorOnce = separator;
}
@@ 97,19 85,6 @@ public struct PacketStringEnumerator
}
/// <summary>
- /// Create next level with the separator given in the prepared level.
- /// </summary>
- /// <remarks>
- /// Level of the current enumerator will stay the same.
- /// Will return null, if there is not a level prepared.
- /// </remarks>
- /// <returns>An enumerator with the new level pushed.</returns>
- public PacketStringEnumerator? CreatePreparedLevel()
- {
- return _preparedLevel is not null ? CreateLevel(_preparedLevel.Value.Separator, _preparedLevel.Value.MaxTokens) : null;
- }
-
- /// <summary>
/// Push next level with the separator given in the prepared level.
/// </summary>
/// <returns>Whether there is a prepared level present.</returns>
@@ 120,7 95,7 @@ public struct PacketStringEnumerator
return false;
}
- _currentToken = null;
+ _currentTokenRead = false;
_currentLevel = new EnumeratorLevel(_currentLevel, _preparedLevel.Value.Separator, _preparedLevel.Value.MaxTokens)
{
ReachedEnd = _currentLevel.ReachedEnd
@@ 136,25 111,6 @@ public struct PacketStringEnumerator
}
/// <summary>
- /// Create next level with the given separator and maximum number of tokens.
- /// </summary>
- /// <remarks>
- /// Level of the current enumerator will stay the same.
- /// The maximum number of tokens indicates how many tokens can be read ie. in lists,
- /// the enumerator won't allow reading more than that many tokens, error will be thrown if the user tries to read more.
- /// </remarks>
- /// <param name="separator">The separator of the new level.</param>
- /// <param name="maxTokens">The maximum number of tokens to read.</param>
- /// <returns>An enumerator with the new level pushed.</returns>
- public PacketStringEnumerator CreateLevel(char separator, uint? maxTokens = default)
- {
- _currentToken = null;
- var stringEnumerator = new PacketStringEnumerator(_data, _currentLevel, _numberOfSeparators);
- stringEnumerator.PushLevel(separator, maxTokens);
- return stringEnumerator;
- }
-
- /// <summary>
/// Push new separator level to the stack.
/// </summary>
/// <remarks>
@@ 166,7 122,7 @@ public struct PacketStringEnumerator
public void PushLevel(char separator, uint? maxTokens = default)
{
_preparedLevel = null;
- _currentToken = null;
+ _currentTokenRead = false;
_currentLevel = new EnumeratorLevel(_currentLevel, separator, maxTokens)
{
ReachedEnd = _currentLevel.ReachedEnd
@@ 197,36 153,50 @@ public struct PacketStringEnumerator
}
/// <summary>
+ /// Skip the given amount of characters.
+ /// </summary>
+ /// <param name="count">The count of characters to skip.</param>
+ public void Skip(int count)
+ {
+ _cursor += count;
+ }
+
+ /// <summary>
/// Get the next token.
/// </summary>
+ /// <param name="packetToken">The resulting token.</param>
/// <param name="seek">Whether to seek the cursor to the end of the token.</param>
/// <returns>The found token.</returns>
- public Result<PacketToken> GetNextToken(bool seek = true)
+ public Result GetNextToken(out PacketToken packetToken, bool seek = true)
{
// The token is cached if seek was false to speed things up.
- if (_currentToken != null)
+ if (_currentTokenRead)
{
- var cachedToken = _currentToken.Value;
+ var cachedToken = _currentToken;
if (seek)
{
UpdateCurrentAndParentLevels(cachedToken);
_currentLevel.TokensRead++;
- _currentToken = null;
- _data.Cursor += cachedToken.Token.Length + 1;
+ _currentTokenRead = false;
+ _cursor += cachedToken.Token.Length + 1;
_currentLevel.SeparatorOnce = null;
}
- return cachedToken;
+ packetToken = new PacketToken(default, default, default, default);
+ packetToken = cachedToken;
+ return Result.FromSuccess();
}
- if (_data.ReachedEnd || (_currentLevel.ReachedEnd ?? false))
+ if ((_cursor >= _data.Length) || (_currentLevel.ReachedEnd ?? false))
{
- return new PacketEndReachedError(_data.Data, _currentLevel.ReachedEnd ?? false);
+ packetToken = new PacketToken(default, default, default, default);
+ return new PacketEndReachedError(_data.ToString(), _currentLevel.ReachedEnd ?? false);
}
- var currentIndex = _data.Cursor;
- char currentCharacter = _data.Data[currentIndex];
- StringBuilder tokenString = new StringBuilder();
+ var currentIndex = _cursor;
+ var length = 0;
+ var startIndex = currentIndex;
+ char currentCharacter = _data[currentIndex];
bool? isLast, encounteredUpperLevel;
@@ 234,27 204,27 @@ public struct PacketStringEnumerator
// If should read to last, then read until isLast is null or true.
while (!IsSeparator(currentCharacter, out isLast, out encounteredUpperLevel) || (_readToLast && !(isLast ?? true)))
{
- tokenString.Append(currentCharacter);
+ length++;
currentIndex++;
- if (currentIndex == _data.Data.Length)
+ if (currentIndex >= _data.Length)
{
isLast = true;
encounteredUpperLevel = true;
break;
}
- currentCharacter = _data.Data[currentIndex];
+ currentCharacter = _data[currentIndex];
}
_readToLast = false;
currentIndex++;
- var token = new PacketToken(tokenString.ToString(), isLast, encounteredUpperLevel, _data.ReachedEnd);
+ var token = new PacketToken(_data.Slice(startIndex, length), isLast, encounteredUpperLevel, _cursor >= _data.Length);
if (seek)
{
UpdateCurrentAndParentLevels(token);
- _data.Cursor = currentIndex;
+ _cursor = currentIndex;
_currentLevel.TokensRead++;
}
else
@@ 262,7 232,8 @@ public struct PacketStringEnumerator
_currentToken = token;
}
- return token;
+ packetToken = token;
+ return Result.FromSuccess();
}
/// <summary>
@@ 312,7 283,7 @@ public struct PacketStringEnumerator
/// <returns>Whether the last token was read. Null if cannot determine (ie. there are multiple levels with the same separator.)</returns>
public bool? IsOnLastToken()
{
- if (_data.ReachedEnd)
+ if (_cursor >= _data.Length)
{
return true;
}
@@ 376,21 347,6 @@ public struct PacketStringEnumerator
return true;
}
- private class EnumeratorData
- {
- public EnumeratorData(string data)
- {
- Data = data;
- Cursor = 0;
- }
-
- public string Data { get; }
-
- public int Cursor { get; set; }
-
- public bool ReachedEnd => Cursor >= Data.Length;
- }
-
private class EnumeratorLevel
{
public EnumeratorLevel(EnumeratorLevel? parent, char separator, uint? maxTokens = default)
M Core/NosSmooth.Packets/PacketToken.cs => Core/NosSmooth.Packets/PacketToken.cs +50 -6
@@ 4,6 4,7 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System;
using System.Diagnostics.CodeAnalysis;
namespace NosSmooth.Packets;
@@ 11,9 12,52 @@ namespace NosSmooth.Packets;
/// <summary>
/// The single token from a packet.
/// </summary>
-/// <param name="Token">The token.</param>
-/// <param name="IsLast">Whether the token is last in the current level. Null if it cannot be determined.</param>
-/// <param name="EncounteredUpperLevel">Whether the current separator was from an upper stack level than the parent. That could mean some kind of an error if not etc. at the end of parsing a last entry of a list and last entry of a subpacket.</param>
-/// <param name="PacketEndReached">Whether the packet's end was reached.</param>
-[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "Record struct creates the underlying properties.")]
-public readonly record struct PacketToken(string Token, bool? IsLast, bool? EncounteredUpperLevel, bool PacketEndReached);>
\ No newline at end of file
+[SuppressMessage
+(
+ "StyleCop.CSharp.NamingRules",
+ "SA1313:Parameter names should begin with lower-case letter",
+ Justification = "Record struct creates the underlying properties."
+)]
+public readonly ref struct PacketToken
+{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PacketToken"/> struct.
+ /// </summary>
+ /// <param name="token">The token.</param>
+ /// <param name="isLast">Whether this is the last token in the current level.</param>
+ /// <param name="encounteredUpperLevel">Whether upper level separator was encountered.</param>
+ /// <param name="packetEndReached">Whether the packet end was reached.</param>
+ public PacketToken
+ (
+ ReadOnlySpan<char> token,
+ bool? isLast,
+ bool? encounteredUpperLevel,
+ bool packetEndReached
+ )
+ {
+ Token = token;
+ IsLast = isLast;
+ EncounteredUpperLevel = encounteredUpperLevel;
+ PacketEndReached = packetEndReached;
+ }
+
+ /// <summary>
+ /// The token.
+ /// </summary>
+ public ReadOnlySpan<char> Token { get; }
+
+ /// <summary>
+ /// Whether the token is last in the current level. Null if it cannot be determined.
+ /// </summary>
+ public bool? IsLast { get; }
+
+ /// <summary>
+ /// Whether the current separator was from an upper stack level than the parent. That could mean some kind of an error if not etc. at the end of parsing a last entry of a list and last entry of a subpacket.
+ /// </summary>
+ public bool? EncounteredUpperLevel { get; }
+
+ /// <summary>
+ /// Whether the packet's end was reached.
+ /// </summary>
+ public bool PacketEndReached { get; }
+}<
\ No newline at end of file
A Local/NosSmooth.Inject/NosSmooth.Inject.vcxproj => Local/NosSmooth.Inject/NosSmooth.Inject.vcxproj +174 -0
@@ 0,0 1,174 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>16.0</VCProjectVersion>
+ <Keyword>Win32Proj</Keyword>
+ <ProjectGuid>{ca2873d8-bd0b-4583-818d-b94a3c2abba3}</ProjectGuid>
+ <RootNamespace>NosSmoothInject</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;NOSSMOOTHINJECT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableUAC>false</EnableUAC>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>nethost.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;NDEBUG;NOSSMOOTHINJECT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableUAC>false</EnableUAC>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>nethost.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_DEBUG;NOSSMOOTHINJECT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableUAC>false</EnableUAC>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>nethost.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>NDEBUG;NOSSMOOTHINJECT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableUAC>false</EnableUAC>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>nethost.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp" />
+ <ClCompile Include="nossmooth.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="coreclr_delegates.h" />
+ <ClInclude Include="nethost.h" />
+ <ClInclude Include="hostfxr.h" />
+ <ClInclude Include="nossmooth.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project><
\ No newline at end of file
A Local/NosSmooth.Inject/NosSmooth.Inject.vcxproj.filters => Local/NosSmooth.Inject/NosSmooth.Inject.vcxproj.filters +39 -0
@@ 0,0 1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="nossmooth.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="nossmooth.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="hostfxr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="nethost.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="coreclr_delegates.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project><
\ No newline at end of file
A Local/NosSmooth.Inject/coreclr_delegates.h => Local/NosSmooth.Inject/coreclr_delegates.h +47 -0
@@ 0,0 1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef __CORECLR_DELEGATES_H__
+#define __CORECLR_DELEGATES_H__
+
+#include <stdint.h>
+
+#if defined(_WIN32)
+#define CORECLR_DELEGATE_CALLTYPE __stdcall
+#ifdef _WCHAR_T_DEFINED
+typedef wchar_t char_t;
+#else
+typedef unsigned short char_t;
+#endif
+#else
+#define CORECLR_DELEGATE_CALLTYPE
+typedef char char_t;
+#endif
+
+#define UNMANAGEDCALLERSONLY_METHOD ((const char_t*)-1)
+
+// Signature of delegate returned by coreclr_delegate_type::load_assembly_and_get_function_pointer
+typedef int (CORECLR_DELEGATE_CALLTYPE* load_assembly_and_get_function_pointer_fn)(
+ const char_t* assembly_path /* Fully qualified path to assembly */,
+ const char_t* type_name /* Assembly qualified type name */,
+ const char_t* method_name /* Public static method name compatible with delegateType */,
+ const char_t* delegate_type_name /* Assembly qualified delegate type name or null
+ or UNMANAGEDCALLERSONLY_METHOD if the method is marked with
+ the UnmanagedCallersOnlyAttribute. */,
+ void* reserved /* Extensibility parameter (currently unused and must be 0) */,
+ /*out*/ void** delegate /* Pointer where to store the function pointer result */);
+
+// Signature of delegate returned by load_assembly_and_get_function_pointer_fn when delegate_type_name == null (default)
+typedef int (CORECLR_DELEGATE_CALLTYPE* component_entry_point_fn)(void* arg, int32_t arg_size_in_bytes);
+
+typedef int (CORECLR_DELEGATE_CALLTYPE* get_function_pointer_fn)(
+ const char_t* type_name /* Assembly qualified type name */,
+ const char_t* method_name /* Public static method name compatible with delegateType */,
+ const char_t* delegate_type_name /* Assembly qualified delegate type name or null,
+ or UNMANAGEDCALLERSONLY_METHOD if the method is marked with
+ the UnmanagedCallersOnlyAttribute. */,
+ void* load_context /* Extensibility parameter (currently unused and must be 0) */,
+ void* reserved /* Extensibility parameter (currently unused and must be 0) */,
+ /*out*/ void** delegate /* Pointer where to store the function pointer result */);
+
+#endif // __CORECLR_DELEGATES_H__<
\ No newline at end of file
A Local/NosSmooth.Inject/dllmain.cpp => Local/NosSmooth.Inject/dllmain.cpp +19 -0
@@ 0,0 1,19 @@
+// dllmain.cpp : Defines the entry point for the DLL application.
+#include <windows.h>
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
A Local/NosSmooth.Inject/framework.h => Local/NosSmooth.Inject/framework.h +5 -0
@@ 0,0 1,5 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files
+#include <windows.h>
A Local/NosSmooth.Inject/hostfxr.h => Local/NosSmooth.Inject/hostfxr.h +323 -0
@@ 0,0 1,323 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifndef __HOSTFXR_H__
+#define __HOSTFXR_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(_WIN32)
+#define HOSTFXR_CALLTYPE __cdecl
+#ifdef _WCHAR_T_DEFINED
+typedef wchar_t char_t;
+#else
+typedef unsigned short char_t;
+#endif
+#else
+#define HOSTFXR_CALLTYPE
+typedef char char_t;
+#endif
+
+enum hostfxr_delegate_type
+{
+ hdt_com_activation,
+ hdt_load_in_memory_assembly,
+ hdt_winrt_activation,
+ hdt_com_register,
+ hdt_com_unregister,
+ hdt_load_assembly_and_get_function_pointer,
+ hdt_get_function_pointer,
+};
+
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_main_fn)(const int argc, const char_t** argv);
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_main_startupinfo_fn)(
+ const int argc,
+ const char_t** argv,
+ const char_t* host_path,
+ const char_t* dotnet_root,
+ const char_t* app_path);
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_main_bundle_startupinfo_fn)(
+ const int argc,
+ const char_t** argv,
+ const char_t* host_path,
+ const char_t* dotnet_root,
+ const char_t* app_path,
+ int64_t bundle_header_offset);
+
+typedef void(HOSTFXR_CALLTYPE* hostfxr_error_writer_fn)(const char_t* message);
+
+//
+// Sets a callback which is to be used to write errors to.
+//
+// Parameters:
+// error_writer
+// A callback function which will be invoked every time an error is to be reported.
+// Or nullptr to unregister previously registered callback and return to the default behavior.
+// Return value:
+// The previously registered callback (which is now unregistered), or nullptr if no previous callback
+// was registered
+//
+// The error writer is registered per-thread, so the registration is thread-local. On each thread
+// only one callback can be registered. Subsequent registrations overwrite the previous ones.
+//
+// By default no callback is registered in which case the errors are written to stderr.
+//
+// Each call to the error writer is sort of like writing a single line (the EOL character is omitted).
+// Multiple calls to the error writer may occure for one failure.
+//
+// If the hostfxr invokes functions in hostpolicy as part of its operation, the error writer
+// will be propagated to hostpolicy for the duration of the call. This means that errors from
+// both hostfxr and hostpolicy will be reporter through the same error writer.
+//
+typedef hostfxr_error_writer_fn(HOSTFXR_CALLTYPE* hostfxr_set_error_writer_fn)(hostfxr_error_writer_fn error_writer);
+
+typedef void* hostfxr_handle;
+struct hostfxr_initialize_parameters
+{
+ size_t size;
+ const char_t* host_path;
+ const char_t* dotnet_root;
+};
+
+//
+// Initializes the hosting components for a dotnet command line running an application
+//
+// Parameters:
+// argc
+// Number of argv arguments
+// argv
+// Command-line arguments for running an application (as if through the dotnet executable).
+// Only command-line arguments which are accepted by runtime installation are supported, SDK/CLI commands are not supported.
+// For example 'app.dll app_argument_1 app_argument_2`.
+// parameters
+// Optional. Additional parameters for initialization
+// host_context_handle
+// On success, this will be populated with an opaque value representing the initialized host context
+//
+// Return value:
+// Success - Hosting components were successfully initialized
+// HostInvalidState - Hosting components are already initialized
+//
+// This function parses the specified command-line arguments to determine the application to run. It will
+// then find the corresponding .runtimeconfig.json and .deps.json with which to resolve frameworks and
+// dependencies and prepare everything needed to load the runtime.
+//
+// This function only supports arguments for running an application. It does not support SDK commands.
+//
+// This function does not load the runtime.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_initialize_for_dotnet_command_line_fn)(
+ int argc,
+ const char_t** argv,
+ const struct hostfxr_initialize_parameters* parameters,
+ /*out*/ hostfxr_handle* host_context_handle);
+
+//
+// Initializes the hosting components using a .runtimeconfig.json file
+//
+// Parameters:
+// runtime_config_path
+// Path to the .runtimeconfig.json file
+// parameters
+// Optional. Additional parameters for initialization
+// host_context_handle
+// On success, this will be populated with an opaque value representing the initialized host context
+//
+// Return value:
+// Success - Hosting components were successfully initialized
+// Success_HostAlreadyInitialized - Config is compatible with already initialized hosting components
+// Success_DifferentRuntimeProperties - Config has runtime properties that differ from already initialized hosting components
+// CoreHostIncompatibleConfig - Config is incompatible with already initialized hosting components
+//
+// This function will process the .runtimeconfig.json to resolve frameworks and prepare everything needed
+// to load the runtime. It will only process the .deps.json from frameworks (not any app/component that
+// may be next to the .runtimeconfig.json).
+//
+// This function does not load the runtime.
+//
+// If called when the runtime has already been loaded, this function will check if the specified runtime
+// config is compatible with the existing runtime.
+//
+// Both Success_HostAlreadyInitialized and Success_DifferentRuntimeProperties codes are considered successful
+// initializations. In the case of Success_DifferentRuntimeProperties, it is left to the consumer to verify that
+// the difference in properties is acceptable.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_initialize_for_runtime_config_fn)(
+ const char_t* runtime_config_path,
+ const struct hostfxr_initialize_parameters* parameters,
+ /*out*/ hostfxr_handle* host_context_handle);
+
+//
+// Gets the runtime property value for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// name
+// Runtime property name
+// value
+// Out parameter. Pointer to a buffer with the property value.
+//
+// Return value:
+// The error code result.
+//
+// The buffer pointed to by value is owned by the host context. The lifetime of the buffer is only
+// guaranteed until any of the below occur:
+// - a 'run' method is called for the host context
+// - properties are changed via hostfxr_set_runtime_property_value
+// - the host context is closed via 'hostfxr_close'
+//
+// If host_context_handle is nullptr and an active host context exists, this function will get the
+// property value for the active host context.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_get_runtime_property_value_fn)(
+ const hostfxr_handle host_context_handle,
+ const char_t* name,
+ /*out*/ const char_t** value);
+
+//
+// Sets the value of a runtime property for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// name
+// Runtime property name
+// value
+// Value to set
+//
+// Return value:
+// The error code result.
+//
+// Setting properties is only supported for the first host context, before the runtime has been loaded.
+//
+// If the property already exists in the host context, it will be overwritten. If value is nullptr, the
+// property will be removed.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_set_runtime_property_value_fn)(
+ const hostfxr_handle host_context_handle,
+ const char_t* name,
+ const char_t* value);
+
+//
+// Gets all the runtime properties for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// count
+// [in] Size of the keys and values buffers
+// [out] Number of properties returned (size of keys/values buffers used). If the input value is too
+// small or keys/values is nullptr, this is populated with the number of available properties
+// keys
+// Array of pointers to buffers with runtime property keys
+// values
+// Array of pointers to buffers with runtime property values
+//
+// Return value:
+// The error code result.
+//
+// The buffers pointed to by keys and values are owned by the host context. The lifetime of the buffers is only
+// guaranteed until any of the below occur:
+// - a 'run' method is called for the host context
+// - properties are changed via hostfxr_set_runtime_property_value
+// - the host context is closed via 'hostfxr_close'
+//
+// If host_context_handle is nullptr and an active host context exists, this function will get the
+// properties for the active host context.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_get_runtime_properties_fn)(
+ const hostfxr_handle host_context_handle,
+ /*inout*/ size_t* count,
+ /*out*/ const char_t** keys,
+ /*out*/ const char_t** values);
+
+//
+// Load CoreCLR and run the application for an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+//
+// Return value:
+// If the app was successfully run, the exit code of the application. Otherwise, the error code result.
+//
+// The host_context_handle must have been initialized using hostfxr_initialize_for_dotnet_command_line.
+//
+// This function will not return until the managed application exits.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_run_app_fn)(const hostfxr_handle host_context_handle);
+
+//
+// Gets a typed delegate from the currently loaded CoreCLR or from a newly created one.
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+// type
+// Type of runtime delegate requested
+// delegate
+// An out parameter that will be assigned the delegate.
+//
+// Return value:
+// The error code result.
+//
+// If the host_context_handle was initialized using hostfxr_initialize_for_runtime_config,
+// then all delegate types are supported.
+// If the host_context_handle was initialized using hostfxr_initialize_for_dotnet_command_line,
+// then only the following delegate types are currently supported:
+// hdt_load_assembly_and_get_function_pointer
+// hdt_get_function_pointer
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_get_runtime_delegate_fn)(
+ const hostfxr_handle host_context_handle,
+ enum hostfxr_delegate_type type,
+ /*out*/ void** delegate);
+
+//
+// Closes an initialized host context
+//
+// Parameters:
+// host_context_handle
+// Handle to the initialized host context
+//
+// Return value:
+// The error code result.
+//
+typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_close_fn)(const hostfxr_handle host_context_handle);
+
+struct hostfxr_dotnet_environment_sdk_info
+{
+ size_t size;
+ const char_t* version;
+ const char_t* path;
+};
+
+typedef void(HOSTFXR_CALLTYPE* hostfxr_get_dotnet_environment_info_result_fn)(
+ const struct hostfxr_dotnet_environment_info* info,
+ void* result_context);
+
+struct hostfxr_dotnet_environment_framework_info
+{
+ size_t size;
+ const char_t* name;
+ const char_t* version;
+ const char_t* path;
+};
+
+struct hostfxr_dotnet_environment_info
+{
+ size_t size;
+
+ const char_t* hostfxr_version;
+ const char_t* hostfxr_commit_hash;
+
+ size_t sdk_count;
+ const hostfxr_dotnet_environment_sdk_info* sdks;
+
+ size_t framework_count;
+ const hostfxr_dotnet_environment_framework_info* frameworks;
+};
+
+#endif //__HOSTFXR_H__<
\ No newline at end of file
A Local/NosSmooth.Inject/nethost.h => Local/NosSmooth.Inject/nethost.h +94 -0
@@ 0,0 1,94 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __NETHOST_H__
+#define __NETHOST_H__
+
+#include <stddef.h>
+
+#if defined(_WIN32)
+#ifdef NETHOST_EXPORT
+#define NETHOST_API __declspec(dllexport)
+#else
+#define NETHOST_API __declspec(dllimport)
+#endif
+
+#define NETHOST_CALLTYPE __stdcall
+#ifdef _WCHAR_T_DEFINED
+typedef wchar_t char_t;
+#else
+typedef unsigned short char_t;
+#endif
+#else
+#ifdef NETHOST_EXPORT
+#define NETHOST_API __attribute__((__visibility__("default")))
+#else
+#define NETHOST_API
+#endif
+
+#define NETHOST_CALLTYPE
+typedef char char_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ // Parameters for get_hostfxr_path
+ //
+ // Fields:
+ // size
+ // Size of the struct. This is used for versioning.
+ //
+ // assembly_path
+ // Path to the compenent's assembly.
+ // If specified, hostfxr is located as if the assembly_path is the apphost
+ //
+ // dotnet_root
+ // Path to directory containing the dotnet executable.
+ // If specified, hostfxr is located as if an application is started using
+ // 'dotnet app.dll', which means it will be searched for under the dotnet_root
+ // path and the assembly_path is ignored.
+ //
+ struct get_hostfxr_parameters {
+ size_t size;
+ const char_t* assembly_path;
+ const char_t* dotnet_root;
+ };
+
+ //
+ // Get the path to the hostfxr library
+ //
+ // Parameters:
+ // buffer
+ // Buffer that will be populated with the hostfxr path, including a null terminator.
+ //
+ // buffer_size
+ // [in] Size of buffer in char_t units.
+ // [out] Size of buffer used in char_t units. If the input value is too small
+ // or buffer is nullptr, this is populated with the minimum required size
+ // in char_t units for a buffer to hold the hostfxr path
+ //
+ // get_hostfxr_parameters
+ // Optional. Parameters that modify the behaviour for locating the hostfxr library.
+ // If nullptr, hostfxr is located using the enviroment variable or global registration
+ //
+ // Return value:
+ // 0 on success, otherwise failure
+ // 0x80008098 - buffer is too small (HostApiBufferTooSmall)
+ //
+ // Remarks:
+ // The full search for the hostfxr library is done on every call. To minimize the need
+ // to call this function multiple times, pass a large buffer (e.g. PATH_MAX).
+ //
+ NETHOST_API int NETHOST_CALLTYPE get_hostfxr_path(
+ char_t* buffer,
+ size_t* buffer_size,
+ const struct get_hostfxr_parameters* parameters);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // __NETHOST_H__
A Local/NosSmooth.Inject/nossmooth.cpp => Local/NosSmooth.Inject/nossmooth.cpp +125 -0
@@ 0,0 1,125 @@
+#include "nossmooth.h"
+
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// Standard headers
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <iostream>
+#include "nethost.h"
+#include "coreclr_delegates.h"
+#include "hostfxr.h"
+
+#include <cassert>
+
+#define STR(s) L ## s
+#define CH(c) L ## c
+#define DIR_SEPARATOR L'\\'
+
+using string_t = std::basic_string<char_t>;
+
+// Globals to hold hostfxr exports
+hostfxr_initialize_for_runtime_config_fn init_fptr;
+hostfxr_get_runtime_delegate_fn get_delegate_fptr;
+hostfxr_close_fn close_fptr;
+
+// Forward declarations
+bool load_hostfxr();
+load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t* assembly);
+
+/********************************************************************************************
+ * Function used to load and activate .NET Core
+ ********************************************************************************************/
+
+ // Forward declarations
+void* load_library(const char_t*);
+void* get_export(void*, const char*);
+
+void* load_library(const char_t* path)
+{
+ HMODULE h = ::LoadLibraryW(path);
+ assert(h != nullptr);
+ return (void*)h;
+}
+void* get_export(void* h, const char* name)
+{
+ void* f = ::GetProcAddress((HMODULE)h, name);
+ assert(f != nullptr);
+ return f;
+}
+
+// Using the nethost library, discover the location of hostfxr and get exports
+bool load_hostfxr()
+{
+ // Pre-allocate a large buffer for the path to hostfxr
+ char_t buffer[MAX_PATH];
+ size_t buffer_size = sizeof(buffer) / sizeof(char_t);
+ int rc = get_hostfxr_path(buffer, &buffer_size, nullptr);
+ if (rc != 0)
+ return false;
+
+ // Load hostfxr and get desired exports
+ void* lib = load_library(buffer);
+ init_fptr = (hostfxr_initialize_for_runtime_config_fn)get_export(lib, "hostfxr_initialize_for_runtime_config");
+ get_delegate_fptr = (hostfxr_get_runtime_delegate_fn)get_export(lib, "hostfxr_get_runtime_delegate");
+ close_fptr = (hostfxr_close_fn)get_export(lib, "hostfxr_close");
+
+ return (init_fptr && get_delegate_fptr && close_fptr);
+}
+
+// Load and initialize .NET Core and get desired function pointer for scenario
+load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t* config_path)
+{
+ // Load .NET Core
+ void* load_assembly_and_get_function_pointer = nullptr;
+ hostfxr_handle cxt = nullptr;
+ int rc = init_fptr(config_path, nullptr, &cxt);
+ if (rc != 0 || cxt == nullptr)
+ {
+ std::cerr << "Init failed: " << std::hex << std::showbase << rc << std::endl;
+ close_fptr(cxt);
+ return nullptr;
+ }
+
+ // Get the load assembly function pointer
+ rc = get_delegate_fptr(
+ cxt,
+ hdt_load_assembly_and_get_function_pointer,
+ &load_assembly_and_get_function_pointer);
+ if (rc != 0 || load_assembly_and_get_function_pointer == nullptr)
+ std::cerr << "Get delegate failed: " << std::hex << std::showbase << rc << std::endl;
+
+ close_fptr(cxt);
+ return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
+}
+
+bool LoadAndCallMethod(LoadParams* params)
+{
+ if (!load_hostfxr())
+ {
+ assert(false && "Failure: load_hostfxr()");
+ return false;
+ }
+
+ load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = nullptr;
+ load_assembly_and_get_function_pointer = get_dotnet_load_assembly(params->runtimeConfigPath);
+ assert(load_assembly_and_get_function_pointer != nullptr && "Failure: get_dotnet_load_assembly()");
+
+ typedef void (CORECLR_DELEGATE_CALLTYPE* main_entry_point_fn)();
+ main_entry_point_fn main = nullptr;
+ int rc = load_assembly_and_get_function_pointer(
+ params->libraryPath,
+ params->typePath,
+ params->methodName,
+ UNMANAGEDCALLERSONLY_METHOD,
+ nullptr,
+ (void**)&main);
+ assert(rc == 0 && main != nullptr && "Failure: load_assembly_and_get_function_pointer()");
+ main();
+ return true;
+}<
\ No newline at end of file
A Local/NosSmooth.Inject/nossmooth.h => Local/NosSmooth.Inject/nossmooth.h +15 -0
@@ 0,0 1,15 @@
+#pragma once
+#include <Windows.h>
+
+#pragma pack(push, 1)
+struct LoadParams
+{
+ wchar_t *libraryPath;
+ wchar_t *runtimeConfigPath;
+ wchar_t *typePath;
+ wchar_t *methodName;
+};
+#pragma pack(pop)
+#define DllExport extern "C" __declspec( dllexport )
+
+DllExport bool LoadAndCallMethod(LoadParams* params);<
\ No newline at end of file
A Local/NosSmooth.Inject/pch.cpp => Local/NosSmooth.Inject/pch.cpp +5 -0
@@ 0,0 1,5 @@
+// pch.cpp: source file corresponding to the pre-compiled header
+
+#include "pch.h"
+
+// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
A Local/NosSmooth.Inject/pch.h => Local/NosSmooth.Inject/pch.h +13 -0
@@ 0,0 1,13 @@
+// pch.h: This is a precompiled header file.
+// Files listed below are compiled only once, improving build performance for future builds.
+// This also affects IntelliSense performance, including code completion and many code browsing features.
+// However, files listed here are ALL re-compiled if any one of them is updated between builds.
+// Do not add files here that you will be updating frequently as this negates the performance advantage.
+
+#ifndef PCH_H
+#define PCH_H
+
+// add headers that you want to pre-compile here
+#include "framework.h"
+
+#endif //PCH_H
A Local/NosSmooth.Injector.CLI/Commands/InjectCommand.cs => Local/NosSmooth.Injector.CLI/Commands/InjectCommand.cs +56 -0
@@ 0,0 1,56 @@
+//
+// InjectCommand.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.ComponentModel;
+using Remora.Commands.Attributes;
+using Remora.Commands.Groups;
+using Remora.Results;
+
+namespace NosSmooth.Injector.CLI.Commands
+{
+ /// <summary>
+ /// Injection command for injecting .NET 5+ libraries with UnmanagedCallersOnly method.
+ /// </summary>
+ internal class InjectCommand : CommandGroup
+ {
+ private readonly NosInjector _injector;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InjectCommand"/> class.
+ /// </summary>
+ /// <param name="injector">The nos smooth injector.</param>
+ public InjectCommand(NosInjector injector)
+ {
+ _injector = injector;
+ }
+
+ /// <summary>
+ /// The command to inject.
+ /// </summary>
+ /// <param name="processId">The id of the process.</param>
+ /// <param name="dllPath">The path to the dll to inject.</param>
+ /// <param name="typeName">The full type specifier. Default is LibraryName.DllMain, LibraryName.</param>
+ /// <param name="methodName">The name of the UnmanagedCallersOnly method. Default is Main.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ [Command("inject")]
+ public Task<Result> Inject
+ (
+ [Description("The id of the process to inject into.")]
+ int processId,
+ [Description("The path to the dll to inject.")]
+ string dllPath,
+ [Option('t', "type"), Description("The full type specifier. Default is LibraryName.DllMain, LibraryName")]
+ string? typeName = null,
+ [Option('m', "method"), Description("The name of the UnmanagedCallersOnly method. Default is Main")]
+ string? methodName = null
+ )
+ {
+ var dllName = Path.GetFileNameWithoutExtension(dllPath);
+ return Task.FromResult
+ (_injector.Inject(processId, dllPath, $"{dllName}.DllMain, {dllName}", methodName ?? "Main"));
+ }
+ }
+}<
\ No newline at end of file
A Local/NosSmooth.Injector.CLI/Commands/ListProcessesCommand.cs => Local/NosSmooth.Injector.CLI/Commands/ListProcessesCommand.cs +41 -0
@@ 0,0 1,41 @@
+//
+// ListProcessesCommand.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Diagnostics;
+using Remora.Commands.Attributes;
+using Remora.Commands.Groups;
+using Remora.Results;
+
+namespace NosSmooth.Injector.CLI.Commands
+{
+ /// <summary>
+ /// Command for listing processes to find id of NosTale process.
+ /// </summary>
+ internal class ListProcessesCommand : CommandGroup
+ {
+ /// <summary>
+ /// Lists processes by the given criteria.
+ /// </summary>
+ /// <param name="nameContains">What should the name of the process contain.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ [Command("list")]
+ public Task<Result> List(string nameContains = "Nostale")
+ {
+ var processes = Process.GetProcesses();
+ foreach (var process in processes.Where(x => x.ProcessName.Contains(nameContains, StringComparison.OrdinalIgnoreCase)))
+ {
+ Console.WriteLine(ProcessToString(process));
+ }
+
+ return Task.FromResult(Result.FromSuccess());
+ }
+
+ private string ProcessToString(Process process)
+ {
+ return $"{process.ProcessName} - {process.Id}";
+ }
+ }
+}
A Local/NosSmooth.Injector.CLI/NosSmooth.Injector.CLI.csproj => Local/NosSmooth.Injector.CLI/NosSmooth.Injector.CLI.csproj +28 -0
@@ 0,0 1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net6.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ <ApplicationManifest>app.manifest</ApplicationManifest>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
+ <PackageReference Include="Remora.Commands" Version="8.0.0" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <NativeLibs Remove="Program.cs" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <NativeLibs Remove="app.manifest" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\NosSmooth.Injector\NosSmooth.Injector.csproj" />
+ </ItemGroup>
+
+</Project>
A Local/NosSmooth.Injector.CLI/Program.cs => Local/NosSmooth.Injector.CLI/Program.cs +75 -0
@@ 0,0 1,75 @@
+//
+// Program.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Extensions.DependencyInjection;
+using NosSmooth.Injector.CLI.Commands;
+using Remora.Commands.Extensions;
+using Remora.Commands.Services;
+using Remora.Results;
+
+namespace NosSmooth.Injector.CLI
+{
+ /// <summary>
+ /// The entrypoint class.
+ /// </summary>
+ public static class Program
+ {
+ /// <summary>
+ /// The entrypoint method.
+ /// </summary>
+ /// <param name="argv">The command line arguments.</param>
+ /// <returns>A task that may or may not have succeeded.</returns>
+ public static async Task Main(string[] argv)
+ {
+ var services = CreateServices();
+ var commandService = services.GetRequiredService<CommandService>();
+ var preparedCommandResult = await commandService.TryPrepareCommandAsync(string.Join(' ', argv), services);
+ if (!preparedCommandResult.IsSuccess)
+ {
+ Console.Error.WriteLine($"There was an error, {preparedCommandResult.Error.Message}");
+ return;
+ }
+
+ if (preparedCommandResult.Entity is null)
+ {
+ Console.Error.WriteLine("You must enter a command such ast list or inject.");
+ return;
+ }
+
+ var executionResult = await commandService.TryExecuteAsync(preparedCommandResult.Entity, services);
+ if (!executionResult.Entity.IsSuccess)
+ {
+ switch (executionResult.Entity.Error)
+ {
+ case ExceptionError exc:
+ Console.Error.WriteLine($"There was an exception, {exc.Exception.Message}");
+ break;
+ default:
+ Console.Error.WriteLine($"There was an error, {executionResult.Entity.Error!.Message}");
+ break;
+ }
+
+ return;
+ }
+ }
+
+ private static IServiceProvider CreateServices()
+ {
+ var collection = new ServiceCollection();
+ collection
+ .AddSingleton<NosInjector>()
+ .AddOptions<NosInjectorOptions>();
+
+ collection
+ .AddCommands()
+ .AddCommandTree()
+ .WithCommandGroup<InjectCommand>()
+ .WithCommandGroup<ListProcessesCommand>();
+
+ return collection.BuildServiceProvider();
+ }
+ }
+}
A Local/NosSmooth.Injector.CLI/app.manifest => Local/NosSmooth.Injector.CLI/app.manifest +11 -0
@@ 0,0 1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+ <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+ <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly><
\ No newline at end of file
A Local/NosSmooth.Injector/Errors/InjectionFailedError.cs => Local/NosSmooth.Injector/Errors/InjectionFailedError.cs +15 -0
@@ 0,0 1,15 @@
+//
+// InjectionFailedError.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Remora.Results;
+
+namespace NosSmooth.Injector.Errors;
+
+/// <summary>
+/// The injection could not be finished successfully.
+/// </summary>
+/// <param name="DllPath">The path to the dll.</param>
+public record InjectionFailedError(string DllPath) : ResultError($"Could not inject {DllPath} dll into the process.");<
\ No newline at end of file
A Local/NosSmooth.Injector/Errors/InsufficientPermissionsError.cs => Local/NosSmooth.Injector/Errors/InsufficientPermissionsError.cs +20 -0
@@ 0,0 1,20 @@
+//
+// InsufficientPermissionsError.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Remora.Results;
+
+namespace NosSmooth.Injector.Errors;
+
+/// <summary>
+/// The current user has insufficient permissions to inject into the given process.
+/// </summary>
+/// <param name="ProcessId">The id of the process.</param>
+/// <param name="ProcessName">The name of the process.</param>
+public record InsufficientPermissionsError(long ProcessId, string ProcessName)
+ : ResultError
+ (
+ $"Insufficient permissions to open process {ProcessId} ({ProcessName}). Try running the injector as administrator."
+ );<
\ No newline at end of file
A Local/NosSmooth.Injector/Errors/ProcessNotFoundError.cs => Local/NosSmooth.Injector/Errors/ProcessNotFoundError.cs +16 -0
@@ 0,0 1,16 @@
+//
+// ProcessNotFoundError.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Remora.Results;
+
+namespace NosSmooth.Injector.Errors;
+
+/// <summary>
+/// The given process was not found.
+/// </summary>
+/// <param name="ProcessId">The id of the process.</param>
+public record ProcessNotFoundError(string ProcessId)
+ : NotFoundError($"Could not find process with the given id {ProcessId}.");<
\ No newline at end of file
A Local/NosSmooth.Injector/LoadParams.cs => Local/NosSmooth.Injector/LoadParams.cs +44 -0
@@ 0,0 1,44 @@
+//
+// LoadParams.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NosSmooth.Injector
+{
+ /// <summary>
+ /// The parameters passed to the inject module.
+ /// </summary>
+ internal struct LoadParams
+ {
+ /// <summary>
+ /// The full path of the library.
+ /// </summary>
+ public int LibraryPath;
+
+ /// <summary>
+ /// The full path of the library.
+ /// </summary>
+ public int RuntimeConfigPath;
+
+ /// <summary>
+ /// The full path to the type with the method marked as UnsafeCallersOnly.
+ /// </summary>
+ /// <remarks>
+ /// Can be for example "LibraryNamespace.Type, LibraryNamespace".
+ /// </remarks>
+ public int TypePath;
+
+ /// <summary>
+ /// The name of the method to execute.
+ /// </summary>
+ public int MethodName;
+ }
+}
A Local/NosSmooth.Injector/ManagedMemoryAllocation.cs => Local/NosSmooth.Injector/ManagedMemoryAllocation.cs +47 -0
@@ 0,0 1,47 @@
+//
+// ManagedMemoryAllocation.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Net.NetworkInformation;
+using Reloaded.Memory.Sources;
+
+namespace NosSmooth.Injector;
+
+/// <summary>
+/// Represents freeable memory allocation.
+/// </summary>
+internal class ManagedMemoryAllocation : IDisposable
+{
+ private readonly IMemory _memory;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ManagedMemoryAllocation"/> class.
+ /// </summary>
+ /// <param name="memory">The memory with allocation.</param>
+ /// <param name="pointer">The pointer to allocated memory.</param>
+ public ManagedMemoryAllocation(IMemory memory, IntPtr pointer)
+ {
+ Pointer = pointer;
+ _memory = memory;
+
+ }
+
+ /// <summary>
+ /// The allocated pointer number.
+ /// </summary>
+ public IntPtr Pointer { get; private set; }
+
+ /// <summary>
+ /// Whether the memory is currently allocated.
+ /// </summary>
+ public bool Allocated => Pointer != IntPtr.Zero;
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ _memory.Free(Pointer);
+ Pointer = IntPtr.Zero;
+ }
+}<
\ No newline at end of file
A Local/NosSmooth.Injector/NosInjector.cs => Local/NosSmooth.Injector/NosInjector.cs +155 -0
@@ 0,0 1,155 @@
+//
+// NosInjector.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Diagnostics;
+using System.Security;
+using System.Text;
+using Microsoft.Extensions.Options;
+using NosSmooth.Injector.Errors;
+using Reloaded.Memory.Sources;
+using Remora.Results;
+
+namespace NosSmooth.Injector;
+
+/// <summary>
+/// Nos smooth injector for .NET 5+ projects.
+/// </summary>
+/// <remarks>
+/// If you want to inject your project into NosTale that
+/// uses NosSmooth.LocalClient, use this injector.
+/// You must expose static UnmanagedCallersOnly method.
+/// </remarks>
+public class NosInjector
+{
+ private readonly NosInjectorOptions _options;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NosInjector"/> class.
+ /// </summary>
+ /// <param name="options">The injector options.</param>
+ public NosInjector(IOptions<NosInjectorOptions> options)
+ {
+ _options = options.Value;
+ }
+
+ /// <summary>
+ /// Inject the given .NET 5+ dll into NosTale process.
+ /// </summary>
+ /// <remarks>
+ /// The dll must also have .runtimeconfig.json present next to the dll.
+ /// </remarks>
+ /// <param name="processId">The id of the process to inject to.</param>
+ /// <param name="dllPath">The absolute path to the dll to inject.</param>
+ /// <param name="classPath">The full path to the class. Such as "MyLibrary.DllMain, MyLibrary".</param>
+ /// <param name="methodName">The name of the method to execute. The method should return void and have no parameters.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Result Inject
+ (
+ int processId,
+ string dllPath,
+ string classPath,
+ string methodName = "Main"
+ )
+ {
+ using var process = Process.GetProcessById(processId);
+ return Inject(process, dllPath, classPath, methodName);
+ }
+
+ /// <summary>
+ /// Inject the given .NET 5+ dll into NosTale process.
+ /// </summary>
+ /// <remarks>
+ /// The dll must also have .runtimeconfig.json present next to the dll.
+ /// </remarks>
+ /// <param name="process">The process to inject to.</param>
+ /// <param name="dllPath">The absolute path to the dll to inject.</param>
+ /// <param name="classPath">The full path to the class. Such as "MyLibrary.DllMain, MyLibrary".</param>
+ /// <param name="methodName">The name of the method to execute. The method should return void and have no parameters.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Result Inject
+ (
+ Process process,
+ string dllPath,
+ string classPath,
+ string methodName = "Main"
+ )
+ {
+ try
+ {
+ using var injector = new Reloaded.Injector.Injector(process);
+ var memory = new ExternalMemory(process);
+
+ var directoryName = Path.GetDirectoryName(dllPath);
+ if (directoryName is null)
+ {
+ return new GenericError("There was an error obtaining directory name of the dll path.");
+ }
+
+ var runtimePath = Path.Combine
+ (directoryName, Path.GetFileNameWithoutExtension(dllPath)) + ".runtimeconfig.json";
+
+ using var dllPathMemory = AllocateString(memory, dllPath);
+ using var classPathMemory = AllocateString(memory, classPath);
+ using var methodNameMemory = AllocateString(memory, methodName);
+ using var runtimePathMemory = AllocateString(memory, runtimePath);
+
+ if (!dllPathMemory.Allocated || !classPathMemory.Allocated || !methodNameMemory.Allocated
+ || !runtimePathMemory.Allocated)
+ {
+ return new GenericError("Could not allocate memory in the external process.");
+ }
+
+ var loadParams = new LoadParams()
+ {
+ LibraryPath = (int)dllPathMemory.Pointer,
+ MethodName = (int)methodNameMemory.Pointer,
+ RuntimeConfigPath = (int)runtimePathMemory.Pointer,
+ TypePath = (int)classPathMemory.Pointer
+ };
+
+ var nosSmoothInjectPath = Path.GetFullPath(_options.NosSmoothInjectPath);
+ var injected = injector.Inject(nosSmoothInjectPath);
+ if (injected == 0)
+ {
+ return new InjectionFailedError(nosSmoothInjectPath);
+ }
+
+ var functionResult = injector.CallFunction(nosSmoothInjectPath, "LoadAndCallMethod", loadParams);
+ if (functionResult != 0)
+ {
+ return new InjectionFailedError(dllPath);
+ }
+
+ return Result.FromSuccess();
+ }
+ catch (UnauthorizedAccessException)
+ {
+ return new InsufficientPermissionsError(process.Id, process.ProcessName);
+ }
+ catch (SecurityException)
+ {
+ return new InsufficientPermissionsError(process.Id, process.ProcessName);
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ }
+
+ private ManagedMemoryAllocation AllocateString(IMemory memory, string str)
+ {
+ var bytes = Encoding.Unicode.GetBytes(str);
+ var allocated = memory.Allocate(bytes.Length + 1);
+ if (allocated == IntPtr.Zero)
+ {
+ return new ManagedMemoryAllocation(memory, allocated);
+ }
+
+ memory.SafeWriteRaw(allocated + bytes.Length, new byte[] { 0 });
+ memory.SafeWriteRaw(allocated, bytes);
+ return new ManagedMemoryAllocation(memory, allocated);
+ }
+}<
\ No newline at end of file
A Local/NosSmooth.Injector/NosInjectorOptions.cs => Local/NosSmooth.Injector/NosInjectorOptions.cs +32 -0
@@ 0,0 1,32 @@
+//
+// NosInjectorOptions.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Options;
+
+namespace NosSmooth.Injector
+{
+ /// <summary>
+ /// Options for NosInjector.
+ /// </summary>
+ public class NosInjectorOptions : IOptions<NosInjectorOptions>
+ {
+ /// <summary>
+ /// Gets or sets the path to the nos smooth inject dll.
+ /// </summary>
+ /// <remarks>
+ /// If not absolute path, then relative path from the current executing process is assumed.
+ /// </remarks>
+ public string NosSmoothInjectPath { get; set; } = "NosSmooth.Inject.dll";
+
+ /// <inheritdoc/>
+ public NosInjectorOptions Value => this;
+ }
+}
A Local/NosSmooth.Injector/NosSmooth.Injector.csproj => Local/NosSmooth.Injector/NosSmooth.Injector.csproj +15 -0
@@ 0,0 1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net6.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
+ <PackageReference Include="Reloaded.Injector" Version="1.2.4" />
+ <PackageReference Include="Remora.Results" Version="7.1.0" />
+ </ItemGroup>
+
+</Project>
M Local/NosSmooth.LocalClient/NosSmooth.LocalClient.csproj => Local/NosSmooth.LocalClient/NosSmooth.LocalClient.csproj +1 -2
@@ 4,8 4,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
- <TargetFramework>net48</TargetFramework>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <TargetFrameworks>net6.0;netstandard2.1</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
M Local/NosSmooth.LocalClient/Utils/User32.cs => Local/NosSmooth.LocalClient/Utils/User32.cs +1 -0
@@ 7,6 7,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;
+#pragma warning disable CS1591
namespace NosSmooth.LocalClient.Utils;
M NosSmooth.sln => NosSmooth.sln +58 -43
@@ 48,11 48,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NosSmooth.PacketSerializers
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NosSmooth.Packets.Tests", "Tests\NosSmooth.Packets.Tests\NosSmooth.Packets.Tests.csproj", "{9BC56B40-64E3-4A8F-AD49-C52857A35026}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NativeExecutor", "NativeExecutor\NativeExecutor.vcxproj", "{1BA17328-38FE-4830-9F26-F24585D4CAAB}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NosSmooth.Inject", "Local\NosSmooth.Inject\NosSmooth.Inject.vcxproj", "{CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "ClassLibrary1\ClassLibrary1.csproj", "{4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NosSmooth.Injector.CLI", "Local\NosSmooth.Injector.CLI\NosSmooth.Injector.CLI.csproj", "{5D351C91-C631-40F6-82D2-F8D68468B076}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.LocalBinding", "Local\NosSmooth.LocalBinding\NosSmooth.LocalBinding.csproj", "{AA21666C-C2EF-4899-B047-889657CEB2D9}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.Injector", "Local\NosSmooth.Injector\NosSmooth.Injector.csproj", "{7B68DE7D-159B-42A7-8CF8-69F156CD04DE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.LocalBinding", "Local\NosSmooth.LocalBinding\NosSmooth.LocalBinding.csproj", "{BC0DCD32-E917-4187-8CF3-970D17301ED3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ 232,42 234,54 @@ Global
{9BC56B40-64E3-4A8F-AD49-C52857A35026}.Release|x64.Build.0 = Release|Any CPU
{9BC56B40-64E3-4A8F-AD49-C52857A35026}.Release|x86.ActiveCfg = Release|Any CPU
{9BC56B40-64E3-4A8F-AD49-C52857A35026}.Release|x86.Build.0 = Release|Any CPU
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Debug|Any CPU.ActiveCfg = Debug|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Debug|Any CPU.Build.0 = Debug|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Debug|x64.ActiveCfg = Debug|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Debug|x64.Build.0 = Debug|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Debug|x86.ActiveCfg = Debug|Win32
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Debug|x86.Build.0 = Debug|Win32
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Release|Any CPU.ActiveCfg = Release|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Release|Any CPU.Build.0 = Release|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Release|x64.ActiveCfg = Release|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Release|x64.Build.0 = Release|x64
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Release|x86.ActiveCfg = Release|Win32
- {1BA17328-38FE-4830-9F26-F24585D4CAAB}.Release|x86.Build.0 = Release|Win32
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Debug|x64.ActiveCfg = Debug|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Debug|x64.Build.0 = Debug|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Debug|x86.ActiveCfg = Debug|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Debug|x86.Build.0 = Debug|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Release|Any CPU.Build.0 = Release|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Release|x64.ActiveCfg = Release|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Release|x64.Build.0 = Release|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Release|x86.ActiveCfg = Release|Any CPU
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA}.Release|x86.Build.0 = Release|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Debug|x64.ActiveCfg = Debug|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Debug|x64.Build.0 = Debug|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Debug|x86.ActiveCfg = Debug|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Debug|x86.Build.0 = Debug|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Release|Any CPU.Build.0 = Release|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Release|x64.ActiveCfg = Release|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Release|x64.Build.0 = Release|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Release|x86.ActiveCfg = Release|Any CPU
- {AA21666C-C2EF-4899-B047-889657CEB2D9}.Release|x86.Build.0 = Release|Any CPU
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|Any CPU.Build.0 = Debug|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|x64.ActiveCfg = Debug|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|x64.Build.0 = Debug|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|x86.ActiveCfg = Debug|Win32
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|x86.Build.0 = Debug|Win32
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Release|Any CPU.ActiveCfg = Release|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Release|Any CPU.Build.0 = Release|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Release|x64.ActiveCfg = Release|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Release|x64.Build.0 = Release|x64
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Release|x86.ActiveCfg = Release|Win32
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Release|x86.Build.0 = Release|Win32
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Debug|x64.Build.0 = Debug|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Debug|x86.Build.0 = Debug|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Release|x64.ActiveCfg = Release|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Release|x64.Build.0 = Release|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Release|x86.ActiveCfg = Release|Any CPU
+ {5D351C91-C631-40F6-82D2-F8D68468B076}.Release|x86.Build.0 = Release|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Debug|x64.Build.0 = Debug|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Debug|x86.Build.0 = Debug|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Release|x64.ActiveCfg = Release|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Release|x64.Build.0 = Release|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Release|x86.ActiveCfg = Release|Any CPU
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE}.Release|x86.Build.0 = Release|Any CPU
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3}.Debug|x64.Build.0 = Debug|Any CPU
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3}.Debug|x86.Build.0 = Debug|Any CPU
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3}.Release|x64.ActiveCfg = Release|Any CPU
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3}.Release|x64.Build.0 = Release|Any CPU
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3}.Release|x86.ActiveCfg = Release|Any CPU
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ 282,14 296,15 @@ Global
{726188BA-F0EA-4ECA-ACF4-CCC066464FF0} = {C6A8760D-92CB-4307-88A7-36CCAEBA4AD1}
{F96F3AA0-131E-4B6B-AB21-BBE2DEBCEF3A} = {9025731C-084E-4E82-8CD4-0F52D3AA1F54}
{9025731C-084E-4E82-8CD4-0F52D3AA1F54} = {F20FE754-FDEA-4F3A-93D4-0750CB9EBB33}
- {18A62EF6-ADDA-4224-90AB-2D5DCFC95D3E} = {F20FE754-FDEA-4F3A-93D4-0750CB9EBB33}
{1A10C624-48E5-425D-938E-31A4CC7AC687} = {C6A8760D-92CB-4307-88A7-36CCAEBA4AD1}
{C4DAFD83-C6DC-4597-AA1F-BA2F3ABB612C} = {01B5E872-271F-4D30-A1AA-AD48D81840C5}
{C61EBDB6-053C-48C3-B896-58642639C93F} = {01B5E872-271F-4D30-A1AA-AD48D81840C5}
{9BC56B40-64E3-4A8F-AD49-C52857A35026} = {C6A8760D-92CB-4307-88A7-36CCAEBA4AD1}
- {1BA17328-38FE-4830-9F26-F24585D4CAAB} = {F20FE754-FDEA-4F3A-93D4-0750CB9EBB33}
- {4F7B9EBB-B2F4-4170-ABDA-504D19D807AA} = {F20FE754-FDEA-4F3A-93D4-0750CB9EBB33}
- {AA21666C-C2EF-4899-B047-889657CEB2D9} = {6078AE6E-7CD0-48E4-84E0-EB164D8881DA}
+ {CA2873D8-BD0B-4583-818D-B94A3C2ABBA3} = {6078AE6E-7CD0-48E4-84E0-EB164D8881DA}
+ {5D351C91-C631-40F6-82D2-F8D68468B076} = {6078AE6E-7CD0-48E4-84E0-EB164D8881DA}
+ {7B68DE7D-159B-42A7-8CF8-69F156CD04DE} = {6078AE6E-7CD0-48E4-84E0-EB164D8881DA}
+ {18A62EF6-ADDA-4224-90AB-2D5DCFC95D3E} = {F20FE754-FDEA-4F3A-93D4-0750CB9EBB33}
+ {BC0DCD32-E917-4187-8CF3-970D17301ED3} = {6078AE6E-7CD0-48E4-84E0-EB164D8881DA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C5F46653-4DEC-429B-8580-4ED18ED9B4CA}
M Samples/InterceptNameChanger/DllMain.cs => Samples/InterceptNameChanger/DllMain.cs +8 -9
@@ 23,22 23,21 @@ namespace InterceptNameChanger
/// <summary>
/// The main entrypoint method of the dll.
/// </summary>
- /// <param name="handle">The handle of the module.</param>
- [DllExport]
- public static void Main(IntPtr handle)
+ [UnmanagedCallersOnly(EntryPoint = "Main")]
+ public static void Main()
{
AllocConsole();
Console.WriteLine("Hello from InterceptNameChanger DllMain entry point.");
new Thread(() =>
{
- try
- {
- new NameChanger().RunAsync().GetAwaiter().GetResult();
+ try
+ {
+ new NameChanger().RunAsync().GetAwaiter().GetResult();
}
- catch (Exception e)
- {
- Console.WriteLine(e.ToString());
+ catch (Exception e)
+ {
+ Console.WriteLine(e.ToString());
}
}).Start();
}
M Samples/InterceptNameChanger/InterceptNameChanger.csproj => Samples/InterceptNameChanger/InterceptNameChanger.csproj +2 -44
@@ 1,37 1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net48</TargetFramework>
- <ImplicitUsings>enable</ImplicitUsings>
+ <TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
- </PropertyGroup>
- <PropertyGroup>
- <DllExportIdent>89E4EE92-5848-4390-A6A7-34972FE923F5</DllExportIdent>
- <DllExportMetaLibName>DllExport.dll</DllExportMetaLibName>
- <DllExportNamespace>InterceptNameChanger</DllExportNamespace>
- <DllExportDDNSCecil>true</DllExportDDNSCecil>
- <PlatformTarget>x86</PlatformTarget>
- <DllExportOrdinalsBase>7</DllExportOrdinalsBase>
- <DllExportGenExpLib>false</DllExportGenExpLib>
- <DllExportOurILAsm>false</DllExportOurILAsm>
- <DllExportSysObjRebase>false</DllExportSysObjRebase>
- <DllExportLeaveIntermediateFiles>false</DllExportLeaveIntermediateFiles>
- <DllExportTimeout>30000</DllExportTimeout>
- <DllExportPeCheck>2</DllExportPeCheck>
- <DllExportPatches>0</DllExportPatches>
- <DllExportPreProcType>0</DllExportPreProcType>
- <DllExportPostProcType>0</DllExportPostProcType>
+ <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Costura.Fody" Version="5.7.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="DllExport">
- <Version>1.7.4</Version>
- <Visible>false</Visible>
- <Wz>1</Wz>
- </PackageReference>
<PackageReference Include="Fody" Version="6.6.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ 44,24 22,4 @@
<ProjectReference Include="..\..\Core\NosSmooth.Core\NosSmooth.Core.csproj" />
<ProjectReference Include="..\..\Local\NosSmooth.LocalClient\NosSmooth.LocalClient.csproj" />
</ItemGroup>
- <ImportGroup Label=".NET DllExport">
- <Import Project="$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets" Condition="Exists($([MSBuild]::Escape('$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets')))" Label="8337224c9ad9e356" />
- </ImportGroup>
- <Target Name="DllExportRestorePkg" BeforeTargets="PrepareForBuild">
- <Error Condition="!Exists('$(SolutionDir)DllExport.bat')" Text="DllExport.bat is not found. Path: '$(SolutionDir)' - https://github.com/3F/DllExport" />
- <Exec Condition="('$(DllExportModImported)' != 'true' Or !Exists('$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets')) And Exists('$(SolutionDir)DllExport.bat')" Command=".\DllExport.bat -action Restore" WorkingDirectory="$(SolutionDir)" />
- <MSBuild Condition="'$(DllExportModImported)' != 'true'" Projects="$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets" Targets="DllExportMetaXBaseTarget" Properties="TargetFramework=$(TargetFramework)">
- <Output TaskParameter="TargetOutputs" PropertyName="DllExportMetaXBase" />
- </MSBuild>
- <ItemGroup>
- <Reference Include="DllExport, PublicKeyToken=8337224c9ad9e356">
- <HintPath>$(SolutionDir)packages\DllExport.1.7.4\gcache\$(DllExportMetaXBase)\$(DllExportNamespace)\$(DllExportMetaLibName)</HintPath>
- <Private>False</Private>
- <SpecificVersion>False</SpecificVersion>
- </Reference>
- </ItemGroup>
- </Target>
- <Target Name="DllExportRPkgDynamicImport" BeforeTargets="PostBuildEvent" DependsOnTargets="GetFrameworkPaths" Condition="'$(DllExportModImported)' != 'true' And '$(DllExportRPkgDyn)' != 'false'">
- <MSBuild BuildInParallel="true" UseResultsCache="true" Projects="$(MSBuildProjectFullPath)" Properties="DllExportRPkgDyn=true" Targets="Build" />
- </Target>
</Project>=
\ No newline at end of file
M Samples/SimpleChat/DllMain.cs => Samples/SimpleChat/DllMain.cs +6 -7
@@ 4,15 4,15 @@
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using System.Runtime.InteropServices;
-
+using System.Runtime.InteropServices;
+
namespace SimpleChat;
/// <summary>
/// The main entrypoint class of the dll.
/// </summary>
public class DllMain
-{
+{
[DllImport("kernel32")]
#pragma warning disable SA1600
public static extern bool AllocConsole();
@@ 21,10 21,9 @@ public class DllMain
/// <summary>
/// The main entrypoint method of the dll.
/// </summary>
- /// <param name="handle">The handle of the module.</param>
- [DllExport]
- public static void Main(IntPtr handle)
- {
+ [UnmanagedCallersOnly(EntryPoint = "Main")]
+ public static void Main()
+ {
AllocConsole();
Console.WriteLine("Hello from SimpleChat DllMain entry point.");
M Samples/SimpleChat/SimpleChat.cs => Samples/SimpleChat/SimpleChat.cs +9 -0
@@ 8,6 8,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NosSmooth.Core.Client;
using NosSmooth.Core.Extensions;
+using NosSmooth.LocalBinding;
using NosSmooth.LocalClient.Extensions;
using NosSmooth.Packets.Enums;
using NosSmooth.Packets.Enums.Chat;
@@ 43,6 44,14 @@ public class SimpleChat
var logger = provider.GetRequiredService<ILogger<SimpleChat>>();
logger.LogInformation("Hello world from SimpleChat!");
+ var bindingManager = provider.GetRequiredService<NosBindingManager>();
+ var initializeResult = bindingManager.Initialize();
+ if (!initializeResult.IsSuccess)
+ {
+ logger.LogError($"Could not initialize the bindings {initializeResult.Error.Message}.");
+ return;
+ }
+
var client = provider.GetRequiredService<INostaleClient>();
await client.ReceivePacketAsync
M Samples/SimpleChat/SimpleChat.csproj => Samples/SimpleChat/SimpleChat.csproj +2 -43
@@ 1,37 1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net48</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
- </PropertyGroup>
- <PropertyGroup>
- <DllExportIdent>89E4EE92-5848-4390-A6A7-34972FE923F5</DllExportIdent>
- <DllExportMetaLibName>DllExport.dll</DllExportMetaLibName>
- <DllExportNamespace>SimpleChat</DllExportNamespace>
- <DllExportDDNSCecil>true</DllExportDDNSCecil>
- <PlatformTarget>x86</PlatformTarget>
- <DllExportOrdinalsBase>7</DllExportOrdinalsBase>
- <DllExportGenExpLib>false</DllExportGenExpLib>
- <DllExportOurILAsm>false</DllExportOurILAsm>
- <DllExportSysObjRebase>false</DllExportSysObjRebase>
- <DllExportLeaveIntermediateFiles>false</DllExportLeaveIntermediateFiles>
- <DllExportTimeout>30000</DllExportTimeout>
- <DllExportPeCheck>2</DllExportPeCheck>
- <DllExportPatches>0</DllExportPatches>
- <DllExportPreProcType>0</DllExportPreProcType>
- <DllExportPostProcType>0</DllExportPostProcType>
+ <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Costura.Fody" Version="5.7.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="DllExport">
- <Version>1.7.4</Version>
- <Visible>false</Visible>
- <Wz>1</Wz>
- </PackageReference>
<PackageReference Include="Fody" Version="6.6.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ 44,24 23,4 @@
<ProjectReference Include="..\..\Core\NosSmooth.Core\NosSmooth.Core.csproj" />
<ProjectReference Include="..\..\Local\NosSmooth.LocalClient\NosSmooth.LocalClient.csproj" />
</ItemGroup>
- <ImportGroup Label=".NET DllExport">
- <Import Project="$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets" Condition="Exists($([MSBuild]::Escape('$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets')))" Label="8337224c9ad9e356" />
- </ImportGroup>
- <Target Name="DllExportRestorePkg" BeforeTargets="PrepareForBuild">
- <Error Condition="!Exists('$(SolutionDir)DllExport.bat')" Text="DllExport.bat is not found. Path: '$(SolutionDir)' - https://github.com/3F/DllExport" />
- <Exec Condition="('$(DllExportModImported)' != 'true' Or !Exists('$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets')) And Exists('$(SolutionDir)DllExport.bat')" Command=".\DllExport.bat -action Restore" WorkingDirectory="$(SolutionDir)" />
- <MSBuild Condition="'$(DllExportModImported)' != 'true'" Projects="$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets" Targets="DllExportMetaXBaseTarget" Properties="TargetFramework=$(TargetFramework)">
- <Output TaskParameter="TargetOutputs" PropertyName="DllExportMetaXBase" />
- </MSBuild>
- <ItemGroup>
- <Reference Include="DllExport, PublicKeyToken=8337224c9ad9e356">
- <HintPath>$(SolutionDir)packages\DllExport.1.7.4\gcache\$(DllExportMetaXBase)\$(DllExportNamespace)\$(DllExportMetaLibName)</HintPath>
- <Private>False</Private>
- <SpecificVersion>False</SpecificVersion>
- </Reference>
- </ItemGroup>
- </Target>
- <Target Name="DllExportRPkgDynamicImport" BeforeTargets="PostBuildEvent" DependsOnTargets="GetFrameworkPaths" Condition="'$(DllExportModImported)' != 'true' And '$(DllExportRPkgDyn)' != 'false'">
- <MSBuild BuildInParallel="true" UseResultsCache="true" Projects="$(MSBuildProjectFullPath)" Properties="DllExportRPkgDyn=true" Targets="Build" />
- </Target>
</Project>=
\ No newline at end of file
M Samples/WalkCommands/ChatPacketInterceptor.cs => Samples/WalkCommands/ChatPacketInterceptor.cs +2 -2
@@ 88,8 88,8 @@ public class ChatPacketInterceptor : IPacketInterceptor
var walkCommand = scope.ServiceProvider.GetRequiredService<Commands.WalkCommands>();
result = await walkCommand.HandleWalkToAsync
(
- int.Parse(splitted[1]),
- int.Parse(splitted[2]),
+ ushort.Parse(splitted[1]),
+ ushort.Parse(splitted[2]),
splitted.Length > 3 ? bool.Parse(splitted[3]) : true
);
break;
M Samples/WalkCommands/DllMain.cs => Samples/WalkCommands/DllMain.cs +1 -1
@@ 21,7 21,7 @@ public class DllMain
/// <summary>
/// Represents the dll entrypoint method.
/// </summary>
- [DllExport]
+ [UnmanagedCallersOnly(EntryPoint = "Main")]
public static void Main()
{
AllocConsole();
M Samples/WalkCommands/Startup.cs => Samples/WalkCommands/Startup.cs +12 -0
@@ 7,8 7,12 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NosSmooth.Core.Client;
+using NosSmooth.LocalBinding;
using NosSmooth.LocalClient;
using NosSmooth.LocalClient.Extensions;
+using NosSmooth.Packets.Enums;
+using NosSmooth.Packets.Enums.Chat;
+using NosSmooth.Packets.Packets.Server.Chat;
using WalkCommands.Commands;
namespace WalkCommands;
@@ 43,6 47,14 @@ public class Startup
public async Task RunAsync()
{
var provider = BuildServices();
+ var bindingManager = provider.GetRequiredService<NosBindingManager>();
+ var logger = provider.GetRequiredService<ILogger<Startup>>();
+ var initializeResult = bindingManager.Initialize();
+ if (!initializeResult.IsSuccess)
+ {
+ logger.LogError($"Could not initialize {initializeResult.Error.Message}.");
+ }
+
var mainCancellation = provider.GetRequiredService<CancellationTokenSource>();
var client = provider.GetRequiredService<INostaleClient>();
M Samples/WalkCommands/WalkCommands.csproj => Samples/WalkCommands/WalkCommands.csproj +6 -43
@@ 1,28 1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>net48</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>WalkCommands</AssemblyName>
<RootNamespace>WalkCommands</RootNamespace>
<LangVersion>10</LangVersion>
- </PropertyGroup>
- <PropertyGroup>
- <DllExportIdent>9C088A1D-54DE-4A9B-9C1B-DBC5BD5F5299</DllExportIdent>
- <DllExportMetaLibName>DllExport.dll</DllExportMetaLibName>
- <DllExportNamespace>WalkCommands</DllExportNamespace>
- <DllExportDDNSCecil>true</DllExportDDNSCecil>
- <PlatformTarget>x86</PlatformTarget>
- <DllExportOrdinalsBase>7</DllExportOrdinalsBase>
- <DllExportGenExpLib>false</DllExportGenExpLib>
- <DllExportOurILAsm>false</DllExportOurILAsm>
- <DllExportSysObjRebase>false</DllExportSysObjRebase>
- <DllExportLeaveIntermediateFiles>false</DllExportLeaveIntermediateFiles>
- <DllExportTimeout>30000</DllExportTimeout>
- <DllExportPeCheck>2</DllExportPeCheck>
- <DllExportPatches>0</DllExportPatches>
- <DllExportPreProcType>0</DllExportPreProcType>
- <DllExportPostProcType>0</DllExportPostProcType>
+ <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
+ <IncludeCopyLocalFilesOutputGroup>true</IncludeCopyLocalFilesOutputGroup>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Costura.Fody">
@@ 30,11 15,6 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="DllExport">
- <Version>1.7.4</Version>
- <Visible>false</Visible>
- <Wz>1</Wz>
- </PackageReference>
<PackageReference Include="Fody">
<Version>6.6.0</Version>
<PrivateAssets>all</PrivateAssets>
@@ 49,28 29,11 @@
<PackageReference Include="Microsoft.Extensions.Logging.Console">
<Version>6.0.0</Version>
</PackageReference>
+ <PackageReference Include="Remora.Results">
+ <Version>7.1.0</Version>
+ </PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Local\NosSmooth.LocalClient\NosSmooth.LocalClient.csproj" />
</ItemGroup>
- <ImportGroup Label=".NET DllExport">
- <Import Project="$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets" Condition="Exists($([MSBuild]::Escape('$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets')))" Label="8337224c9ad9e356" />
- </ImportGroup>
- <Target Name="DllExportRestorePkg" BeforeTargets="PrepareForBuild">
- <Error Condition="!Exists('$(SolutionDir)DllExport.bat')" Text="DllExport.bat is not found. Path: '$(SolutionDir)' - https://github.com/3F/DllExport" />
- <Exec Condition="('$(DllExportModImported)' != 'true' Or !Exists('$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets')) And Exists('$(SolutionDir)DllExport.bat')" Command=".\DllExport.bat -action Restore" WorkingDirectory="$(SolutionDir)" />
- <MSBuild Condition="'$(DllExportModImported)' != 'true'" Projects="$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets" Targets="DllExportMetaXBaseTarget" Properties="TargetFramework=$(TargetFramework)">
- <Output TaskParameter="TargetOutputs" PropertyName="DllExportMetaXBase" />
- </MSBuild>
- <ItemGroup>
- <Reference Include="DllExport, PublicKeyToken=8337224c9ad9e356">
- <HintPath>$(SolutionDir)packages\DllExport.1.7.4\gcache\$(DllExportMetaXBase)\$(DllExportNamespace)\$(DllExportMetaLibName)</HintPath>
- <Private>False</Private>
- <SpecificVersion>False</SpecificVersion>
- </Reference>
- </ItemGroup>
- </Target>
- <Target Name="DllExportRPkgDynamicImport" BeforeTargets="PostBuildEvent" DependsOnTargets="GetFrameworkPaths" Condition="'$(DllExportModImported)' != 'true' And '$(DllExportRPkgDyn)' != 'false'">
- <MSBuild BuildInParallel="true" UseResultsCache="true" Projects="$(MSBuildProjectFullPath)" Properties="DllExportRPkgDyn=true" Targets="Build" />
- </Target>
</Project>=
\ No newline at end of file
M Tests/NosSmooth.Packets.Tests/PacketStringBuilderTests.cs => Tests/NosSmooth.Packets.Tests/PacketStringBuilderTests.cs +0 -1
@@ 55,7 55,6 @@ public class PacketStringBuilderTests
stringBuilder.Append("a");
stringBuilder.Append("b");
stringBuilder.Append("c");
- stringBuilder.ReplaceWithParentSeparator();
stringBuilder.PopLevel();
stringBuilder.Append("d");
M Tests/NosSmooth.Packets.Tests/PacketStringEnumeratorTests.cs => Tests/NosSmooth.Packets.Tests/PacketStringEnumeratorTests.cs +67 -62
@@ 23,64 23,65 @@ public class PacketStringEnumeratorTests
public void EnumeratorListComplexStringGivesCorrectResult()
{
var stringEnumerator = new PacketStringEnumerator("in 1 11.12.13|14.15.16|17.18.19");
- var headerTokenResult = stringEnumerator.GetNextToken();
+ var headerTokenResult = stringEnumerator.GetNextToken(out var packetToken);
Assert.True(headerTokenResult.IsSuccess);
- Assert.False(headerTokenResult.Entity.PacketEndReached);
- Assert.NotNull(headerTokenResult.Entity.IsLast);
- Assert.NotNull(headerTokenResult.Entity.EncounteredUpperLevel);
- Assert.False(headerTokenResult.Entity.IsLast);
- Assert.False(headerTokenResult.Entity.EncounteredUpperLevel);
- Assert.Matches("in", headerTokenResult.Entity.Token);
-
- var firstToken = stringEnumerator.GetNextToken();
+ Assert.False(packetToken.PacketEndReached);
+ Assert.NotNull(packetToken.IsLast);
+ Assert.NotNull(packetToken.EncounteredUpperLevel);
+ Assert.False(packetToken.IsLast);
+ Assert.False(packetToken.EncounteredUpperLevel);
+ Assert.Matches("in", packetToken.Token.ToString());
+
+ var firstToken = stringEnumerator.GetNextToken(out packetToken);
Assert.True(firstToken.IsSuccess);
- Assert.False(firstToken.Entity.PacketEndReached);
- Assert.NotNull(firstToken.Entity.IsLast);
- Assert.NotNull(firstToken.Entity.EncounteredUpperLevel);
- Assert.False(firstToken.Entity.IsLast);
- Assert.False(firstToken.Entity.EncounteredUpperLevel);
- Assert.Matches("1", firstToken.Entity.Token);
+ Assert.False(packetToken.PacketEndReached);
+ Assert.NotNull(packetToken.IsLast);
+ Assert.NotNull(packetToken.EncounteredUpperLevel);
+ Assert.False(packetToken.IsLast);
+ Assert.False(packetToken.EncounteredUpperLevel);
+ Assert.Matches("1", packetToken.Token.ToString());
- var listEnumerator = stringEnumerator.CreateLevel('|');
- listEnumerator.PrepareLevel('.');
+ stringEnumerator.PushLevel('|');
+ stringEnumerator.PrepareLevel('.');
for (int i = 0; i < 3; i++)
{
- var preparedLevel = listEnumerator.CreatePreparedLevel();
- Assert.NotNull(preparedLevel);
+ stringEnumerator.PushPreparedLevel();
for (int j = 0; j < 3; j++)
{
string currentNum = (j + (i * 3) + 1 + 10).ToString();
- var currentToken = preparedLevel!.Value.GetNextToken();
+ var currentToken = stringEnumerator.GetNextToken(out packetToken);
Assert.True(currentToken.IsSuccess);
- Assert.False(currentToken.Entity.PacketEndReached);
- Assert.NotNull(currentToken.Entity.IsLast);
- Assert.NotNull(currentToken.Entity.EncounteredUpperLevel);
+ Assert.False(packetToken.PacketEndReached);
+ Assert.NotNull(packetToken.IsLast);
+ Assert.NotNull(packetToken.EncounteredUpperLevel);
if (j == 2 && i == 2)
{
- Assert.True(currentToken.Entity.EncounteredUpperLevel);
+ Assert.True(packetToken.EncounteredUpperLevel);
}
else
{
- Assert.False(currentToken.Entity.EncounteredUpperLevel);
+ Assert.False(packetToken.EncounteredUpperLevel);
}
if (j != 2)
{
- Assert.False(currentToken.Entity.IsLast);
+ Assert.False(packetToken.IsLast);
}
else
{
- Assert.True(currentToken.Entity.IsLast);
+ Assert.True(packetToken.IsLast);
}
- Assert.Matches(currentNum, currentToken.Entity.Token);
+ Assert.Matches(currentNum, packetToken.Token.ToString());
}
- Assert.True(preparedLevel!.Value.IsOnLastToken());
+ Assert.True(stringEnumerator.IsOnLastToken());
+ stringEnumerator.PopLevel();
}
+ stringEnumerator.PopLevel();
Assert.True(stringEnumerator.IsOnLastToken());
}
@@ 91,18 92,18 @@ public class PacketStringEnumeratorTests
public void EnumeratorDoesNotAllowOvereachingPacketEnd()
{
var stringEnumerator = new PacketStringEnumerator("in 1 2 3 4");
- var tokenResult = stringEnumerator.GetNextToken();
+ var tokenResult = stringEnumerator.GetNextToken(out _);
Assert.True(tokenResult.IsSuccess); // in
- tokenResult = stringEnumerator.GetNextToken();
+ tokenResult = stringEnumerator.GetNextToken(out _);
Assert.True(tokenResult.IsSuccess); // 1
- tokenResult = stringEnumerator.GetNextToken();
+ tokenResult = stringEnumerator.GetNextToken(out _);
Assert.True(tokenResult.IsSuccess); // 2
- tokenResult = stringEnumerator.GetNextToken();
+ tokenResult = stringEnumerator.GetNextToken(out _);
Assert.True(tokenResult.IsSuccess); // 3
- tokenResult = stringEnumerator.GetNextToken();
+ tokenResult = stringEnumerator.GetNextToken(out _);
Assert.True(tokenResult.IsSuccess); // 4
- tokenResult = stringEnumerator.GetNextToken();
+ tokenResult = stringEnumerator.GetNextToken(out _);
Assert.False(tokenResult.IsSuccess);
Assert.IsType<PacketEndReachedError>(tokenResult.Error);
}
@@ 114,20 115,20 @@ public class PacketStringEnumeratorTests
public void EnumeratorDoesNotAllowOvereachingListComplexTypeEnd()
{
var stringEnumerator = new PacketStringEnumerator("in 1|2.2|3.3|4.4|5");
- var tokenResult = stringEnumerator.GetNextToken();
+ var tokenResult = stringEnumerator.GetNextToken(out _);
Assert.True(tokenResult.IsSuccess); // in
- var listEnumerator = stringEnumerator.CreateLevel('.');
- var itemEnumerator = listEnumerator.CreateLevel('|');
+ stringEnumerator.PushLevel('.');
+ stringEnumerator.PushLevel('|');
- tokenResult = itemEnumerator.GetNextToken();
+ tokenResult = stringEnumerator.GetNextToken(out _);
Assert.True(tokenResult.IsSuccess); // 1
- tokenResult = itemEnumerator.GetNextToken();
+ tokenResult = stringEnumerator.GetNextToken(out var packetToken);
Assert.True(tokenResult.IsSuccess); // 2
- Assert.True(tokenResult.Entity.IsLast);
+ Assert.True(packetToken.IsLast);
- tokenResult = itemEnumerator.GetNextToken();
+ tokenResult = stringEnumerator.GetNextToken(out _);
Assert.False(tokenResult.IsSuccess);
Assert.IsType<PacketEndReachedError>(tokenResult.Error);
Assert.True(((PacketEndReachedError)tokenResult.Error!).LevelEnd);
@@ 140,33 141,37 @@ public class PacketStringEnumeratorTests
public void EnumeratorDoesNotAllowOvereachingListLength()
{
var stringEnumerator = new PacketStringEnumerator("in 1|2.2|3.3|4.4|5");
- var tokenResult = stringEnumerator.GetNextToken();
+ var tokenResult = stringEnumerator.GetNextToken(out _);
Assert.True(tokenResult.IsSuccess); // in
- var listEnumerator = stringEnumerator.CreateLevel('.', 2);
- var itemEnumerator = listEnumerator.CreateLevel('|');
+ stringEnumerator.PushLevel('.', 2);
+ stringEnumerator.PushLevel('|');
// first item
- tokenResult = itemEnumerator.GetNextToken();
+ tokenResult = stringEnumerator.GetNextToken(out _);
Assert.True(tokenResult.IsSuccess);
- tokenResult = itemEnumerator.GetNextToken();
+ tokenResult = stringEnumerator.GetNextToken(out var packetToken);
Assert.True(tokenResult.IsSuccess);
- Assert.True(tokenResult.Entity.IsLast);
+ Assert.True(packetToken.IsLast);
+
+ stringEnumerator.PopLevel();
// second item
- itemEnumerator = listEnumerator.CreateLevel('|');
- tokenResult = itemEnumerator.GetNextToken();
+ stringEnumerator.PushLevel('|');
+ tokenResult = stringEnumerator.GetNextToken(out _);
Assert.True(tokenResult.IsSuccess);
- tokenResult = itemEnumerator.GetNextToken();
+ stringEnumerator.GetNextToken(out packetToken);
Assert.True(tokenResult.IsSuccess);
- Assert.True(tokenResult.Entity.IsLast);
+ Assert.True(packetToken.IsLast);
+
+ stringEnumerator.PopLevel();
// cannot reach third item
- Assert.True(listEnumerator.IsOnLastToken());
- itemEnumerator = listEnumerator.CreateLevel('|');
- tokenResult = itemEnumerator.GetNextToken();
+ Assert.True(stringEnumerator.IsOnLastToken());
+ stringEnumerator.PushLevel('|');
+ tokenResult = stringEnumerator.GetNextToken(out _);
Assert.False(tokenResult.IsSuccess);
Assert.IsType<PacketEndReachedError>(tokenResult.Error);
Assert.True(((PacketEndReachedError)tokenResult.Error!).LevelEnd);
@@ 179,18 184,18 @@ public class PacketStringEnumeratorTests
public void EnumeratorReturnsEncounteredUpperLevel()
{
var stringEnumerator = new PacketStringEnumerator("in 1|2 1");
- var tokenResult = stringEnumerator.GetNextToken();
+ var tokenResult = stringEnumerator.GetNextToken(out _);
Assert.True(tokenResult.IsSuccess); // in
- var listEnumerator = stringEnumerator.CreateLevel('.');
- var itemEnumerator = listEnumerator.CreateLevel('|');
+ stringEnumerator.PushLevel('.');
+ stringEnumerator.PushLevel('|');
- tokenResult = itemEnumerator.GetNextToken();
+ tokenResult = stringEnumerator.GetNextToken(out _);
Assert.True(tokenResult.IsSuccess);
- tokenResult = itemEnumerator.GetNextToken();
+ tokenResult = stringEnumerator.GetNextToken(out var packetToken);
Assert.True(tokenResult.IsSuccess);
- Assert.True(tokenResult.Entity.IsLast);
- Assert.True(tokenResult.Entity.EncounteredUpperLevel);
+ Assert.True(packetToken.IsLast);
+ Assert.True(packetToken.EncounteredUpperLevel);
}
}=
\ No newline at end of file