Files
PURErat-crack-scode/decompiled/purerat decompiled/PureCrack.Wire/ProtoNet.cs
T

378 lines
8.3 KiB
C#
Raw Normal View History

2026-08-27 10:56:38 -06:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PureCrack.Wire;
public static class ProtoNet
{
private const int MaxDumpDepth = 8;
public static byte[] WriteVarint(ulong n)
{
Span<byte> span = stackalloc byte[10];
int length = 0;
while (n > 127)
{
span[length++] = (byte)((n & 0x7F) | 0x80);
n >>= 7;
}
span[length++] = (byte)n;
return span.Slice(0, length).ToArray();
}
public static byte[] WriteTag(int field, ProtoWire wire)
{
return WriteVarint((ulong)((long)field << 3) | (ulong)wire);
}
public static byte[] FString(int field, string s)
{
byte[] bytes = Encoding.UTF8.GetBytes(s);
return Concat(WriteTag(field, ProtoWire.Bytes), WriteVarint((ulong)bytes.Length), bytes);
}
public static byte[] FBytes(int field, byte[] b)
{
return Concat(WriteTag(field, ProtoWire.Bytes), WriteVarint((ulong)b.Length), b);
}
public static byte[] FInt(int field, long v)
{
return Concat(WriteTag(field, ProtoWire.Varint), WriteVarint((ulong)v));
}
public static byte[] FBool(int field, bool v)
{
return Concat(WriteTag(field, ProtoWire.Varint), WriteVarint((ulong)(v ? 1 : 0)));
}
public static byte[] FSub(int field, byte[] body)
{
return Concat(WriteTag(field, ProtoWire.Bytes), WriteVarint((ulong)body.Length), body);
}
private static byte[] Concat(params byte[][] chunks)
{
int num = 0;
byte[][] array = chunks;
foreach (byte[] array2 in array)
{
num += array2.Length;
}
byte[] array3 = new byte[num];
int num2 = 0;
array = chunks;
foreach (byte[] array4 in array)
{
Buffer.BlockCopy(array4, 0, array3, num2, array4.Length);
num2 += array4.Length;
}
return array3;
}
public static (ulong val, int newOff) ReadVarint(byte[] buf, int off)
{
ulong num = 0uL;
int num2 = 0;
while (off < buf.Length)
{
byte b = buf[off++];
num |= (ulong)((long)(b & 0x7F) << num2);
if ((b & 0x80) == 0)
{
return (val: num, newOff: off);
}
num2 += 7;
if (num2 >= 64)
{
throw new FormatException("varint exceeds 10 bytes");
}
}
throw new FormatException("truncated varint");
}
public static Dictionary<int, List<ProtoValue>> Parse(byte[] data)
{
Dictionary<int, List<ProtoValue>> dictionary = new Dictionary<int, List<ProtoValue>>();
int num = 0;
while (num < data.Length)
{
(ulong val, int newOff) tuple = ReadVarint(data, num);
ulong item = tuple.val;
num = tuple.newOff;
int key = (int)(item >> 3);
int num2 = (int)(item & 7);
ProtoValue item2;
switch (num2)
{
case 0:
{
(ulong val, int newOff) tuple2 = ReadVarint(data, num);
ulong item3 = tuple2.val;
num = tuple2.newOff;
item2 = ProtoValue.OfVarint(item3);
break;
}
case 1:
if (num + 8 > data.Length)
{
throw new FormatException("truncated fixed64");
}
item2 = ProtoValue.OfFixed64(BitConverter.ToUInt64(data, num));
num += 8;
break;
case 2:
{
ulong num3;
(num3, num) = ReadVarint(data, num);
if (num + (int)num3 > data.Length)
{
throw new FormatException("truncated length-delimited");
}
byte[] array = new byte[(uint)num3];
Buffer.BlockCopy(data, num, array, 0, (int)num3);
num += (int)num3;
item2 = ProtoValue.OfBytes(array);
break;
}
case 5:
if (num + 4 > data.Length)
{
throw new FormatException("truncated fixed32");
}
item2 = ProtoValue.OfFixed32(BitConverter.ToUInt32(data, num));
num += 4;
break;
default:
throw new FormatException($"unknown wire type {num2} at offset {num}");
}
if (!dictionary.TryGetValue(key, out var value))
{
value = (dictionary[key] = new List<ProtoValue>());
}
value.Add(item2);
}
return dictionary;
}
public static List<string> GetStrings(Dictionary<int, List<ProtoValue>> parsed, int field)
{
if (!parsed.TryGetValue(field, out List<ProtoValue> value))
{
return new List<string>();
}
List<string> list = new List<string>(value.Count);
foreach (ProtoValue item in value)
{
if (item.Wire == ProtoWire.Bytes)
{
list.Add(Encoding.UTF8.GetString(item.Bytes));
}
}
return list;
}
public static List<long> GetInts(Dictionary<int, List<ProtoValue>> parsed, int field)
{
if (!parsed.TryGetValue(field, out List<ProtoValue> value))
{
return new List<long>();
}
List<long> list = new List<long>(value.Count);
foreach (ProtoValue item in value)
{
if (item.Wire == ProtoWire.Varint)
{
list.Add((long)item.Number);
}
}
return list;
}
public static string? FirstString(Dictionary<int, List<ProtoValue>> parsed, int field, string? fallback = null)
{
List<string> strings = GetStrings(parsed, field);
if (strings.Count <= 0)
{
return fallback;
}
return strings[0];
}
public static byte[]? FirstSub(Dictionary<int, List<ProtoValue>> parsed, int field)
{
if (!parsed.TryGetValue(field, out List<ProtoValue> value))
{
return null;
}
foreach (ProtoValue item in value)
{
if (item.Wire == ProtoWire.Bytes)
{
return item.Bytes;
}
}
return null;
}
public static string Dump(byte[] data)
{
StringBuilder stringBuilder = new StringBuilder();
DumpInto(data, 0, stringBuilder);
return stringBuilder.ToString();
}
private static void DumpInto(byte[] data, int depth, StringBuilder sb)
{
if (depth > 8)
{
sb.Append(Indent(depth)).Append("<max depth>\n");
return;
}
Dictionary<int, List<ProtoValue>> source;
try
{
source = Parse(data);
}
catch (Exception ex)
{
sb.Append(Indent(depth)).Append("<parse err: ").Append(ex.Message)
.Append(">\n");
return;
}
foreach (KeyValuePair<int, List<ProtoValue>> item in source.OrderBy((KeyValuePair<int, List<ProtoValue>> p) => p.Key))
{
foreach (ProtoValue item2 in item.Value)
{
string value = Indent(depth);
switch (item2.Wire)
{
case ProtoWire.Varint:
sb.Append(value).Append('F').Append(item.Key)
.Append(" varint = ")
.Append(item2.Number)
.Append('\n');
break;
case ProtoWire.Fixed64:
sb.Append(value).Append('F').Append(item.Key)
.Append(" fixed64 = 0x")
.Append(item2.Number.ToString("x16"))
.Append('\n');
break;
case ProtoWire.Fixed32:
sb.Append(value).Append('F').Append(item.Key)
.Append(" fixed32 = 0x")
.Append(((uint)item2.Number).ToString("x8"))
.Append('\n');
break;
case ProtoWire.Bytes:
{
string s;
if (item2.Bytes.Length == 0)
{
sb.Append(value).Append('F').Append(item.Key)
.Append(" empty\n");
}
else if (LooksLikeProto(item2.Bytes))
{
sb.Append(value).Append('F').Append(item.Key)
.Append(" sub(")
.Append(item2.Bytes.Length)
.Append("):\n");
DumpInto(item2.Bytes, depth + 1, sb);
}
else if (TryUtf8(item2.Bytes, out s))
{
sb.Append(value).Append('F').Append(item.Key)
.Append(" str(")
.Append(item2.Bytes.Length)
.Append(") = ")
.Append(EscapeString(s))
.Append('\n');
}
else
{
sb.Append(value).Append('F').Append(item.Key)
.Append(" bytes(")
.Append(item2.Bytes.Length)
.Append(") = ")
.Append(HexHead(item2.Bytes, 24))
.Append('\n');
}
break;
}
}
}
}
}
private static string Indent(int depth)
{
return new string(' ', depth * 2);
}
private static bool TryUtf8(byte[] b, out string s)
{
try
{
s = Encoding.UTF8.GetString(b);
string text = s;
foreach (char c in text)
{
if (c < ' ' && c != '\t' && c != '\n' && c != '\r')
{
s = "";
return false;
}
}
return true;
}
catch
{
s = "";
return false;
}
}
private static bool LooksLikeProto(byte[] b)
{
if (b.Length < 2)
{
return false;
}
try
{
return Parse(b).Count > 0;
}
catch
{
return false;
}
}
private static string HexHead(byte[] b, int n)
{
StringBuilder stringBuilder = new StringBuilder(n * 3 + 4);
for (int i = 0; i < Math.Min(n, b.Length); i++)
{
stringBuilder.Append(b[i].ToString("x2")).Append(' ');
}
if (b.Length > n)
{
stringBuilder.Append("...");
}
return stringBuilder.ToString().TrimEnd(Array.Empty<char>());
}
private static string EscapeString(string s)
{
if (s.Length > 80)
{
s = s.Substring(0, 80) + "...";
}
return "\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"";
}
}