mirror of https://github.com/Xpl0itR/protodec.git
More improvements
including but not limited to: - Improve assembly loading - Enable AoT publish - Add github action for building releases
This commit is contained in:
parent
e41df439ea
commit
c61334d291
|
@ -0,0 +1,41 @@
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- published
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- runtime: 'linux-x64'
|
||||||
|
os: 'ubuntu-latest'
|
||||||
|
- runtime: 'win-x64'
|
||||||
|
os: 'windows-latest'
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Git checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup dotnet
|
||||||
|
uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
dotnet-version: '8.0.x'
|
||||||
|
|
||||||
|
- name: Build protodec
|
||||||
|
run: dotnet publish --configuration Release --runtime ${{ matrix.runtime }} /p:VersionPrefix=${{ github.ref_name }} /p:ContinuousIntegrationBuild=true
|
||||||
|
|
||||||
|
- name: Pack LibProtodec
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
run: dotnet pack --configuration Release /p:VersionPrefix=${{ github.ref_name }} /p:ContinuousIntegrationBuild=true
|
||||||
|
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
bin/protodec/Release/net8.0/${{ matrix.runtime }}/publish/protodec*
|
||||||
|
bin/LibProtodec/Release/*nupkg
|
17
README.md
17
README.md
|
@ -1,15 +1,14 @@
|
||||||
protodec
|
protodec
|
||||||
========
|
========
|
||||||
A tool to decompile protobuf parser/serializer classes compiled by [protoc](https://github.com/protocolbuffers/protobuf), from dotnet assemblies back into .proto definitions.
|
A tool to decompile protobuf classes compiled by [protoc](https://github.com/protocolbuffers/protobuf), from CIL assemblies back into .proto definitions.
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
```
|
```
|
||||||
Usage: protodec(.exe) <target_assembly_dir> <out_path> [target_assembly_name] [options]
|
Usage: protodec(.exe) <target_assembly_path> <out_path> [options]
|
||||||
Arguments:
|
Arguments:
|
||||||
target_assembly_dir A directory of assemblies to be loaded.
|
target_assembly_path Either the path to the target assembly or a directory of assemblies, all of which be parsed.
|
||||||
out_path An existing directory to output into individual files, otherwise output to a single file.
|
out_path An existing directory to output into individual files, otherwise output to a single file.
|
||||||
target_assembly_name The name of an assembly to parse. If omitted, all assemblies in the target_assembly_dir will be parsed.
|
|
||||||
Options:
|
Options:
|
||||||
--skip_enums Skip parsing enums and replace references to them with int32.
|
--skip_enums Skip parsing enums and replace references to them with int32.
|
||||||
--skip_properties_without_protoc_attribute Skip properties that aren't decorated with `GeneratedCode("protoc")` when parsing
|
--skip_properties_without_protoc_attribute Skip properties that aren't decorated with `GeneratedCode("protoc")` when parsing
|
||||||
|
@ -17,13 +16,9 @@ Options:
|
||||||
|
|
||||||
Limitations
|
Limitations
|
||||||
-----------
|
-----------
|
||||||
- Integers are assumed to be (u)int32/64 as C# doesn't differentiate between them and sint32/64 and (s)fixed32/64.
|
- Integers are assumed to be (u)int32/64 as CIL doesn't differentiate between them and sint32/64 and (s)fixed32/64.
|
||||||
### Decompiling from [Il2CppDumper](https://github.com/Perfare/Il2CppDumper) DummyDLLs
|
- When decompiling from [Il2CppDumper](https://github.com/Perfare/Il2CppDumper) DummyDLLs
|
||||||
- The `Name` parameter of `OriginalNameAttribute` is not dumped. In this case the C# names are used after conforming them to protobuf conventions
|
- The `Name` parameter of `OriginalNameAttribute` is not dumped. In this case, the CIL enum field names are used after conforming them to protobuf conventions
|
||||||
- Dumped assemblies depend on strong-named core libs, however the ones dumped are not strong-named.
|
|
||||||
This interferes with loading and can be mitigated by copying the assemblies from your runtime into the target assembly directory.
|
|
||||||
|
|
||||||
I recommend using [Cpp2IL](https://github.com/SamboyCoding/Cpp2IL) instead of Il2CppDumper.
|
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
using System;
|
// Copyright © 2023-2024 Xpl0itR
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -11,33 +17,54 @@ public sealed class AssemblyInspector : IDisposable
|
||||||
public readonly MetadataLoadContext AssemblyContext;
|
public readonly MetadataLoadContext AssemblyContext;
|
||||||
public readonly IReadOnlyList<Type> LoadedTypes;
|
public readonly IReadOnlyList<Type> LoadedTypes;
|
||||||
|
|
||||||
public AssemblyInspector(string assemblyDir, string? assemblyName = null)
|
public AssemblyInspector(string assemblyPath)
|
||||||
{
|
{
|
||||||
string[] assemblyPaths = Directory.EnumerateFiles(assemblyDir, searchPattern: "*.dll")
|
bool isFile = File.Exists(assemblyPath);
|
||||||
.ToArray();
|
string assemblyDir = isFile
|
||||||
|
? Path.GetDirectoryName(assemblyPath)!
|
||||||
|
: assemblyPath;
|
||||||
|
|
||||||
AssemblyContext = new MetadataLoadContext(
|
PermissiveAssemblyResolver assemblyResolver = new(
|
||||||
new PathAssemblyResolver(assemblyPaths));
|
Directory.EnumerateFiles(assemblyDir, searchPattern: "*.dll"));
|
||||||
|
|
||||||
LoadedTypes = assemblyName is null
|
AssemblyContext = new MetadataLoadContext(assemblyResolver);
|
||||||
? assemblyPaths.SelectMany(path => AssemblyContext.LoadFromAssemblyPath(path).GetTypes()).ToList()
|
LoadedTypes = isFile
|
||||||
: AssemblyContext.LoadFromAssemblyName(assemblyName).GetTypes();
|
? AssemblyContext.LoadFromAssemblyPath(assemblyPath).GetTypes()
|
||||||
|
: assemblyResolver.AssemblyPathLookup.Values.SelectMany(path => AssemblyContext.LoadFromAssemblyPath(path).GetTypes()).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Type> GetProtobufMessageTypes()
|
public IEnumerable<Type> GetProtobufMessageTypes()
|
||||||
{
|
{
|
||||||
Type googleProtobufIMessage = AssemblyContext.LoadFromAssemblyName("Google.Protobuf")
|
Type? googleProtobufIMessage = AssemblyContext.LoadFromAssemblyName("Google.Protobuf")
|
||||||
.GetType("Google.Protobuf.IMessage")!;
|
.GetType("Google.Protobuf.IMessage");
|
||||||
return from type
|
return from type
|
||||||
in LoadedTypes
|
in LoadedTypes
|
||||||
where !type.IsNested
|
where !type.IsNested
|
||||||
&& type.IsSealed
|
&& type.IsSealed
|
||||||
&& type.Namespace != "Google.Protobuf.Reflection"
|
&& type.Namespace?.StartsWith("Google.Protobuf", StringComparison.Ordinal) != true
|
||||||
&& type.Namespace != "Google.Protobuf.WellKnownTypes"
|
|
||||||
&& type.IsAssignableTo(googleProtobufIMessage)
|
&& type.IsAssignableTo(googleProtobufIMessage)
|
||||||
select type;
|
select type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() =>
|
public void Dispose() =>
|
||||||
AssemblyContext.Dispose();
|
AssemblyContext.Dispose();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An assembly resolver that uses paths to every assembly that may be loaded.
|
||||||
|
/// The file name is expected to be the same as the assembly's simple name (casing ignored).
|
||||||
|
/// PublicKeyToken, Version and CultureName are ignored.
|
||||||
|
/// </summary>
|
||||||
|
private sealed class PermissiveAssemblyResolver(IEnumerable<string> assemblyPaths) : MetadataAssemblyResolver
|
||||||
|
{
|
||||||
|
public readonly IReadOnlyDictionary<string, string> AssemblyPathLookup =
|
||||||
|
assemblyPaths.ToDictionary(
|
||||||
|
path => Path.GetFileNameWithoutExtension(path),
|
||||||
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Assembly? Resolve(MetadataLoadContext mlc, AssemblyName assemblyName) =>
|
||||||
|
AssemblyPathLookup.TryGetValue(assemblyName.Name!, out string? assemblyPath)
|
||||||
|
? mlc.LoadFromAssemblyPath(assemblyPath)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,12 @@
|
||||||
using System.CodeDom.Compiler;
|
// Copyright © 2023-2024 Xpl0itR
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using SystemEx.Collections;
|
||||||
|
|
||||||
namespace LibProtodec;
|
namespace LibProtodec;
|
||||||
|
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace LibProtodec;
|
|
||||||
|
|
||||||
public static class Extensions
|
|
||||||
{
|
|
||||||
public static void Add<TKey, TValue>(this ICollection<KeyValuePair<TKey, TValue>> keyValuePairs, TKey key, TValue value) =>
|
|
||||||
keyValuePairs.Add(new KeyValuePair<TKey, TValue>(key, value));
|
|
||||||
|
|
||||||
public static bool ContainsDuplicateKey<TKey, TValue>(
|
|
||||||
this IEnumerable<KeyValuePair<TKey, TValue>> keyValuePairs,
|
|
||||||
IEqualityComparer<TKey>? comparer = null)
|
|
||||||
{
|
|
||||||
HashSet<TKey> set = new(5, comparer);
|
|
||||||
|
|
||||||
foreach (KeyValuePair<TKey, TValue> kvp in keyValuePairs)
|
|
||||||
{
|
|
||||||
if (!set.Add(kvp.Key))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string TrimEnd(this string @string, string trimStr) =>
|
|
||||||
@string.EndsWith(trimStr, StringComparison.Ordinal)
|
|
||||||
? @string[..^trimStr.Length]
|
|
||||||
: @string;
|
|
||||||
|
|
||||||
public static string ToSnakeCaseLower(this string str) =>
|
|
||||||
string.Create(str.Length + CountUpper(str, 1), str, (newString, oldString) =>
|
|
||||||
{
|
|
||||||
newString[0] = char.ToLowerInvariant(oldString[0]);
|
|
||||||
|
|
||||||
char chr;
|
|
||||||
for (int i = 1, j = 1; i < oldString.Length; i++, j++)
|
|
||||||
{
|
|
||||||
chr = oldString[i];
|
|
||||||
|
|
||||||
if (char.IsAsciiLetterUpper(chr))
|
|
||||||
{
|
|
||||||
newString[j++] = '_';
|
|
||||||
newString[j] = char.ToLowerInvariant(chr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newString[j] = chr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
public static string ToSnakeCaseUpper(this string str) =>
|
|
||||||
string.Create(str.Length + CountUpper(str, 1), str, (newString, oldString) =>
|
|
||||||
{
|
|
||||||
newString[0] = char.ToUpperInvariant(oldString[0]);
|
|
||||||
|
|
||||||
char chr;
|
|
||||||
for (int i = 1, j = 1; i < oldString.Length; i++, j++)
|
|
||||||
{
|
|
||||||
chr = oldString[i];
|
|
||||||
|
|
||||||
if (char.IsAsciiLetterUpper(chr))
|
|
||||||
{
|
|
||||||
newString[j++] = '_';
|
|
||||||
newString[j] = chr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newString[j] = char.ToUpperInvariant(chr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
// ReSharper disable once IdentifierTypo
|
|
||||||
public static bool IsBeebyted(this string name) =>
|
|
||||||
name.Length == 11 && CountUpper(name) == 11;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static int CountUpper(this string str, int i = 0)
|
|
||||||
{
|
|
||||||
int upper = 0;
|
|
||||||
|
|
||||||
for (; i < str.Length; i++)
|
|
||||||
if (char.IsAsciiLetterUpper(str[i]))
|
|
||||||
upper++;
|
|
||||||
|
|
||||||
return upper;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright © 2024 Xpl0itR
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
namespace LibProtodec;
|
||||||
|
|
||||||
|
public static class FieldTypeName
|
||||||
|
{
|
||||||
|
public const string Double = "double";
|
||||||
|
public const string Float = "float";
|
||||||
|
public const string Int32 = "int32";
|
||||||
|
public const string Int64 = "int64";
|
||||||
|
public const string UInt32 = "uint32";
|
||||||
|
public const string UInt64 = "uint64";
|
||||||
|
public const string SInt32 = "sint32";
|
||||||
|
public const string SInt64 = "sint64";
|
||||||
|
public const string Fixed32 = "fixed32";
|
||||||
|
public const string Fixed64 = "fixed64";
|
||||||
|
public const string SFixed32 = "sfixed32";
|
||||||
|
public const string SFixed64 = "sfixed64";
|
||||||
|
public const string Bool = "bool";
|
||||||
|
public const string String = "string";
|
||||||
|
public const string Bytes = "bytes";
|
||||||
|
}
|
|
@ -3,8 +3,8 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Authors>Xpl0itR</Authors>
|
<Authors>Xpl0itR</Authors>
|
||||||
<BaseOutputPath>$(SolutionDir)bin/$(MSBuildProjectName)</BaseOutputPath>
|
<BaseOutputPath>$(SolutionDir)bin/$(MSBuildProjectName)</BaseOutputPath>
|
||||||
<Copyright>Copyright © 2023 Xpl0itR</Copyright>
|
<Copyright>Copyright © 2023-2024 Xpl0itR</Copyright>
|
||||||
<Description>A library to decompile protobuf parser/serializer classes compiled by protoc, from dotnet assemblies back into .proto definitions</Description>
|
<Description>A library to decompile protobuf classes compiled by protoc, from CIL assemblies back into .proto definitions</Description>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<LangVersion>Latest</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
|
@ -18,9 +18,9 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.2.2" />
|
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
|
||||||
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="8.0.0" />
|
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Xpl0itR.SystemEx" Version="1.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -1,4 +1,10 @@
|
||||||
using System.CodeDom.Compiler;
|
// Copyright © 2023-2024 Xpl0itR
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
using System.CodeDom.Compiler;
|
// Copyright © 2023-2024 Xpl0itR
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace LibProtodec;
|
namespace LibProtodec;
|
||||||
|
|
|
@ -1,17 +1,25 @@
|
||||||
|
// Copyright © 2023-2024 Xpl0itR
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.CodeDom.Compiler;
|
using System.CodeDom.Compiler;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using SystemEx;
|
||||||
|
using SystemEx.Collections;
|
||||||
using CommunityToolkit.Diagnostics;
|
using CommunityToolkit.Diagnostics;
|
||||||
|
|
||||||
namespace LibProtodec;
|
namespace LibProtodec;
|
||||||
|
|
||||||
public sealed class Protodec
|
|
||||||
{
|
|
||||||
public delegate bool LookupFunc(string key, [MaybeNullWhen(false)] out string value);
|
public delegate bool LookupFunc(string key, [MaybeNullWhen(false)] out string value);
|
||||||
|
|
||||||
|
public sealed class ProtodecContext
|
||||||
|
{
|
||||||
private readonly Dictionary<string, Protobuf> _protobufs = [];
|
private readonly Dictionary<string, Protobuf> _protobufs = [];
|
||||||
private readonly HashSet<string> _currentDescent = [];
|
private readonly HashSet<string> _currentDescent = [];
|
||||||
|
|
||||||
|
@ -81,9 +89,8 @@ public sealed class Protodec
|
||||||
{
|
{
|
||||||
PropertyInfo property = properties[pi];
|
PropertyInfo property = properties[pi];
|
||||||
|
|
||||||
if (property.GetMethod is null
|
if ((skipPropertiesWithoutProtocAttribute && !HasProtocAttribute(property))
|
||||||
|| property.GetMethod.IsVirtual
|
|| property.GetMethod?.IsVirtual != false)
|
||||||
|| (skipPropertiesWithoutProtocAttribute && !HasProtocAttribute(property)))
|
|
||||||
{
|
{
|
||||||
fi--;
|
fi--;
|
||||||
continue;
|
continue;
|
||||||
|
@ -92,7 +99,8 @@ public sealed class Protodec
|
||||||
Type propertyType = property.PropertyType;
|
Type propertyType = property.PropertyType;
|
||||||
|
|
||||||
// only OneOf enums are defined nested directly in the message class
|
// only OneOf enums are defined nested directly in the message class
|
||||||
if (propertyType.IsEnum && propertyType.DeclaringType?.Name == message.Name)
|
if (propertyType.IsEnum
|
||||||
|
&& propertyType.DeclaringType?.Name == messageClass.Name)
|
||||||
{
|
{
|
||||||
string oneOfName = TranslateOneOfName(property.Name);
|
string oneOfName = TranslateOneOfName(property.Name);
|
||||||
int[] oneOfProtoFieldIds = propertyType.GetFields(BindingFlags.Public | BindingFlags.Static)
|
int[] oneOfProtoFieldIds = propertyType.GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||||
|
@ -158,23 +166,23 @@ public sealed class Protodec
|
||||||
switch (type.Name)
|
switch (type.Name)
|
||||||
{
|
{
|
||||||
case "ByteString":
|
case "ByteString":
|
||||||
return "bytes";
|
return FieldTypeName.Bytes;
|
||||||
case nameof(String):
|
case nameof(String):
|
||||||
return "string";
|
return FieldTypeName.String;
|
||||||
case nameof(Boolean):
|
case nameof(Boolean):
|
||||||
return "bool";
|
return FieldTypeName.Bool;
|
||||||
case nameof(Double):
|
case nameof(Double):
|
||||||
return "double";
|
return FieldTypeName.Double;
|
||||||
case nameof(UInt32):
|
case nameof(UInt32):
|
||||||
return "uint32";
|
return FieldTypeName.UInt32;
|
||||||
case nameof(UInt64):
|
case nameof(UInt64):
|
||||||
return "uint64";
|
return FieldTypeName.UInt64;
|
||||||
case nameof(Int32):
|
case nameof(Int32):
|
||||||
return "int32";
|
return FieldTypeName.Int32;
|
||||||
case nameof(Int64):
|
case nameof(Int64):
|
||||||
return "int64";
|
return FieldTypeName.Int64;
|
||||||
case nameof(Single):
|
case nameof(Single):
|
||||||
return "float";
|
return FieldTypeName.Float;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type.GenericTypeArguments.Length)
|
switch (type.GenericTypeArguments.Length)
|
||||||
|
@ -197,7 +205,7 @@ public sealed class Protodec
|
||||||
{
|
{
|
||||||
if (skipEnums)
|
if (skipEnums)
|
||||||
{
|
{
|
||||||
return "int32";
|
return FieldTypeName.Int32;
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseEnumInternal(type, message);
|
ParseEnumInternal(type, message);
|
||||||
|
@ -215,12 +223,6 @@ public sealed class Protodec
|
||||||
return type.Name;
|
return type.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HasProtocAttribute(PropertyInfo property) =>
|
|
||||||
property.GetCustomAttributesData()
|
|
||||||
.Any(attr =>
|
|
||||||
attr.AttributeType.Name == "GeneratedCodeAttribute"
|
|
||||||
&& attr.ConstructorArguments[0].Value as string == "protoc");
|
|
||||||
|
|
||||||
private string TranslateProtobufName(string name) =>
|
private string TranslateProtobufName(string name) =>
|
||||||
CustomNameLookup?.Invoke(name, out string? translatedName) == true
|
CustomNameLookup?.Invoke(name, out string? translatedName) == true
|
||||||
? translatedName
|
? translatedName
|
||||||
|
@ -253,7 +255,7 @@ public sealed class Protodec
|
||||||
return translatedName;
|
return translatedName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!enumName.IsBeebyted())
|
if (!IsBeebyted(enumName))
|
||||||
{
|
{
|
||||||
enumName = enumName.ToSnakeCaseUpper();
|
enumName = enumName.ToSnakeCaseUpper();
|
||||||
}
|
}
|
||||||
|
@ -269,6 +271,15 @@ public sealed class Protodec
|
||||||
}
|
}
|
||||||
|
|
||||||
translatedName = name;
|
translatedName = name;
|
||||||
return name.IsBeebyted();
|
return IsBeebyted(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once IdentifierTypo
|
||||||
|
private static bool IsBeebyted(string name) =>
|
||||||
|
name.Length == 11 && name.CountUpper() == 11;
|
||||||
|
|
||||||
|
private static bool HasProtocAttribute(MemberInfo member) =>
|
||||||
|
member.GetCustomAttributesData()
|
||||||
|
.Any(attr => attr.AttributeType.Name == nameof(GeneratedCodeAttribute)
|
||||||
|
&& attr.ConstructorArguments[0].Value as string == "protoc");
|
||||||
}
|
}
|
|
@ -6,11 +6,10 @@ using LibProtodec;
|
||||||
|
|
||||||
const string indent = " ";
|
const string indent = " ";
|
||||||
const string help = """
|
const string help = """
|
||||||
Usage: protodec(.exe) <target_assembly_dir> <out_path> [target_assembly_name] [options]
|
Usage: protodec(.exe) <target_assembly_path> <out_path> [options]
|
||||||
Arguments:
|
Arguments:
|
||||||
target_assembly_dir A directory of assemblies to be loaded.
|
target_assembly_path Either the path to the target assembly or a directory of assemblies, all of which be parsed.
|
||||||
out_path An existing directory to output into individual files, otherwise output to a single file.
|
out_path An existing directory to output into individual files, otherwise output to a single file.
|
||||||
target_assembly_name The name of an assembly to parse. If omitted, all assemblies in the target_assembly_dir will be parsed.
|
|
||||||
Options:
|
Options:
|
||||||
--skip_enums Skip parsing enums and replace references to them with int32.
|
--skip_enums Skip parsing enums and replace references to them with int32.
|
||||||
--skip_properties_without_protoc_attribute Skip properties that aren't decorated with `GeneratedCode("protoc")` when parsing
|
--skip_properties_without_protoc_attribute Skip properties that aren't decorated with `GeneratedCode("protoc")` when parsing
|
||||||
|
@ -22,28 +21,22 @@ if (args.Length < 2)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string? assemblyName = null;
|
string assemblyPath = args[0];
|
||||||
if (args.Length > 2 && !args[2].StartsWith('-'))
|
|
||||||
{
|
|
||||||
assemblyName = args[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
string assemblyDir = args[0];
|
|
||||||
string outPath = Path.GetFullPath(args[1]);
|
string outPath = Path.GetFullPath(args[1]);
|
||||||
bool skipEnums = args.Contains("--skip_enums");
|
bool skipEnums = args.Contains("--skip_enums");
|
||||||
bool skipPropertiesWithoutProtocAttribute = args.Contains("--skip_properties_without_protoc_attribute");
|
bool skipPropertiesWithoutProtocAttribute = args.Contains("--skip_properties_without_protoc_attribute");
|
||||||
|
|
||||||
using AssemblyInspector inspector = new(assemblyDir, assemblyName);
|
using AssemblyInspector inspector = new(assemblyPath);
|
||||||
Protodec protodec = new();
|
ProtodecContext ctx = new();
|
||||||
|
|
||||||
foreach (Type message in inspector.GetProtobufMessageTypes())
|
foreach (Type message in inspector.GetProtobufMessageTypes())
|
||||||
{
|
{
|
||||||
protodec.ParseMessage(message, skipEnums, skipPropertiesWithoutProtocAttribute);
|
ctx.ParseMessage(message, skipEnums, skipPropertiesWithoutProtocAttribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Directory.Exists(outPath))
|
if (Directory.Exists(outPath))
|
||||||
{
|
{
|
||||||
foreach (Protobuf proto in protodec.Protobufs.Values)
|
foreach (Protobuf proto in ctx.Protobufs.Values)
|
||||||
{
|
{
|
||||||
string protoPath = Path.Join(outPath, proto.Name + ".proto");
|
string protoPath = Path.Join(outPath, proto.Name + ".proto");
|
||||||
|
|
||||||
|
@ -58,5 +51,5 @@ else
|
||||||
using StreamWriter streamWriter = new(outPath);
|
using StreamWriter streamWriter = new(outPath);
|
||||||
using IndentedTextWriter indentWriter = new(streamWriter, indent);
|
using IndentedTextWriter indentWriter = new(streamWriter, indent);
|
||||||
|
|
||||||
protodec.WriteAllTo(indentWriter);
|
ctx.WriteAllTo(indentWriter);
|
||||||
}
|
}
|
|
@ -5,12 +5,13 @@
|
||||||
<LangVersion>Latest</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
|
<PublishAot>true</PublishAot>
|
||||||
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\LibProtodec\LibProtodec.csproj" />
|
<ProjectReference Include="$(SolutionDir)src\LibProtodec\LibProtodec.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
Loading…
Reference in New Issue