mirror of https://github.com/Xpl0itR/protodec.git
Add an option to skip parsing properties that aren't decorated with the protoc attribute
also update to dotnet 8 and update the project folder structure
This commit is contained in:
parent
b1eed86d6c
commit
e41df439ea
|
@ -11,7 +11,8 @@ Arguments:
|
||||||
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.
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
Limitations
|
Limitations
|
||||||
|
|
|
@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.6.33513.286
|
VisualStudioVersion = 17.6.33513.286
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "protodec", "protodec\protodec.csproj", "{A5493BF4-F78C-4DCF-B449-D9A9A52FB5F0}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "protodec", "src\protodec\protodec.csproj", "{A5493BF4-F78C-4DCF-B449-D9A9A52FB5F0}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibProtodec", "LibProtodec\LibProtodec.csproj", "{5F6DAD82-D9AD-4CE5-86E6-D20C9F059A4D}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibProtodec", "src\LibProtodec\LibProtodec.csproj", "{5F6DAD82-D9AD-4CE5-86E6-D20C9F059A4D}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace LibProtodec;
|
||||||
|
|
||||||
public sealed class Enum : Protobuf
|
public sealed class Enum : Protobuf
|
||||||
{
|
{
|
||||||
public readonly List<KeyValuePair<int, string>> Fields = new();
|
public readonly List<KeyValuePair<int, string>> Fields = [];
|
||||||
|
|
||||||
public override void WriteFileTo(IndentedTextWriter writer)
|
public override void WriteFileTo(IndentedTextWriter writer)
|
||||||
{
|
{
|
||||||
|
@ -22,7 +22,7 @@ public sealed class Enum : Protobuf
|
||||||
|
|
||||||
if (Fields.ContainsDuplicateKey())
|
if (Fields.ContainsDuplicateKey())
|
||||||
{
|
{
|
||||||
writer.WriteLine("""option allow_alias = true;""");
|
writer.WriteLine("option allow_alias = true;");
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ((int id, string name) in Fields)
|
foreach ((int id, string name) in Fields)
|
|
@ -2,24 +2,25 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Authors>Xpl0itR</Authors>
|
<Authors>Xpl0itR</Authors>
|
||||||
|
<BaseOutputPath>$(SolutionDir)bin/$(MSBuildProjectName)</BaseOutputPath>
|
||||||
<Copyright>Copyright © 2023 Xpl0itR</Copyright>
|
<Copyright>Copyright © 2023 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 parser/serializer classes compiled by protoc, from dotnet assemblies back into .proto definitions</Description>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<LangVersion>11</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<PackageLicenseExpression>MPL-2.0</PackageLicenseExpression>
|
<PackageLicenseExpression>MPL-2.0</PackageLicenseExpression>
|
||||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.2.2" />
|
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.2.2" />
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
|
||||||
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="7.0.0" />
|
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
|
@ -7,10 +7,10 @@ namespace LibProtodec;
|
||||||
|
|
||||||
public sealed class Message : Protobuf
|
public sealed class Message : Protobuf
|
||||||
{
|
{
|
||||||
public readonly HashSet<string> Imports = new();
|
public readonly HashSet<string> Imports = [];
|
||||||
public readonly Dictionary<string, int[]> OneOfs = new();
|
public readonly Dictionary<string, int[]> OneOfs = [];
|
||||||
public readonly Dictionary<int, (bool IsOptional, string Type, string Name)> Fields = new();
|
public readonly Dictionary<int, (bool IsOptional, string Type, string Name)> Fields = [];
|
||||||
public readonly Dictionary<string, Protobuf> Nested = new();
|
public readonly Dictionary<string, Protobuf> Nested = [];
|
||||||
|
|
||||||
public override void WriteFileTo(IndentedTextWriter writer)
|
public override void WriteFileTo(IndentedTextWriter writer)
|
||||||
{
|
{
|
|
@ -12,14 +12,8 @@ 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);
|
||||||
|
|
||||||
private readonly Dictionary<string, Protobuf> _protobufs;
|
private readonly Dictionary<string, Protobuf> _protobufs = [];
|
||||||
private readonly HashSet<string> _currentDescent;
|
private readonly HashSet<string> _currentDescent = [];
|
||||||
|
|
||||||
public Protodec()
|
|
||||||
{
|
|
||||||
_protobufs = new Dictionary<string, Protobuf>();
|
|
||||||
_currentDescent = new HashSet<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public LookupFunc? CustomTypeLookup { get; init; }
|
public LookupFunc? CustomTypeLookup { get; init; }
|
||||||
|
|
||||||
|
@ -40,11 +34,11 @@ public sealed class Protodec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ParseMessage(Type type, bool skipEnums = false)
|
public void ParseMessage(Type type, bool skipEnums = false, bool skipPropertiesWithoutProtocAttribute = false)
|
||||||
{
|
{
|
||||||
Guard.IsTrue(type.IsClass);
|
Guard.IsTrue(type.IsClass);
|
||||||
|
|
||||||
ParseMessageInternal(type, skipEnums, null);
|
ParseMessageInternal(type, skipEnums, skipPropertiesWithoutProtocAttribute, null);
|
||||||
_currentDescent.Clear();
|
_currentDescent.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +60,7 @@ public sealed class Protodec
|
||||||
|| !_currentDescent.Add(type.Name);
|
|| !_currentDescent.Add(type.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ParseMessageInternal(Type messageClass, bool skipEnums, Message? parentMessage)
|
private void ParseMessageInternal(Type messageClass, bool skipEnums, bool skipPropertiesWithoutProtocAttribute, Message? parentMessage)
|
||||||
{
|
{
|
||||||
if (IsParsed(messageClass, parentMessage, out Dictionary<string, Protobuf> protobufs))
|
if (IsParsed(messageClass, parentMessage, out Dictionary<string, Protobuf> protobufs))
|
||||||
{
|
{
|
||||||
|
@ -87,7 +81,9 @@ public sealed class Protodec
|
||||||
{
|
{
|
||||||
PropertyInfo property = properties[pi];
|
PropertyInfo property = properties[pi];
|
||||||
|
|
||||||
if (property.GetMethod is null || property.GetMethod.IsVirtual)
|
if (property.GetMethod is null
|
||||||
|
|| property.GetMethod.IsVirtual
|
||||||
|
|| (skipPropertiesWithoutProtocAttribute && !HasProtocAttribute(property)))
|
||||||
{
|
{
|
||||||
fi--;
|
fi--;
|
||||||
continue;
|
continue;
|
||||||
|
@ -116,7 +112,7 @@ public sealed class Protodec
|
||||||
|
|
||||||
int msgFieldId = (int)idField.GetRawConstantValue()!;
|
int msgFieldId = (int)idField.GetRawConstantValue()!;
|
||||||
bool msgFieldIsOptional = false;
|
bool msgFieldIsOptional = false;
|
||||||
string msgFieldType = ParseFieldType(propertyType, skipEnums, message);
|
string msgFieldType = ParseFieldType(propertyType, skipEnums, skipPropertiesWithoutProtocAttribute, message);
|
||||||
string msgFieldName = TranslateMessageFieldName(property.Name);
|
string msgFieldName = TranslateMessageFieldName(property.Name);
|
||||||
|
|
||||||
// optional protobuf fields will generate an additional "Has" get-only boolean property immediately after the real property
|
// optional protobuf fields will generate an additional "Has" get-only boolean property immediately after the real property
|
||||||
|
@ -157,7 +153,7 @@ public sealed class Protodec
|
||||||
protobufs.Add(@enum.Name, @enum);
|
protobufs.Add(@enum.Name, @enum);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ParseFieldType(Type type, bool skipEnums, Message message)
|
private string ParseFieldType(Type type, bool skipEnums, bool skipPropertiesWithoutProtocAttribute, Message message)
|
||||||
{
|
{
|
||||||
switch (type.Name)
|
switch (type.Name)
|
||||||
{
|
{
|
||||||
|
@ -184,11 +180,11 @@ public sealed class Protodec
|
||||||
switch (type.GenericTypeArguments.Length)
|
switch (type.GenericTypeArguments.Length)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
string t = ParseFieldType(type.GenericTypeArguments[0], skipEnums, message);
|
string t = ParseFieldType(type.GenericTypeArguments[0], skipEnums, skipPropertiesWithoutProtocAttribute, message);
|
||||||
return "repeated " + t;
|
return "repeated " + t;
|
||||||
case 2:
|
case 2:
|
||||||
string t1 = ParseFieldType(type.GenericTypeArguments[0], skipEnums, message);
|
string t1 = ParseFieldType(type.GenericTypeArguments[0], skipEnums, skipPropertiesWithoutProtocAttribute, message);
|
||||||
string t2 = ParseFieldType(type.GenericTypeArguments[1], skipEnums, message);
|
string t2 = ParseFieldType(type.GenericTypeArguments[1], skipEnums, skipPropertiesWithoutProtocAttribute, message);
|
||||||
return $"map<{t1}, {t2}>";
|
return $"map<{t1}, {t2}>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +204,7 @@ public sealed class Protodec
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ParseMessageInternal(type, skipEnums, message);
|
ParseMessageInternal(type, skipEnums, skipPropertiesWithoutProtocAttribute, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!type.IsNested)
|
if (!type.IsNested)
|
||||||
|
@ -219,6 +215,12 @@ 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
|
|
@ -12,7 +12,8 @@ const string help = """
|
||||||
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.
|
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
|
||||||
""";
|
""";
|
||||||
|
|
||||||
if (args.Length < 2)
|
if (args.Length < 2)
|
||||||
|
@ -30,13 +31,14 @@ if (args.Length > 2 && !args[2].StartsWith('-'))
|
||||||
string assemblyDir = args[0];
|
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");
|
||||||
|
|
||||||
using AssemblyInspector inspector = new(assemblyDir, assemblyName);
|
using AssemblyInspector inspector = new(assemblyDir, assemblyName);
|
||||||
Protodec protodec = new();
|
Protodec protodec = new();
|
||||||
|
|
||||||
foreach (Type message in inspector.GetProtobufMessageTypes())
|
foreach (Type message in inspector.GetProtobufMessageTypes())
|
||||||
{
|
{
|
||||||
protodec.ParseMessage(message, skipEnums);
|
protodec.ParseMessage(message, skipEnums, skipPropertiesWithoutProtocAttribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Directory.Exists(outPath))
|
if (Directory.Exists(outPath))
|
|
@ -1,11 +1,12 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<LangVersion>11</LangVersion>
|
<BaseOutputPath>$(SolutionDir)bin/$(MSBuildProjectName)</BaseOutputPath>
|
||||||
|
<LangVersion>Latest</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
Loading…
Reference in New Issue