#include <cstdint>
#include <string>
#include <vector>
#include <pro.h>
#include <ida.hpp>
#include <typeinf.hpp>
#include "gen_type_build.h"
#include "internal.h"
#include "type_write_common.h"
using namespace facade;
namespace gen {
rust::String captured_reason() { return to_rust_string(g_output.c_str(), g_output.length()); }
bool build_int(tinfo_t &out, uint32_t bytes, bool is_signed) {
type_t base;
switch (bytes) {
case 1:
base = BT_INT8;
break;
case 2:
base = BT_INT16;
break;
case 4:
base = BT_INT32;
break;
case 8:
base = BT_INT64;
break;
case 16:
base = BT_INT128;
break;
default:
return false;
}
return out.create_simple_type(base | (is_signed ? BTMT_SIGNED : BTMT_UNSIGNED));
}
bool build_float(tinfo_t &out, uint32_t bytes) {
type_t mod;
switch (bytes) {
case 4:
mod = BTMT_FLOAT;
break;
case 8:
mod = BTMT_DOUBLE;
break;
default:
return false;
}
return out.create_simple_type(BT_FLOAT | mod);
}
bool build_named(tinfo_t &out, const char *name) {
return out.get_named_type(get_idati(), name, BTF_TYPEDEF, false);
}
namespace {
bool build_bitfield(tinfo_t &out, uint8_t nbytes, uint8_t width, bool is_unsigned) {
return out.create_bitfield(nbytes, width, is_unsigned);
}
struct recipe_reader {
const uint8_t *p;
size_t len;
size_t pos = 0;
bool ok = true;
bool has_more() const { return ok && pos < len; }
uint8_t u8() {
if (pos + 1 > len) {
ok = false;
return 0;
}
return p[pos++];
}
uint64_t uint_le(size_t n) {
if (pos + n > len) {
ok = false;
return 0;
}
uint64_t v = 0;
for (size_t i = 0; i < n; i++)
v |= static_cast<uint64_t>(p[pos++]) << (8 * i);
return v;
}
bool str(std::string &out) {
uint64_t n = uint_le(4);
if (!ok || pos + static_cast<size_t>(n) > len) {
ok = false;
return false;
}
out.assign(reinterpret_cast<const char *>(p + pos), static_cast<size_t>(n));
pos += static_cast<size_t>(n);
return true;
}
};
}
int build_recipe(const uint8_t *buf, size_t len, tinfo_t &out) {
recipe_reader reader{buf, len};
std::vector<tinfo_t> stack;
while (reader.has_more()) {
uint8_t op = reader.u8();
switch (op) {
case RECIPE_VOID: {
tinfo_t t;
if (!t.create_simple_type(BTF_VOID))
return TYPE_ERR_INPUT;
stack.push_back(t);
break;
}
case RECIPE_BOOL: {
tinfo_t t;
if (!t.create_simple_type(BT_BOOL))
return TYPE_ERR_INPUT;
stack.push_back(t);
break;
}
case RECIPE_INT: {
uint8_t bytes = reader.u8();
uint8_t is_signed = reader.u8();
tinfo_t t;
if (!reader.ok || !build_int(t, bytes, is_signed != 0))
return TYPE_ERR_INPUT;
stack.push_back(t);
break;
}
case RECIPE_FLOAT: {
uint8_t bytes = reader.u8();
tinfo_t t;
if (!reader.ok || !build_float(t, bytes))
return TYPE_ERR_INPUT;
stack.push_back(t);
break;
}
case RECIPE_NAMED: {
std::string name;
if (!reader.str(name))
return TYPE_ERR_INPUT;
tinfo_t t;
if (!build_named(t, name.c_str()))
return TYPE_ERR_INPUT;
stack.push_back(t);
break;
}
case RECIPE_DECL: {
std::string decl;
if (!reader.str(decl))
return TYPE_ERR_INPUT;
tinfo_t t;
qstring pname;
if (!parse_decl(&t, &pname, get_idati(), decl.c_str(), PT_SEMICOLON))
return TYPE_ERR_INPUT;
stack.push_back(t);
break;
}
case RECIPE_PTR: {
if (stack.empty())
return TYPE_ERR_INPUT;
tinfo_t inner = stack.back();
stack.pop_back();
tinfo_t t;
if (!t.create_ptr(inner))
return TYPE_ERR_INPUT;
stack.push_back(t);
break;
}
case RECIPE_ARRAY: {
constexpr uint64_t MAX_ARRAY_ELEMS = 0xffffffffULL;
uint64_t nelems = reader.uint_le(8); if (!reader.ok || nelems > MAX_ARRAY_ELEMS || stack.empty())
return TYPE_ERR_INPUT;
tinfo_t inner = stack.back();
stack.pop_back();
tinfo_t t;
if (!t.create_array(inner, static_cast<uint32>(nelems)))
return TYPE_ERR_INPUT;
stack.push_back(t);
break;
}
case RECIPE_CONST: {
if (stack.empty())
return TYPE_ERR_INPUT;
stack.back().set_const(); break;
}
case RECIPE_VOLATILE: {
if (stack.empty())
return TYPE_ERR_INPUT;
stack.back().set_volatile(); break;
}
case RECIPE_FUNCTION: {
uint64_t nparams = reader.uint_le(4); uint8_t varargs = reader.u8();
uint64_t cc = reader.uint_le(2); if (!reader.ok || nparams > (reader.len - reader.pos) / 4)
return TYPE_ERR_INPUT;
std::vector<std::string> names(static_cast<size_t>(nparams));
for (uint64_t i = 0; i < nparams && reader.ok; i++)
reader.str(names[static_cast<size_t>(i)]);
if (!reader.ok || stack.size() < static_cast<size_t>(nparams) + 1)
return TYPE_ERR_INPUT;
func_type_data_t ftd;
size_t base = stack.size() - static_cast<size_t>(nparams);
ftd.rettype = stack[base - 1];
for (size_t i = 0; i < static_cast<size_t>(nparams); i++) {
funcarg_t arg;
arg.type = stack[base + i];
arg.name = names[i].c_str();
ftd.push_back(arg);
}
stack.resize(base - 1);
if (varargs != 0)
ftd.set_cc(CM_CC_ELLIPSIS);
else if (cc != 0)
ftd.set_cc(static_cast<callcnv_t>(cc));
tinfo_t t;
if (!t.create_func(ftd))
return TYPE_ERR_INPUT;
stack.push_back(t);
break;
}
case RECIPE_BITFIELD: {
uint8_t nbytes = reader.u8();
uint8_t width = reader.u8();
uint8_t is_signed = reader.u8();
tinfo_t t;
if (!reader.ok || !build_bitfield(t, nbytes, width, is_signed == 0))
return TYPE_ERR_INPUT;
stack.push_back(t);
break;
}
default:
return TYPE_ERR_INPUT;
}
}
if (!reader.ok || stack.size() != 1)
return TYPE_ERR_INPUT;
out = stack[0];
return TYPE_OK;
}
bool load_named_type(const char *type_name, tinfo_t &tif) {
return tif.get_named_type(get_idati(), type_name) && !tif.empty();
}
rust::Vec<uint8_t> cm_cc_ids() {
rust::Vec<uint8_t> out;
for (callcnv_t c : {CM_CC_UNKNOWN, CM_CC_CDECL, CM_CC_STDCALL, CM_CC_PASCAL, CM_CC_FASTCALL,
CM_CC_THISCALL, CM_CC_SWIFT, CM_CC_GOLANG})
out.push_back(static_cast<uint8_t>(c));
return out;
}
rust::Vec<uint32_t> frb_vtype_ids() {
rust::Vec<uint32_t> out;
for (uint32_t v : {FRB_NUMB, FRB_NUMO, FRB_NUMH, FRB_NUMD, FRB_CHAR})
out.push_back(v);
return out;
}
rust::Vec<int32_t> tinfo_code_ids() {
rust::Vec<int32_t> out;
for (tinfo_code_t c :
{TERR_OK, TERR_SAVE_ERROR, TERR_SERIALIZE, TERR_BAD_NAME, TERR_BAD_ARG,
TERR_BAD_TYPE, TERR_BAD_SIZE, TERR_BAD_INDEX, TERR_BAD_ARRAY, TERR_BAD_BF,
TERR_BAD_OFFSET, TERR_BAD_UNIVAR, TERR_BAD_VARLAST, TERR_OVERLAP, TERR_BAD_SUBTYPE,
TERR_BAD_VALUE, TERR_NO_BMASK, TERR_BAD_BMASK, TERR_BAD_MSKVAL, TERR_BAD_REPR,
TERR_GRP_NOEMPTY, TERR_DUPNAME, TERR_UNION_BF, TERR_BAD_TAH, TERR_BAD_BASE,
TERR_BAD_GAP, TERR_NESTED, TERR_NOT_COMPAT, TERR_BAD_LAYOUT, TERR_BAD_GROUPS,
TERR_BAD_SERIAL, TERR_ALIEN_NAME, TERR_STOCK, TERR_ENUM_SIZE, TERR_NOT_IMPL,
TERR_TYPE_WORSE, TERR_BAD_FX_SIZE, TERR_STRUCT_SIZE, TERR_NOT_FOUND})
out.push_back(static_cast<int32_t>(c));
return out;
}
}