first commit

This commit is contained in:
dasha 2026-01-05 23:50:53 +03:00
commit 7dd9dd689e
40 changed files with 4100 additions and 0 deletions

74
ProtoGen/ProtoCache.cpp Normal file
View file

@ -0,0 +1,74 @@
#include "ProtoCache.h"
#include "ProtoMetadata.h"
TypeCache TypeCache::init() {
TypeCache cache;
auto corlib = Il2CppApi::AssemblyGetImage(Il2CppApi::DomainAssemblyOpen("mscorlib.dll"));
auto assembly = Il2CppApi::AssemblyGetImage(Il2CppApi::DomainAssemblyOpen("Assembly-CSharp.dll"));
cache.type_map = std::map<std::size_t, CachedType> {
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "Object")),
Object,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "Boolean")),
Boolean,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "Byte")),
Byte,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "SByte")),
SByte,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "UInt16")),
UInt16,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "Int16")),
Int16,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "UInt32")),
UInt32,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "Int32")),
Int32,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "UInt64")),
UInt64,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "Int64")),
Int64,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "Single")),
Single,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "Double")),
Double,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "String")),
String,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(corlib, "System", "Enum")),
Enum,
},
{
reinterpret_cast<std::size_t>(Il2CppApi::ClassFromName(assembly, "", BYTE_STRING)),
ByteString,
},
};
return cache;
}

28
ProtoGen/ProtoCache.h Normal file
View file

@ -0,0 +1,28 @@
#pragma once
#include "Il2CppApi.h"
#include <map>
enum CachedType{
Object,
Boolean,
Byte,
SByte,
UInt16,
Int16,
UInt32,
Int32,
UInt64,
Int64,
Single,
Double,
String,
ByteString,
Any,
Enum,
};
struct TypeCache {
std::map<std::size_t, CachedType> type_map;
static TypeCache init();
};

376
ProtoGen/ProtoMetadata.cpp Normal file
View file

@ -0,0 +1,376 @@
// #include <z3++.h>
#include <chrono>
#include "ProtoMetadata.h"
const std::vector<int> wire_types = {WIRE_TYPE_VAR_INT, WIRE_TYPE_LENGTH_PREFIXED, WIRE_TYPE_I32, WIRE_TYPE_I64};
std::string ProtoMetadata::DumpProto(Il2CppClass* klass) {
void* iter;
std::stringstream outPut;
TypeCache type_cache = TypeCache::init();
std::map<size_t, MessageMinimalInfo> minimal_info_map;
auto get_cmd_id = Il2CppApi::ClassGetMethodFromName(klass, GET_CMD_ID, 0);
if (!get_cmd_id) return {};
Il2CppObject* proto_instance = Il2CppApi::ObjectNew(klass);
Il2CppApi::MethodInvoke(Il2CppApi::ClassGetMethodFromName(klass, ".ctor", 0), proto_instance, nullptr);
Il2CppObject* cmd_id_boxed = Il2CppApi::MethodInvoke(get_cmd_id, proto_instance, nullptr);
auto cmd_id = *reinterpret_cast<const uint16_t*>(Il2CppApi::ObjectUnbox(cmd_id_boxed));
MessageMinimalInfo message_info(cmd_id);
Bruteforcer bruteforcer(type_cache, proto_instance);
for (int field_id = 1; field_id < 4096; ++field_id) {
bool detected = false;
for (int wire_type : wire_types) {
uint32_t tag = pack_wire_tag(field_id, wire_type);
auto input_result = bruteforcer.input(tag);
if (input_result.has_value()) {
const FieldDetectionInfo& info = input_result.value();
message_info.fields.emplace_back(FieldMinimalInfo{
0,
info.offset,
tag,
info.oneof_extra_data
});
detected = true;
break; // salir de wire_types para este field_id
}
}
if (detected) continue;
}
if (Il2CppApi::ClassIsEnum(Il2CppApi::ClassGetParent(klass))) {
std::cout << "Class is enum so we have to do a different thing" << std::endl;
std::string enum_name = Il2CppApi::ClassGetName(klass);
std::vector<std::pair<std::string, int32_t>> variants;
iter = nullptr;
while(auto field = Il2CppApi::ClassGetFields(klass, &iter)) {
std::cout << "Casting field to an IL2CPPField*" << std::endl;
auto value = Il2CppApi::FieldStaticGetValue(field);
std::cout << "Obtaining Field name from field" << std::endl;
auto name = Il2CppApi::FieldGetName(field);
if (!Il2CppApi::FieldIsInstance(field) && value == 0) {
std::cout << "Field with static_get_value == 0" << std::endl;
variants.emplace_back(enum_name + "_" + name, static_cast<int32_t>(value));
}
}
iter = nullptr;
while(auto field = Il2CppApi::ClassGetFields(klass, &iter)) {
std::cout << "Casting field to an IL2CPPField* 2" << std::endl;
auto value = Il2CppApi::FieldStaticGetValue(field);
std::cout << "Obtaining Field name from field 2" << std::endl;
auto name = Il2CppApi::FieldGetName(field);
if (!Il2CppApi::FieldIsInstance(field) && value != 0) {
std::cout << "Field with static_get_value != 0" << std::endl;
variants.emplace_back(enum_name + "_" + name, static_cast<int32_t>(value));
}
}
std::cout << "Emplacing the item" << std::endl;
proto::Enum enumItem = proto::Enum{
enum_name,
std::move(variants)
};
outPut << enumItem;
}
else {
std::vector<std::pair<size_t, proto::Field>> fields;
for (auto field_info : message_info.fields) {
if (field_info.oneof_extra_data.has_value()) continue;
uintptr_t* matching_field = nullptr;
iter = nullptr;
while (auto field = Il2CppApi::ClassGetFields(klass, &iter)) {
auto offset = Il2CppApi::FieldGetOffset(field);
if (Il2CppApi::FieldIsInstance(field) && offset == field_info.offset) {
matching_field = field;
break;
}
}
if (matching_field == nullptr) {
continue;
}
fields.push_back(std::make_pair(
Il2CppApi::FieldGetToken(matching_field),
proto::Field{
ProtoMetadata::CsharpTypeToProtobufType(type_cache, Il2CppApi::FieldGetType(matching_field)),
std::string(Il2CppApi::FieldGetName(matching_field)),
field_info.tag >> 3,
proto::FieldComment{
Il2CppApi::FieldGetOffset(matching_field),
field_info.xor_
}
}
));
}
std::sort(fields.begin(), fields.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
std::vector<proto::Oneof> oneofs;
// Procesar campos que sí tienen oneof_extra_data
for (auto field_info : message_info.fields) {
if (!field_info.oneof_extra_data.has_value()) continue;
auto oneof_data = field_info.oneof_extra_data.value();
uintptr_t* oneof_data_field = nullptr;
iter = nullptr;
while(auto f = Il2CppApi::ClassGetFields(klass, &iter)) {
auto offset = Il2CppApi::FieldGetOffset(f);
if (Il2CppApi::FieldIsInstance(f) && offset == field_info.offset) {
oneof_data_field = f;
break;
}
}
if (!oneof_data_field) continue;
uintptr_t* oneof_enum_field = nullptr;
iter = nullptr;
while(auto f = Il2CppApi::ClassGetFields(klass, &iter)) {
auto offset = Il2CppApi::FieldGetOffset(f);
if (Il2CppApi::FieldIsInstance(f) && offset == oneof_data.oneof_enum_offset) {
oneof_enum_field = f;
break;
}
}
if (!oneof_enum_field) continue;
Il2CppClass* oneof_enum = Il2CppApi::FromIl2CppType(Il2CppApi::FieldGetType(oneof_enum_field));
uintptr_t* oneof_case_enum_field = nullptr;
iter = nullptr;
while(auto f = Il2CppApi::ClassGetFields(oneof_enum, &iter)) {
auto value = Il2CppApi::FieldStaticGetValue(f);
if (!Il2CppApi::FieldIsInstance(f) && value == (field_info.tag >> 3)) {
oneof_case_enum_field = f;
break;
}
}
if (!oneof_case_enum_field) continue;
// Buscar o crear Oneof en vector oneofs
proto::Oneof* oneof_ptr = nullptr;
for (auto& o : oneofs) {
auto fieldName = Il2CppApi::FieldGetName(oneof_data_field);
if (o.name == fieldName) {
oneof_ptr = &o;
break;
}
}
if (!oneof_ptr) {
oneofs.emplace_back(proto::Oneof{std::string(Il2CppApi::FieldGetName(oneof_data_field)), {}});
oneof_ptr = &oneofs.back();
}
// Añadir Field al Oneof
oneof_ptr->fields.emplace_back(proto::Field{
ProtoMetadata::CsharpTypeToProtobufType(type_cache, oneof_data.variant_type),
Il2CppApi::FieldGetName(oneof_case_enum_field),
field_info.tag >> 3,
std::nullopt
});
}
// Construir Message y agregar a proto_file
std::vector<proto::Field> extractedFields;
for (const auto& p : fields) {
extractedFields.push_back(p.second);
}
proto::Message messageItem {
message_info.cmd_id,
Il2CppApi::ClassGetName(klass),
std::move(extractedFields),
std::move(oneofs),
};
outPut << messageItem;
}
return outPut.str();
}
// Constructor bruteforcer con cálculo directo del field_number
Bruteforcer::Bruteforcer(TypeCache type_cache, Il2CppObject* obj) : object(obj) {
Il2CppClass* klass = Il2CppApi::ObjectGetClass(obj);
void* iter = nullptr;
while (auto field = Il2CppApi::ClassGetFields(klass, &iter)) {
size_t offset = Il2CppApi::FieldGetOffset(field);
auto type = Il2CppApi::FieldGetType(field);
Il2CppClass* field_class = Il2CppApi::FromIl2CppType(type);
const char* type_name = Il2CppApi::ClassGetName(field_class);
int wire_type = -1;
if (strcmp(type_name, "Int32") == 0 ||
strcmp(type_name, "UInt32") == 0 ||
strcmp(type_name, "Int64") == 0 ||
strcmp(type_name, "UInt64") == 0 ||
strcmp(type_name, "Boolean") == 0 ||
strcmp(type_name, "Enum") == 0)
{
// En Protobuf: int32, int64, uint32, uint64, bool, enum → varint
wire_type = 0;
}
else if (strcmp(type_name, "String") == 0 ||
strcmp(type_name, "Object") == 0 ||
strcmp(type_name, "Byte[]") == 0)
{
// string, bytes, objetos serializados → length-delimited
wire_type = 2;
}
else if (strcmp(type_name, "Single") == 0 ||
strcmp(type_name, "Fixed32") == 0 ||
strcmp(type_name, "SFixed32") == 0)
{
// float, fixed32, sfixed32 → 32-bit
wire_type = 5;
}
else if (strcmp(type_name, "Double") == 0 ||
strcmp(type_name, "Fixed64") == 0 ||
strcmp(type_name, "SFixed64") == 0)
{
// double, fixed64, sfixed64 → 64-bit
wire_type = 1;
}
bool is_enum = Il2CppApi::ClassIsEnum(field_class);
bool is_object = (strcmp(type_name, "Object") == 0 || strcmp(type_name, "String") == 0);
// Aquí no secuencias field_number, lo calculas con la función dedicada después
cached_fields.push_back(CachedFieldInfo{
-1, // field_number aún no calculado
wire_type,
offset,
is_enum,
is_object
});
}
}
// Método directo que calcula field_number del wire_tag con formula protobuf
std::optional<int> Bruteforcer::build_field_number(uint32_t wire_tag) {
int field_number = wire_tag >> 3;
if (field_number >= 1 && field_number <= 4095) {
return field_number;
}
return std::nullopt;
}
// Función input usa cálculo directo y consulta cached_fields
std::optional<FieldDetectionInfo> Bruteforcer::input(uint32_t wire_tag) {
auto maybe_field_number = build_field_number(wire_tag);
if (!maybe_field_number.has_value()) return std::nullopt;
int field_number = maybe_field_number.value();
int wire_type = wire_tag & 0x07;
for (auto& f : cached_fields) {
if (f.wire_type == wire_type) {
// Ahora consideramos cualquier field_number que coincida con el calculado
// y asociamos el field_number a cached_fields así:
if (f.field_number == -1) {
f.field_number = field_number; // asignar el valor deducido
}
if (f.field_number == field_number) {
bool is_obj = false, is_enum = false;
for (const auto& ff : cached_fields) {
if (ff.offset == f.offset && ff.is_object) is_obj = true;
if (ff.is_enum) is_enum = true;
}
if (is_obj && is_enum) {
return build_field_oneof(wire_tag);
}
return FieldDetectionInfo{
f.offset,
std::nullopt
};
}
}
}
return std::nullopt;
}
std::optional<FieldDetectionInfo> Bruteforcer::build_field_oneof(uint32_t wire_tag) {
auto maybe_field_number = build_field_number(wire_tag);
if (!maybe_field_number.has_value()) return std::nullopt;
int field_number = maybe_field_number.value();
int wire_type = wire_tag & 0x07;
for (auto& f : cached_fields) {
// Considera solo campos con wire_type igual, y que tengan el mismo field_number o no asignado aún (-1)
if (f.wire_type == wire_type && (f.field_number == field_number || f.field_number == -1)) {
// Recolectar campos que compartan offset y sean objeto o enum
std::vector<const CachedFieldInfo*> candidates;
for (const auto& ff : cached_fields) {
if (ff.offset == f.offset && (ff.is_object || ff.is_enum)) {
candidates.push_back(&ff);
}
}
// Un campo oneof típico tiene exactamente dos candidatos: uno objeto, uno enum
if (candidates.size() == 2) {
const CachedFieldInfo* data_field = candidates[0];
const CachedFieldInfo* enum_field = candidates[1];
// Corregir si están invertidos (enum y objeto)
if (enum_field->is_enum && data_field->is_object) {
// Correcto orden
} else if (data_field->is_enum && enum_field->is_object) {
std::swap(data_field, enum_field);
} else {
// No es combinación válida enum-objeto
return std::nullopt;
}
// Usar Il2CppApi para obtener Il2CppType* del campo de datos
// Aquí hay que convertir correctamente el 'field' de la cache a tipo
// Asumo que tienes alguna forma de obtener Il2CppType* desde CachedFieldInfo->offset
// Si no, adapta según tu API
Il2CppClass* klass = Il2CppApi::ObjectGetClass(object);
Il2CppType* data_type = nullptr;
// Buscar campo en klass por offset para obtener tipo
void* iter = nullptr;
while (auto field = Il2CppApi::ClassGetFields(klass, &iter)) {
auto offset = Il2CppApi::FieldGetOffset(field);
if (offset == data_field->offset) {
data_type = const_cast<Il2CppType*>(Il2CppApi::FieldGetType(field));
break;
}
}
if (!data_type) {
// No se pudo obtener tipo, abortar
return std::nullopt;
}
return FieldDetectionInfo{
data_field->offset,
std::optional<OneofVariantInfo>{
OneofVariantInfo{
enum_field->offset,
data_type
}
}
};
}
}
}
return std::nullopt;
}
std::string ProtoMetadata::CsharpTypeToProtobufType(const TypeCache& cache, const Il2CppType* ty) {
if (!ty) return "unknown";
// 🔎 comprobar si es genérico
if (ty->type == IL2CPP_TYPE_GENERICINST) {
Il2CppClass* klass = Il2CppApi::FromIl2CppType(ty);
int gen_count = Il2CppApi::ClassGetGenericArgCount(klass);
if (gen_count == 1) {
auto generic_type = reinterpret_cast<const Il2CppType*>(Il2CppApi::ClassGetGenericArgType(klass, 0));
return "repeated " + CsharpTypeToProtobufType(cache, generic_type);
} else if (gen_count == 2) {
auto generic_type1 = reinterpret_cast<const Il2CppType*>(Il2CppApi::ClassGetGenericArgType(klass, 0));
auto generic_type2 = reinterpret_cast<const Il2CppType*>(Il2CppApi::ClassGetGenericArgType(klass, 1));
return "map<"
+ CsharpTypeToProtobufType(cache, generic_type1) + ", "
+ CsharpTypeToProtobufType(cache, generic_type2) + ">";
}
}
// 🔎 tipos básicos: mirar en type_cache
Il2CppClass* klass = Il2CppApi::FromIl2CppType(ty);
auto ptr = reinterpret_cast<std::size_t>(klass);
auto it = cache.type_map.find(ptr);
if (it != cache.type_map.end()) {
switch (it->second) {
case CachedType::Boolean: return "bool";
case CachedType::Int32: return "int32";
case CachedType::UInt32: return "uint32";
case CachedType::Int64: return "int64";
case CachedType::UInt64: return "uint64";
case CachedType::Single: return "float";
case CachedType::Double: return "double";
case CachedType::String: return "string";
case CachedType::ByteString: return "bytes";
case CachedType::Any: return "google.protobuf.Any";
default: throw std::runtime_error("unreachable CachedType");
}
}
// 🔎 si no es básico ni genérico → es tipo definido por el usuario
return Il2CppApi::ClassGetName(klass);
}

80
ProtoGen/ProtoMetadata.h Normal file
View file

@ -0,0 +1,80 @@
#pragma once
#include <vector>
#include <cstdint>
#include <algorithm>
#include <map>
#include <optional>
#include "Il2CppHelper.h"
#include "ProtoUtil.h"
#include "ProtoCache.h"
#include "ProtoOutput.h"
// Constantes equivalentes a cadenas estáticas de Rust
constexpr const char* CODED_INPUT_STREAM = "CELNCCLEKFP";
constexpr const char* MERGE_FROM = "MMANOOLJPGC";
constexpr const char* GET_CMD_ID = "PIGAAPNKIGC";
constexpr const char* PROTO_CLASS = "KKAGGNDBDHE";
constexpr const char* BYTE_STRING = "HIHGFFMNNDN";
struct CachedFieldInfo {
int field_number;
int wire_type;
size_t offset;
bool is_enum;
bool is_object;
};
// Equivalente a OneofVariantInfo
struct OneofVariantInfo {
size_t oneof_enum_offset;
const Il2CppType* variant_type;
};
// Equivalente a FieldMinimalInfo
struct FieldMinimalInfo {
uint32_t xor_;
size_t offset;
uint32_t tag;
std::optional<OneofVariantInfo> oneof_extra_data;
};
// Equivalente a MessageMinimalInfo
struct MessageMinimalInfo {
uint16_t cmd_id;
std::vector<FieldMinimalInfo> fields;
MessageMinimalInfo(uint16_t cmd_id_) : cmd_id(cmd_id_) {}
};
// Equivalente a FieldDetectionInfo
struct FieldDetectionInfo {
size_t offset;
std::optional<OneofVariantInfo> oneof_extra_data;
};
class Bruteforcer {
public:
Il2CppObject* object;
const uint8_t* merge_from_method;
std::vector<CachedFieldInfo> cached_fields;
// Constructor
Bruteforcer(TypeCache type_cache, Il2CppObject* obj);
// Método input
std::optional<FieldDetectionInfo> input(uint32_t wire_tag);
private:
std::optional<int> build_field_number(uint32_t wire_tag);
std::optional<FieldDetectionInfo> build_field_oneof(uint32_t wire_tag);
};
class ProtoMetadata {
public:
static std::string DumpProto(Il2CppClass* klass);
static Il2CppObject* CreateInputStream(const std::vector<uint8_t>& buf);
private:
static std::string CsharpTypeToProtobufType(const TypeCache& cache, const Il2CppType* ty);
};

74
ProtoGen/ProtoOutput.cpp Normal file
View file

@ -0,0 +1,74 @@
#include "ProtoOutput.h"
using namespace proto;
std::ostream& proto::operator<<(std::ostream& os, const FieldComment& c) {
os << " // offset: " << c.offset << ", xor const: " << c.xor_const;
return os;
}
std::ostream& proto::operator<<(std::ostream& os, const Field& f) {
os << " " << f.kind << " " << f.name << " = " << f.number << ";";
if (f.comment) {
os << *(f.comment);
}
return os;
}
std::ostream& proto::operator<<(std::ostream& os, const Oneof& o) {
os << " oneof " << o.name << " {\n";
for (const auto& field : o.fields) {
os << " " << field << "\n";
}
os << " }";
return os;
}
std::ostream& proto::operator<<(std::ostream& os, const Enum& e) {
os << "enum " << e.name << " {\n";
for (const auto& [name, value] : e.variants) {
os << " " << name << " = " << value << ";\n";
}
os << "}\n";
return os;
}
std::ostream& proto::operator<<(std::ostream& os, const Message& m) {
os << "message " << m.name << " {";
if (m.cmd_id != 0) {
os << " // CmdID: " << m.cmd_id;
}
os << "\n";
for (const auto& field : m.fields) {
os << field << "\n";
}
for (const auto& oneof : m.oneofs) {
os << oneof << "\n";
}
os << "}\n\n";
return os;
}
std::ostream& proto::operator<<(std::ostream& os, const ProtoItem& item) {
if (std::holds_alternative<Message>(item)) {
os << std::get<Message>(item);
} else if (std::holds_alternative<Enum>(item)) {
os << std::get<Enum>(item);
}
return os;
}
std::ostream& proto::operator<<(std::ostream& os, const ProtoFile& pf) {
os << "syntax = \"" << pf.syntax << "\";\n";
for (const auto& import : pf.imports) {
os << "import \"" << import << "\";\n";
}
os << "\n";
for (const auto& item : pf.items) {
os << item;
}
return os;
}

58
ProtoGen/ProtoOutput.h Normal file
View file

@ -0,0 +1,58 @@
#pragma once
#include <cstdint>
#include <iostream>
#include <string>
#include <vector>
#include <optional>
#include <variant>
// Declaraciones de estructuras
namespace proto {
struct FieldComment {
size_t offset;
uint32_t xor_const;
};
struct Field {
std::string kind;
std::string name;
uint32_t number;
std::optional<FieldComment> comment;
};
struct Oneof {
std::string name;
std::vector<Field> fields;
};
struct Enum {
std::string name;
std::vector<std::pair<std::string, int32_t>> variants;
};
struct Message {
uint16_t cmd_id;
std::string name;
std::vector<Field> fields;
std::vector<Oneof> oneofs;
};
using ProtoItem = std::variant<Message, Enum>;
struct ProtoFile {
std::string syntax;
std::vector<std::string> imports;
std::vector<ProtoItem> items;
};
// Declaración de operadores de salida
std::ostream& operator<<(std::ostream& os, const FieldComment& c);
std::ostream& operator<<(std::ostream& os, const Field& f);
std::ostream& operator<<(std::ostream& os, const Oneof& o);
std::ostream& operator<<(std::ostream& os, const Enum& e);
std::ostream& operator<<(std::ostream& os, const Message& m);
std::ostream& operator<<(std::ostream& os, const ProtoItem& item);
std::ostream& operator<<(std::ostream& os, const ProtoFile& pf);
}

32
ProtoGen/ProtoUtil.cpp Normal file
View file

@ -0,0 +1,32 @@
#include "ProtoUtil.h"
size_t varint_length(uint32_t v) {
if (v == 0) return 1;
size_t logcounter = 0;
while (v > 0) {
++logcounter;
v >>= 7;
}
return logcounter;
}
size_t encode_varint(std::vector<uint8_t>& dst, uint32_t value) {
constexpr uint8_t MSB = 0b10000000;
uint32_t n = value;
size_t i = 0;
while (n >= 0x80) {
dst.push_back(MSB | static_cast<uint8_t>(n & 0x7F));
++i;
n >>= 7;
}
dst.push_back(static_cast<uint8_t>(n));
return i + 1;
}
uint32_t pack_wire_tag(uint32_t field_id, uint8_t wire_type) {
return (field_id << 3) | static_cast<uint32_t>(wire_type);
}

32
ProtoGen/ProtoUtil.h Normal file
View file

@ -0,0 +1,32 @@
#pragma once
#include <vector>
#include <cstdint>
const uint8_t bytes_i32[4] = {0, 0, 0, 1};
const uint8_t bytes_i64[8] = {0, 0, 0, 0, 0, 0, 0, 1};
const std::vector<uint8_t> VECTOR_ONE = {1};
const std::vector<uint8_t> VECTOR_ZERO = {0};
const std::vector<uint8_t> VECTOR_COLLECTION = {1, 0};
const std::vector<std::vector<uint8_t>> LENGTH_PREFIXED_SAMPLES = {
{1, 0, 0, 0, 0},
{1, 0, 0, 0, 0, 0, 0, 0, 0},
{5, 0x08, 0x01, 0x33, 0x01, 0x00},
{5, 0x10, 0x01, 0x33, 0x10, 0x00},
};
const std::vector<uint8_t> LENGTH_I32 = {bytes_i32, bytes_i32 + 4};
const std::vector<uint8_t> LENGTH_I64 = {bytes_i64, bytes_i64 + 8};
// Constantes wire types
constexpr uint8_t WIRE_TYPE_VAR_INT = 0;
constexpr uint8_t WIRE_TYPE_I64 = 1;
constexpr uint8_t WIRE_TYPE_LENGTH_PREFIXED = 2;
constexpr uint8_t WIRE_TYPE_I32 = 5;
// Prototipos de funciones
size_t varint_length(uint32_t v);
size_t encode_varint(std::vector<uint8_t>& dst, uint32_t value);
uint32_t pack_wire_tag(uint32_t field_id, uint8_t wire_type);