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
|
||||
========
|
||||
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: protodec(.exe) <target_assembly_dir> <out_path> [target_assembly_name] [options]
|
||||
Usage: protodec(.exe) <target_assembly_path> <out_path> [options]
|
||||
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.
|
||||
target_assembly_name The name of an assembly to parse. If omitted, all assemblies in the target_assembly_dir will be parsed.
|
||||
Options:
|
||||
--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
|
||||
|
@ -17,13 +16,9 @@ Options:
|
|||
|
||||
Limitations
|
||||
-----------
|
||||
- Integers are assumed to be (u)int32/64 as C# doesn't differentiate between them and sint32/64 and (s)fixed32/64.
|
||||
### 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
|
||||
- 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.
|
||||
- Integers are assumed to be (u)int32/64 as CIL doesn't differentiate between them and sint32/64 and (s)fixed32/64.
|
||||
- When decompiling from [Il2CppDumper](https://github.com/Perfare/Il2CppDumper) DummyDLLs
|
||||
- The `Name` parameter of `OriginalNameAttribute` is not dumped. In this case, the CIL enum field names are used after conforming them to protobuf conventions
|
||||
|
||||
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.IO;
|
||||
using System.Linq;
|
||||
|
@ -11,33 +17,54 @@ public sealed class AssemblyInspector : IDisposable
|
|||
public readonly MetadataLoadContext AssemblyContext;
|
||||
public readonly IReadOnlyList<Type> LoadedTypes;
|
||||
|
||||
public AssemblyInspector(string assemblyDir, string? assemblyName = null)
|
||||
public AssemblyInspector(string assemblyPath)
|
||||
{
|
||||
string[] assemblyPaths = Directory.EnumerateFiles(assemblyDir, searchPattern: "*.dll")
|
||||
.ToArray();
|
||||
bool isFile = File.Exists(assemblyPath);
|
||||
string assemblyDir = isFile
|
||||
? Path.GetDirectoryName(assemblyPath)!
|
||||
: assemblyPath;
|
||||
|
||||
AssemblyContext = new MetadataLoadContext(
|
||||
new PathAssemblyResolver(assemblyPaths));
|
||||
PermissiveAssemblyResolver assemblyResolver = new(
|
||||
Directory.EnumerateFiles(assemblyDir, searchPattern: "*.dll"));
|
||||
|
||||
LoadedTypes = assemblyName is null
|
||||
? assemblyPaths.SelectMany(path => AssemblyContext.LoadFromAssemblyPath(path).GetTypes()).ToList()
|
||||
: AssemblyContext.LoadFromAssemblyName(assemblyName).GetTypes();
|
||||
AssemblyContext = new MetadataLoadContext(assemblyResolver);
|
||||
LoadedTypes = isFile
|
||||
? AssemblyContext.LoadFromAssemblyPath(assemblyPath).GetTypes()
|
||||
: assemblyResolver.AssemblyPathLookup.Values.SelectMany(path => AssemblyContext.LoadFromAssemblyPath(path).GetTypes()).ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<Type> GetProtobufMessageTypes()
|
||||
{
|
||||
Type googleProtobufIMessage = AssemblyContext.LoadFromAssemblyName("Google.Protobuf")
|
||||
.GetType("Google.Protobuf.IMessage")!;
|
||||
Type? googleProtobufIMessage = AssemblyContext.LoadFromAssemblyName("Google.Protobuf")
|
||||
.GetType("Google.Protobuf.IMessage");
|
||||
return from type
|
||||
in LoadedTypes
|
||||
where !type.IsNested
|
||||
&& type.IsSealed
|
||||
&& type.Namespace != "Google.Protobuf.Reflection"
|
||||
&& type.Namespace != "Google.Protobuf.WellKnownTypes"
|
||||
&& type.Namespace?.StartsWith("Google.Protobuf", StringComparison.Ordinal) != true
|
||||
&& type.IsAssignableTo(googleProtobufIMessage)
|
||||
select type;
|
||||
}
|
||||
|
||||
public void 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 SystemEx.Collections;
|
||||
|
||||
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>
|
||||
<Authors>Xpl0itR</Authors>
|
||||
<BaseOutputPath>$(SolutionDir)bin/$(MSBuildProjectName)</BaseOutputPath>
|
||||
<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>
|
||||
<Copyright>Copyright © 2023-2024 Xpl0itR</Copyright>
|
||||
<Description>A library to decompile protobuf classes compiled by protoc, from CIL assemblies back into .proto definitions</Description>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<IsPackable>true</IsPackable>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
|
@ -18,9 +18,9 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.2.2" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="8.0.0" />
|
||||
<PackageReference Include="Xpl0itR.SystemEx" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</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.IO;
|
||||
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;
|
||||
|
||||
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.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using SystemEx;
|
||||
using SystemEx.Collections;
|
||||
using CommunityToolkit.Diagnostics;
|
||||
|
||||
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 HashSet<string> _currentDescent = [];
|
||||
|
||||
|
@ -81,9 +89,8 @@ public sealed class Protodec
|
|||
{
|
||||
PropertyInfo property = properties[pi];
|
||||
|
||||
if (property.GetMethod is null
|
||||
|| property.GetMethod.IsVirtual
|
||||
|| (skipPropertiesWithoutProtocAttribute && !HasProtocAttribute(property)))
|
||||
if ((skipPropertiesWithoutProtocAttribute && !HasProtocAttribute(property))
|
||||
|| property.GetMethod?.IsVirtual != false)
|
||||
{
|
||||
fi--;
|
||||
continue;
|
||||
|
@ -92,7 +99,8 @@ public sealed class Protodec
|
|||
Type propertyType = property.PropertyType;
|
||||
|
||||
// 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);
|
||||
int[] oneOfProtoFieldIds = propertyType.GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||
|
@ -158,23 +166,23 @@ public sealed class Protodec
|
|||
switch (type.Name)
|
||||
{
|
||||
case "ByteString":
|
||||
return "bytes";
|
||||
return FieldTypeName.Bytes;
|
||||
case nameof(String):
|
||||
return "string";
|
||||
return FieldTypeName.String;
|
||||
case nameof(Boolean):
|
||||
return "bool";
|
||||
return FieldTypeName.Bool;
|
||||
case nameof(Double):
|
||||
return "double";
|
||||
return FieldTypeName.Double;
|
||||
case nameof(UInt32):
|
||||
return "uint32";
|
||||
return FieldTypeName.UInt32;
|
||||
case nameof(UInt64):
|
||||
return "uint64";
|
||||
return FieldTypeName.UInt64;
|
||||
case nameof(Int32):
|
||||
return "int32";
|
||||
return FieldTypeName.Int32;
|
||||
case nameof(Int64):
|
||||
return "int64";
|
||||
return FieldTypeName.Int64;
|
||||
case nameof(Single):
|
||||
return "float";
|
||||
return FieldTypeName.Float;
|
||||
}
|
||||
|
||||
switch (type.GenericTypeArguments.Length)
|
||||
|
@ -197,7 +205,7 @@ public sealed class Protodec
|
|||
{
|
||||
if (skipEnums)
|
||||
{
|
||||
return "int32";
|
||||
return FieldTypeName.Int32;
|
||||
}
|
||||
|
||||
ParseEnumInternal(type, message);
|
||||
|
@ -215,12 +223,6 @@ public sealed class Protodec
|
|||
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) =>
|
||||
CustomNameLookup?.Invoke(name, out string? translatedName) == true
|
||||
? translatedName
|
||||
|
@ -253,7 +255,7 @@ public sealed class Protodec
|
|||
return translatedName;
|
||||
}
|
||||
|
||||
if (!enumName.IsBeebyted())
|
||||
if (!IsBeebyted(enumName))
|
||||
{
|
||||
enumName = enumName.ToSnakeCaseUpper();
|
||||
}
|
||||
|
@ -269,6 +271,15 @@ public sealed class Protodec
|
|||
}
|
||||
|
||||
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 help = """
|
||||
Usage: protodec(.exe) <target_assembly_dir> <out_path> [target_assembly_name] [options]
|
||||
Usage: protodec(.exe) <target_assembly_path> <out_path> [options]
|
||||
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.
|
||||
target_assembly_name The name of an assembly to parse. If omitted, all assemblies in the target_assembly_dir will be parsed.
|
||||
Options:
|
||||
--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
|
||||
|
@ -22,28 +21,22 @@ if (args.Length < 2)
|
|||
return;
|
||||
}
|
||||
|
||||
string? assemblyName = null;
|
||||
if (args.Length > 2 && !args[2].StartsWith('-'))
|
||||
{
|
||||
assemblyName = args[2];
|
||||
}
|
||||
|
||||
string assemblyDir = args[0];
|
||||
string assemblyPath = args[0];
|
||||
string outPath = Path.GetFullPath(args[1]);
|
||||
bool skipEnums = args.Contains("--skip_enums");
|
||||
bool skipPropertiesWithoutProtocAttribute = args.Contains("--skip_properties_without_protoc_attribute");
|
||||
|
||||
using AssemblyInspector inspector = new(assemblyDir, assemblyName);
|
||||
Protodec protodec = new();
|
||||
using AssemblyInspector inspector = new(assemblyPath);
|
||||
ProtodecContext ctx = new();
|
||||
|
||||
foreach (Type message in inspector.GetProtobufMessageTypes())
|
||||
{
|
||||
protodec.ParseMessage(message, skipEnums, skipPropertiesWithoutProtocAttribute);
|
||||
ctx.ParseMessage(message, skipEnums, skipPropertiesWithoutProtocAttribute);
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
|
@ -58,5 +51,5 @@ else
|
|||
using StreamWriter streamWriter = new(outPath);
|
||||
using IndentedTextWriter indentWriter = new(streamWriter, indent);
|
||||
|
||||
protodec.WriteAllTo(indentWriter);
|
||||
ctx.WriteAllTo(indentWriter);
|
||||
}
|
|
@ -5,12 +5,13 @@
|
|||
<LangVersion>Latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PublishAot>true</PublishAot>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LibProtodec\LibProtodec.csproj" />
|
||||
<ProjectReference Include="$(SolutionDir)src\LibProtodec\LibProtodec.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Loading…
Reference in New Issue