Remodel
Remodel Documentation

remodel is a lightweight C++ library that allows creating wrappers for proprietary data structures and classes (with possibly many unknown fields) of closed source applications or network traffic avoiding padding fields or messy casts.

Basic usage

Imagine a scenario where you have instances of Dog in memory (let's say in your dog-simulator game that you intend to write mods for) that need be accessed.

using namespace remodel;
class CustomString
{
char *data;
std::size_t length;
public:
const char* str() const { return data; }
std::size_t size() const { return length; }
};
class Dog
{
CustomString name;
CustomString* race;
// ..
// possibly many other unknown fields here
// ..
uint8_t age;
bool hatesKittehz;
public:
virtual int calculateFluffiness() const {/* ... */}
virtual void giveGoodie(int amount) {/* ... */}
// .. more methods ..
};

Now the remodeled version:

class CustomString : public AdvancedClassWrapper<8 /* struct size in bytes */>
{
REMODEL_ADV_WRAPPER(CustomString)
// Note we omit the privates here because we decided we only need the methods.
public:
MemberFunction<const char* (*)()> str{this, 0x12345678 /* function addr */};
MemberFunction<std::size_t (*)()> size{this, 0x87654321};
};
// We don't create fields referring to `Dog`, so we don't have to know its
// size and can simply use `ClassWrapper` rather than `AdvancedClassWrapper`.
class Dog : public ClassWrapper
{
// We cheat and make the private fields public for our mod.
public:
Field<CustomString> name{this, 4 /* struct offset in bytes */};
Field<CustomString*> race{this, 12};
// Note that we can just omit the unknown fields here without breaking
// the integrity of the struct. No padding required.
Field<uint8_t> age{this, 124};
Field<bool> hatesKittehz{this, 125};
public:
VirtualFunction<int (*)()> calculateFluffiness{this, 0 /* vftable index */};
VirtualFunction<void (*)(int)> giveGoodie{this, 1};
};

And that's it! You can now use these wrappers pretty similar to how you'd use the original class.

auto dog = wrapper_cast<Dog>(dogInstanceLocation);
// Don't give the bad dog too much of the good stuff!
dog.giveGoodie(dog.hatesKittehz ? 2 : 7);
// Year is over, +1 to age.
++dog.age;
// What was it's race again?
const char* race = dog.race->toStrong().str();

If you read the above snippet carefully, you probably came up with the question why there is a toStrong call where you didn't expect one. When fields are created for types that are wrappers themselves, the library automatically rewrites those with TheWrapperType::Weak which is required to allow pointer and array semantics to behave correctly. These "weak" wrappers can be evolved to "strong" (normal) ones with a simple toStrong() call and can then be used as expected.

Never create pointers to wrappers!

If you mean to represent a pointer to a wrapped object, DO NOT write MyWrapper*. Instead, use MyWrapper::Weak*. Such weak wrapper pointers can be obtained using myWrapperInstance.weakPtr(). The only exception to this rule applies for Field instances that represent pointers to wrappers. Fields automatically detect wrapper pointers and translate them to weak ones, thus effectively Field<MyWrapper*> is equivalent to Field<MyWrapper::Weak*>. Note that creation of pointers to wrappers requires inheritance from AdvancedClassWrapper.

For more information, see Weak wrappers section.

More examples

Creating wrapper instances

Instances of any advanced wrapper (meaning wrappers that derive from AdvancedClassWrapper) can be created using the Instantiable member-type which is derived from your wrapper itself and extends it with actual memory that holds the data of the actual object. In case you were wondering, the Instantiable member-type is declared by the REMODEL_ADV_WRAPPER macro.

struct Flea
{
// ..
};
// Wrappers need to be derived from `AdvancedClassWrapper` in order to
// allow instantiation.
class Cat : public AdvancedClassWrapper<6>
{
public:
enum Gender : uint8_t
{
Male,
Female,
};
Field<uint8_t> age {this, 0};
Field<Gender> gender{this, 1};
Field<Flea*> fleas {this, 2};
};
int main()
{
// You can simply create instances by appending `::Instantiable` to
// your wrapper type and use them just like 'normal' classes.
Cat::Instantiable stackCat;
stackCat.age = 7;
// Need a heap-allocated cat? No problem.
auto heapCat = new Cat::Instantiable;
heapCat->gender = Cat::Male;
return 0;
}

Custom con and destructors

It is possible to define custom routines that serve as con and destructors when instantiating wrappers. When no custom routines are specified, default ones are generated that just do nothing.

Let's expand the example from the previous section with this technique:

struct Flea
{
// ..
};
class Cat : public AdvancedClassWrapper<6>
{
public:
enum Gender : uint8_t
{
Male,
Female,
};
Field<uint8_t> age {this, 0};
Field<Gender> gender{this, 1};
Field<Flea*> fleas {this, 2};
void construct() // (1)
{
age = 0;
gender = Male;
fleas = nullptr;
}
void construct(uint8_t age, Gender gender, Flea* fleas) // (2)
{
this->age = age;
this->gender = gender;
this->fleas = fleas;
}
void destruct()
{
if (fleas)
{
delete [] fleas;
}
}
};
int main()
{
Cat::Instantiable felix{3, Cat::Male, nullptr}; // construct (2) used
Cat::Instantiable unknownCat; // construct (1) used
return 0;
}

It is also possible to use Function (or derivatives) wrappers as con and destruct routines, calling the original con an destructors in memory. In this case, however, you won't be able to use overloads. To work around that, you can create multiple MemberFunction instances and call the correct one in overloaded construct routines.

Functions taking wrapper pointers and callbacks

class Horse : public AdvancedClassWrapper<4>
{
public:
Field<uint32_t> age{this, 0};
};
class Stable : public ClassWrapper
{
public:
MemberFunction<void (__thiscall*)(Horse::Weak*)> addHorse{this, 0x12345678};
using HorseVisitor = void (*)(Horse::Weak*);
MemberFunction<void (__thiscall*)(HorseVisitor)> traverseHorses{this, 0x87654321};
};
void horseTraverser(Horse::Weak* curHorse)
{
std::cout << curHorse->toStrong().age << std::endl;
}
int main()
{
Horse::Instantiable freddy;
auto stable = wrapper_cast<Stable>(locationOfStableInMemory);
stable.addHorse(freddy.weakPtr());
stable.traverseHorses(&horseTraverser);
}

Weak wrappers

Other than "normal" strong wrappers, weak wrappers have the this pointer set to the wrapped object which allows you to create pointers to them directly. This is useful if you need to pass a pointer to a wrapped entity to a function or receive one in a callback without using void* everywhere, thus keeping your code type-safe. You can create a strong wrapper from a weak one via myWeakWrapper.toStrong().