404 lines
9.3 KiB
C++
404 lines
9.3 KiB
C++
#pragma once
|
|
|
|
#include "../Utility/String.hpp"
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
#define assert_struct_size(x, y) static_assert(sizeof(x) == (y), "Improper struct size")
|
|
|
|
#if defined(__clang__)
|
|
#define FORCE_ALIGN_ARG_POINTER __attribute__((force_align_arg_pointer))
|
|
#else
|
|
#define FORCE_ALIGN_ARG_POINTER
|
|
#endif
|
|
|
|
constexpr int32_t DEFAULT_REG_VAL = 0xCCCCCCCC;
|
|
|
|
namespace openloco::interop
|
|
{
|
|
#pragma pack(push, 1)
|
|
/**
|
|
* x86 register structure, only used for easy interop to Locomotion code.
|
|
*/
|
|
struct registers
|
|
{
|
|
union
|
|
{
|
|
int32_t eax{ DEFAULT_REG_VAL };
|
|
int16_t ax;
|
|
struct
|
|
{
|
|
int8_t al;
|
|
int8_t ah;
|
|
};
|
|
};
|
|
union
|
|
{
|
|
int32_t ebx{ DEFAULT_REG_VAL };
|
|
int16_t bx;
|
|
struct
|
|
{
|
|
int8_t bl;
|
|
int8_t bh;
|
|
};
|
|
};
|
|
union
|
|
{
|
|
int32_t ecx{ DEFAULT_REG_VAL };
|
|
int16_t cx;
|
|
struct
|
|
{
|
|
int8_t cl;
|
|
int8_t ch;
|
|
};
|
|
};
|
|
union
|
|
{
|
|
int32_t edx{ DEFAULT_REG_VAL };
|
|
int16_t dx;
|
|
struct
|
|
{
|
|
int8_t dl;
|
|
int8_t dh;
|
|
};
|
|
};
|
|
union
|
|
{
|
|
int32_t esi{ DEFAULT_REG_VAL };
|
|
int16_t si;
|
|
};
|
|
union
|
|
{
|
|
int32_t edi{ DEFAULT_REG_VAL };
|
|
int16_t di;
|
|
};
|
|
union
|
|
{
|
|
int32_t ebp{ DEFAULT_REG_VAL };
|
|
int16_t bp;
|
|
};
|
|
};
|
|
assert_struct_size(registers, 7 * 4);
|
|
#pragma pack(pop)
|
|
|
|
#ifndef USE_MMAP
|
|
constexpr uintptr_t GOOD_PLACE_FOR_DATA_SEGMENT = 0x008A4000;
|
|
#else
|
|
#if defined(PLATFORM_32BIT)
|
|
constexpr uintptr_t GOOD_PLACE_FOR_DATA_SEGMENT = 0x09000000;
|
|
#elif defined(PLATFORM_64BIT)
|
|
constexpr uintptr_t GOOD_PLACE_FOR_DATA_SEGMENT = 0x200000000;
|
|
#else
|
|
#error "Unknown platform"
|
|
#endif
|
|
#endif
|
|
|
|
constexpr uintptr_t remapAddress(uintptr_t locoAddress)
|
|
{
|
|
return GOOD_PLACE_FOR_DATA_SEGMENT - 0x008A4000 + locoAddress;
|
|
}
|
|
|
|
template<uint32_t TAddress, typename T>
|
|
constexpr T& addr()
|
|
{
|
|
return *((T*)remapAddress(TAddress));
|
|
}
|
|
|
|
/**
|
|
* Returns the flags register
|
|
*
|
|
* Flags register is as follows:
|
|
* 0bSZ0A_0P0C_0000_0000
|
|
* S = Signed flag
|
|
* Z = Zero flag
|
|
* C = Carry flag
|
|
* A = Adjust flag
|
|
* P = Parity flag
|
|
* All other bits are undefined.
|
|
*/
|
|
int32_t call(int32_t address);
|
|
int32_t call(int32_t address, registers& registers);
|
|
|
|
template<typename T, uintptr_t TAddress>
|
|
struct loco_global
|
|
{
|
|
public:
|
|
typedef T type;
|
|
typedef type* pointer;
|
|
typedef type& reference;
|
|
typedef const type& const_reference;
|
|
|
|
private:
|
|
pointer _Myptr;
|
|
|
|
public:
|
|
loco_global()
|
|
{
|
|
_Myptr = &(addr<TAddress, T>());
|
|
}
|
|
|
|
operator reference()
|
|
{
|
|
return addr<TAddress, T>();
|
|
}
|
|
|
|
loco_global& operator=(const_reference v)
|
|
{
|
|
addr<TAddress, T>() = v;
|
|
return *this;
|
|
}
|
|
|
|
loco_global& operator+=(const_reference v)
|
|
{
|
|
addr<TAddress, T>() += v;
|
|
return *this;
|
|
}
|
|
|
|
loco_global& operator|=(const_reference v)
|
|
{
|
|
addr<TAddress, T>() |= v;
|
|
return *this;
|
|
}
|
|
|
|
loco_global& operator&=(const_reference v)
|
|
{
|
|
addr<TAddress, T>() &= v;
|
|
return *this;
|
|
}
|
|
|
|
loco_global& operator^=(const_reference v)
|
|
{
|
|
addr<TAddress, T>() ^= v;
|
|
return *this;
|
|
}
|
|
|
|
loco_global& operator-=(const_reference v)
|
|
{
|
|
addr<TAddress, T>() -= v;
|
|
return *this;
|
|
}
|
|
|
|
loco_global& operator++()
|
|
{
|
|
addr<TAddress, T>()++;
|
|
return *this;
|
|
}
|
|
|
|
T operator++(int)
|
|
{
|
|
reference ref = addr<TAddress, T>();
|
|
T temp = ref;
|
|
ref++;
|
|
return temp;
|
|
}
|
|
|
|
loco_global& operator--()
|
|
{
|
|
addr<TAddress, T>()--;
|
|
return *this;
|
|
}
|
|
|
|
T operator--(int)
|
|
{
|
|
reference ref = addr<TAddress, T>();
|
|
T temp = ref;
|
|
ref--;
|
|
return temp;
|
|
}
|
|
|
|
reference operator*()
|
|
{
|
|
return addr<TAddress, T>();
|
|
}
|
|
|
|
pointer operator->()
|
|
{
|
|
return &(addr<TAddress, T>());
|
|
}
|
|
|
|
constexpr size_t size() const
|
|
{
|
|
return sizeof(T);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct loco_global_iterator
|
|
{
|
|
private:
|
|
T* _ptr;
|
|
|
|
public:
|
|
loco_global_iterator(T* p)
|
|
: _ptr(p)
|
|
{
|
|
}
|
|
loco_global_iterator& operator++()
|
|
{
|
|
++_ptr;
|
|
return *this;
|
|
}
|
|
loco_global_iterator operator++(int)
|
|
{
|
|
auto temp = *this;
|
|
++_ptr;
|
|
return temp;
|
|
}
|
|
loco_global_iterator& operator--()
|
|
{
|
|
--_ptr;
|
|
return *this;
|
|
}
|
|
loco_global_iterator operator--(int)
|
|
{
|
|
auto temp = *this;
|
|
--_ptr;
|
|
return temp;
|
|
}
|
|
bool operator==(const loco_global_iterator& rhs)
|
|
{
|
|
return _ptr == rhs._ptr;
|
|
}
|
|
bool operator!=(const loco_global_iterator& rhs)
|
|
{
|
|
return _ptr != rhs._ptr;
|
|
}
|
|
T& operator*()
|
|
{
|
|
return *_ptr;
|
|
}
|
|
};
|
|
|
|
template<typename T, size_t TCount, uintptr_t TAddress>
|
|
struct loco_global<T[TCount], TAddress>
|
|
{
|
|
public:
|
|
typedef T type;
|
|
typedef type* pointer;
|
|
typedef type& reference;
|
|
typedef const type& const_reference;
|
|
typedef loco_global_iterator<T> iterator;
|
|
|
|
private:
|
|
pointer _Myfirst;
|
|
pointer _Mylast;
|
|
|
|
public:
|
|
loco_global()
|
|
{
|
|
_Myfirst = get();
|
|
_Mylast = _Myfirst + TCount;
|
|
}
|
|
|
|
operator pointer()
|
|
{
|
|
return get();
|
|
}
|
|
|
|
pointer get() const
|
|
{
|
|
return reinterpret_cast<pointer>(&addr<TAddress, type>());
|
|
}
|
|
|
|
reference operator[](int idx)
|
|
{
|
|
#ifndef NDEBUG
|
|
if (idx < 0 || static_cast<size_t>(idx) >= size())
|
|
{
|
|
throw std::out_of_range("loco_global: bounds check violation!");
|
|
}
|
|
#endif
|
|
return get()[idx];
|
|
}
|
|
|
|
constexpr size_t size() const
|
|
{
|
|
return TCount;
|
|
}
|
|
|
|
iterator begin() const
|
|
{
|
|
return iterator(&addr<TAddress, T>());
|
|
}
|
|
|
|
iterator end() const
|
|
{
|
|
const pointer ptrEnd = (&addr<TAddress, T>()) + TCount;
|
|
return iterator(ptrEnd);
|
|
}
|
|
};
|
|
|
|
enum
|
|
{
|
|
X86_FLAG_CARRY = 1 << 0,
|
|
X86_FLAG_PARITY = 1 << 2,
|
|
X86_FLAG_ADJUST = 1 << 4,
|
|
X86_FLAG_ZERO = 1 << 6,
|
|
X86_FLAG_SIGN = 1 << 7,
|
|
};
|
|
|
|
class save_state
|
|
{
|
|
private:
|
|
uintptr_t begin = 0;
|
|
uintptr_t end = 0;
|
|
std::vector<std::byte> state;
|
|
|
|
public:
|
|
const std::vector<std::byte>& getState() const
|
|
{
|
|
return state;
|
|
}
|
|
|
|
save_state(uintptr_t begin, uintptr_t end);
|
|
void reset();
|
|
|
|
static void logDiff(const save_state& lhs, const save_state& rhs);
|
|
};
|
|
|
|
bool operator==(const save_state& lhs, const save_state& rhs);
|
|
bool operator!=(const save_state& lhs, const save_state& rhs);
|
|
|
|
void readMemory(uint32_t address, void* data, size_t size);
|
|
void writeMemory(uint32_t address, const void* data, size_t size);
|
|
|
|
using hook_function = uint8_t (*)(registers& regs);
|
|
|
|
void registerHook(uintptr_t address, hook_function function);
|
|
void writeRet(uint32_t address);
|
|
void writeJmp(uint32_t address, void* fn);
|
|
void writeNop(uint32_t address, size_t count);
|
|
void hookDump(uint32_t address, void* fn);
|
|
void hookLib(uint32_t address, void* fn);
|
|
|
|
void registerHooks();
|
|
void loadSections();
|
|
}
|
|
|
|
// these safe string function convenience overloads are located in this header, rather than in Utility/String.hpp,
|
|
// so that Utility/String.hpp doesn't needlessly have to include this header just for the definition of loco_global
|
|
// (and so that we don't have to use type traits SFINAE template wizardry to get around not having the definition available)
|
|
namespace openloco::utility
|
|
{
|
|
template<size_t TCount, uintptr_t TAddress>
|
|
void strcpy_safe(openloco::interop::loco_global<char[TCount], TAddress>& dest, const char* src)
|
|
{
|
|
(void)strlcpy(dest, src, dest.size());
|
|
}
|
|
|
|
template<size_t TCount, uintptr_t TAddress>
|
|
void strcat_safe(openloco::interop::loco_global<char[TCount], TAddress>& dest, const char* src)
|
|
{
|
|
(void)strlcat(dest, src, dest.size());
|
|
}
|
|
|
|
template<size_t TCount, uintptr_t TAddress, typename... Args>
|
|
int sprintf_safe(openloco::interop::loco_global<char[TCount], TAddress>& dest, const char* fmt, Args&&... args)
|
|
{
|
|
return std::snprintf(dest, TCount, fmt, std::forward<Args>(args)...);
|
|
}
|
|
}
|