<repo_name>sedmelluq/witcher-sandbox<file_sep>/README.md
# witcher-sandbox
Witcher DLLs for messing with internal stuff
<file_sep>/internal/src/bundles.cpp
#include "bundles.h"
#include "memory/executable_address_space.h"
#include "logging/log.h"
#include <fstream>

static WDepot** static_depot_pointer;

static void* vtable_WBundleDataHandleReader_000_custom[26];
static void* vtable_WBundleDataHandleReader_010_custom[3];

WBundleDiskFile* bundle_file_find(uint32_t file_index) {
  WXBundleManager& manager = *static_depot_pointer[0]->bundle_manager;

  if (file_index > 0 && file_index < manager.file_count) {
    return manager.files[file_index];
  } else {
    return nullptr;
  }
}

static WXBundleFileMapping* bundle_file_mapping(uint32_t file_index) {
  WXBundleManager& manager = *static_depot_pointer[0]->bundle_manager;
  WXBundleFileIndex& index = *manager.file_index;

  if (file_index < index.mapping_count) {
    WXBundleFileMapping* mapping = &index.mappings[file_index];

    if (mapping->file_id != 0) {
      return mapping;
    }
  }

  return nullptr;
}

WDiskBundle* bundle_file_identify(uint32_t file_index) {
  WXBundleFileMapping* mapping = bundle_file_mapping(file_index);

  if (mapping != nullptr) {
    WXBundleManager& manager = *static_depot_pointer[0]->bundle_manager;
    WXBundleFileIndex& index = *manager.file_index;

    WXBundleFileLocation& location = index.locations[mapping->file_id];

    if (location.bundle_index < manager.bundle_count) {
      return manager.bundles[location.bundle_index];
    }
  }

  return nullptr;
}

void bundle_format_file_directory(WDirectory *directory, std::wstring &path) {
  if (directory != nullptr) {
    bundle_format_file_directory(directory->parent, path);

    path.append(directory->name.text);
    path.append(L"/");
  }
}

static void custom_WBundleDataHandleReader_deconstructor(WBundleDataHandleReader* reader) {
  delete[] ((uint8_t*) reader->buffer);
  delete reader;
}

static WBundleDataHandleReader* find_replacement(const std::wstring& file_path) {
  if (file_path != L"depot/gameplay/items/def_loot_shops.xml") {
    return nullptr;
  }

  std::ifstream file(R"(Q:\Games\uncooked_witcher\def_loot_shops.xml)", std::ios::binary | std::ios::ate);
  std::streamsize size = file.tellg();
  file.seekg(0, std::ios::beg);

  auto data = new uint8_t[size + sizeof(WBundleDataHandleReader)];

  if (file.read((char*) &data[sizeof(WBundleDataHandleReader)], size)) {
    auto reader = new WBundleDataHandleReader;
    reader->vtable_one = vtable_WBundleDataHandleReader_000_custom;
    reader->hardcoded_0A = 0x0A;
    reader->hardcoded_A3 = 0xA3;
    reader->vtable_two = vtable_WBundleDataHandleReader_010_custom;
    reader->p018 = nullptr;

    auto buffer = (WXBuffer<uint8_t>*) data;
    buffer->data = &data[sizeof(WBundleDataHandleReader)];
    buffer->length = size;

    reader->buffer = buffer;
    reader->read_cursor = 0;
    reader->read_limit = size;
    return reader;
  }

  return nullptr;
}

static WBundleDataHandleReader* hook_bundle_file_read(WBundleDiskFile* bundle_file, bool complex) {
  std::wstring full_path;
  bundle_format_file_directory(bundle_file->directory, full_path);
  full_path.append(bundle_file->file_name.text);

  logger::it->debug("Loading {} file {}: {}", complex, bundle_file->file_index, logger::wide(full_path));

  WXBundleFileMapping* mapping = bundle_file_mapping(bundle_file->file_index);
  WDiskBundle* bundle = bundle_file_identify(bundle_file->file_index);

  if (mapping != nullptr && bundle != nullptr) {
    logger::it->debug("... with mapping {} ->{}. Bundle {} named {}", bundle_file->file_index,
                      mapping->file_id, bundle->index,
                      logger::wide(bundle->absolute_path.text));
  }

  return nullptr;
  //return find_replacement(full_path);
}

static WBundleDataHandleReader* hook_bundle_file_read_simple(WBundleDiskFile* bundle_file) {
  return hook_bundle_file_read(bundle_file, false);
}

static WBundleDataHandleReader*  hook_bundle_file_read_complex(WBundleDiskFile* bundle_file) {
  return hook_bundle_file_read(bundle_file, true);
}

static void hook_set_bundle_file_read_simple(ExecutableAddressSpace &space, WrapperAddressSpace *wrapper_space) {
  ModifiableCode wrapper = wrapper_space->reserve(0x40);
  // push rcx
  wrapper.bytes({ 0x51 });
  // sub rsp, 20h (reserve shadow area + align)
  wrapper.bytes({ 0x48, 0x83, 0xEC, 0x20 });
  // mov rax, hook_func
  wrapper.bytes({ 0x48, 0xB8 });
  wrapper.u64((uint64_t) hook_bundle_file_read_simple);
  // call rax
  wrapper.bytes({ 0xFF, 0xD0 });
  // add rsp, 20h (remove shadow area)
  wrapper.bytes({ 0x48, 0x83, 0xC4, 0x20 });
  // pop rcx
  wrapper.bytes({ 0x59 });
  // test rax, rax
  wrapper.bytes({ 0x48, 0x85, 0xC0 });
  // jz $+1
  wrapper.bytes({ 0x74, 0x01 });
  // retn
  wrapper.bytes({ 0xC3 });
  // mov rax, [142AA43B8h] (overwritten)
  wrapper.bytes({ 0x48, 0x8B, 0x05 });
  wrapper.offset32(space.by_offset(0x2AA43B8));
  // jmp 1400929F7
  wrapper.bytes({ 0xE9 });
  wrapper.offset32(space.by_offset(0x929F7));

  ModifiableCode hook = space.modify_code(0x929F0, 0x10);
  hook.bytes({ 0xE9 });
  hook.offset32((uint64_t) wrapper.address);
  hook.bytes({ 0x90, 0x90 });
}

static void hook_set_bundle_file_read_complex(ExecutableAddressSpace &space, WrapperAddressSpace *wrapper_space) {
  ModifiableCode wrapper = wrapper_space->reserve(0x40);
  // push rcx
  wrapper.bytes({ 0x51 });
  // push rdx
  wrapper.bytes({ 0x52 });
  // push r8
  wrapper.bytes({ 0x41, 0x50 });
  // sub rsp, 20h (reserve shadow area + align)
  wrapper.bytes({ 0x48, 0x83, 0xEC, 0x20 });
  // mov rax, hook_bundle_file_read
  wrapper.bytes({ 0x48, 0xB8 });
  wrapper.u64((uint64_t) hook_bundle_file_read_complex);
  // call rax
  wrapper.bytes({ 0xFF, 0xD0 });
  // add rsp, 20h (remove shadow area)
  wrapper.bytes({ 0x48, 0x83, 0xC4, 0x20 });
  // pop r8
  wrapper.bytes({ 0x41, 0x58 });
  // pop rdx
  wrapper.bytes({ 0x5A });
  // pop rcx
  wrapper.bytes({ 0x59 });
  // test rax, rax
  wrapper.bytes({ 0x48, 0x85, 0xC0 });
  // jz $+1
  wrapper.bytes({ 0x74, 0x01 });
  // retn
  wrapper.bytes({ 0xC3 });
  // mov rax, [142AA43B8h] (overwritten)
  wrapper.bytes({ 0x48, 0x8B, 0x05 });
  wrapper.offset32(space.by_offset(0x2AA43B8));
  // jmp 1400929F7
  wrapper.bytes({ 0xE9 });
  wrapper.offset32(space.by_offset(0x92A27));

  ModifiableCode hook = space.modify_code(0x92A20, 0x10);
  hook.bytes({ 0xE9 });
  hook.offset32((uint64_t) wrapper.address);
  hook.bytes({ 0x90, 0x90 });
}

void bundles_setup(TcpServer* tcp_server, WrapperAddressSpace* wrapper_space) {
  ExecutableAddressSpace space;

  static_depot_pointer = (WDepot**) space.by_offset(0x2AA43B8);

  memcpy(vtable_WBundleDataHandleReader_000_custom, (void*) space.by_offset(0x1E13B88),
      sizeof(vtable_WBundleDataHandleReader_000_custom));
  memcpy(vtable_WBundleDataHandleReader_010_custom, (void*) space.by_offset(0x1E13C60),
      sizeof(vtable_WBundleDataHandleReader_010_custom));

  vtable_WBundleDataHandleReader_000_custom[0] = (void*) custom_WBundleDataHandleReader_deconstructor;

  hook_set_bundle_file_read_simple(space, wrapper_space);
  hook_set_bundle_file_read_complex(space, wrapper_space);

  tcp_server->add_handler(10, [] (uint16_t type, const std::vector<uint8_t>& message, const TcpMessageSender& sender) {
    std::string bundle_path("<not found>");
    std::string file_path("<not found>");

    uint32_t file_index = *(uint32_t*) &message[0];

    WXBundleManager& manager = *static_depot_pointer[0]->bundle_manager;

    if (file_index > 0 && file_index < manager.file_count) {
      WBundleDiskFile* file = manager.files[file_index];

      if (file != nullptr) {
        std::wstring full_path;
        bundle_format_file_directory(file->directory, full_path);
        full_path.append(file->file_name.text);
        file_path = logger::wide(full_path);
      }
    }

    WXBundleFileIndex& index = *manager.file_index;

    if (file_index < index.mapping_count) {
      WXBundleFileMapping& mapping = index.mappings[file_index];

      if (mapping.file_id != 0) {
        WXBundleFileLocation& location = index.locations[mapping.file_id];

        if (location.bundle_index < manager.bundle_count) {
          bundle_path = logger::wide(manager.bundles[location.bundle_index]->absolute_path.text);
        }
      }
    }

    const char* bundle_bytes = bundle_path.c_str();
    size_t bundle_bytes_length = strlen(bundle_bytes);

    const char* file_bytes = file_path.c_str();
    size_t file_bytes_length = strlen(file_bytes);

    std::vector<uint8_t> response(8 + bundle_bytes_length + file_bytes_length, 0);

    *(uint32_t*) &response[0] = bundle_bytes_length;
    memcpy(&response[4], bundle_bytes, bundle_bytes_length);
    *(uint32_t*) &response[4 + bundle_bytes_length] = file_bytes_length;
    memcpy(&response[8 + bundle_bytes_length], file_bytes, file_bytes_length);

    sender(11, response);
  });
}
<file_sep>/internal/src/emitters.cpp
#include "emitters.h"
#include "memory/executable_address_space.h"
#include "engine_types.h"
#include "logging/log.h"
#include "server/message_builder.h"
#include "bundles.h"
#include <fstream>

static void* vtable_WParticleEmitter = nullptr;
static void* vtable_WDependencyLoader = nullptr;

struct TrackedParticleEmitter {
  WParticleEmitter* emitter;
  uint32_t file_index;
};

static std::mutex emitter_lock;
static std::unordered_map<WParticleEmitter*, TrackedParticleEmitter> tracked_emitters;

static void hook_emitter_parse_data(WParticleEmitter* emitter, WDependencyLoader* loader) {
  if (emitter->vtable_one != vtable_WParticleEmitter || loader->vtable_one != vtable_WDependencyLoader) {
    logger::it->debug("CParticleEmitter parse call... with GC'd instances...");
    return;
  }

  {
    std::lock_guard<std::mutex> guard(emitter_lock);
    tracked_emitters[emitter] = { emitter, loader->file->file_index };
  }

  logger::it->debug("Parsed CParticleEmitter ({:x}) data from file {}", logger::ptr(emitter), loader->file->file_index);
}

static void hook_emitter_destruct(WParticleEmitter* emitter) {
  {
    std::lock_guard<std::mutex> guard(emitter_lock);
    tracked_emitters.erase(emitter);
  }

  logger::it->debug("Destroyed CParticleEmitter ({:x})", logger::ptr(emitter));
}

struct TrackedRenderParticleEmitter {
  WRenderParticleEmitter* render_emitter;
  WParticleEmitter* emitter;
  uint32_t file_index;
};

static std::unordered_map<WRenderParticleEmitter*, TrackedRenderParticleEmitter> tracked_render_emitters;

static void hook_render_emitter_register(WRenderParticleEmitter* render_emitter, WParticleEmitter* emitter) {
  {
    std::lock_guard<std::mutex> guard(emitter_lock);

    const auto& it = tracked_emitters.find(emitter);

    if (it != tracked_emitters.end()) {
      tracked_render_emitters[render_emitter] = {
          render_emitter,
          it->second.emitter,
          it->second.file_index
      };

      logger::it->debug("Setup CRenderParticleEmitter {:x} from {:x} file {}", logger::ptr(render_emitter),
                        logger::ptr(it->second.emitter), it->second.file_index);
    } else {
      logger::it->debug("Setup CRenderParticleEmitter {:x}, but no corresponding emitter", logger::ptr(render_emitter));
    }
  }
}

static void hook_render_emitter_destruct(WRenderParticleEmitter* render_emitter) {
  {
    std::lock_guard<std::mutex> guard(emitter_lock);
    tracked_render_emitters.erase(render_emitter);
  }

  logger::it->debug("Destroyed CRenderParticleEmitter {:x}", logger::ptr(render_emitter));
}

static void hook_set_emitter_register(ExecutableAddressSpace &space, WrapperAddressSpace *wrapper_space) {
  ModifiableCode wrapper = wrapper_space->reserve(0x40);
  // push rcx
  wrapper.bytes({ 0x51 });
  // push rdx
  wrapper.bytes({ 0x52 });
  // sub rsp, 28h (reserve shadow area + align)
  wrapper.bytes({ 0x48, 0x83, 0xEC, 0x28 });
  // mov rax, hook_func
  wrapper.bytes({ 0x48, 0xB8 });
  wrapper.u64((uint64_t) hook_emitter_parse_data);
  // call rax
  wrapper.bytes({ 0xFF, 0xD0 });
  // add rsp, 28h (remove shadow area)
  wrapper.bytes({ 0x48, 0x83, 0xC4, 0x28 });
  // pop rdx
  wrapper.bytes({ 0x5A });
  // pop rcx
  wrapper.bytes({ 0x59 });
  // jmp originalfunction
  wrapper.bytes({ 0xE9 });
  wrapper.offset32(space.by_offset(0x516490));

  ModifiableCode hook = space.modify_code(0x1F3F4D0, 0x08);
  hook.u64((uint64_t) wrapper.address);
}

static void hook_set_emitter_destruct(ExecutableAddressSpace& space, WrapperAddressSpace* wrapper_space) {
  ModifiableCode wrapper = wrapper_space->reserve(0x40);
  // push rcx
  wrapper.bytes({ 0x51 });
  // sub rsp, 20h (reserve shadow area + align)
  wrapper.bytes({ 0x48, 0x83, 0xEC, 0x20 });
  // mov rax, hook_func
  wrapper.bytes({ 0x48, 0xB8 });
  wrapper.u64((uint64_t) hook_emitter_destruct);
  // call rax
  wrapper.bytes({ 0xFF, 0xD0 });
  // add rsp, 20h (remove shadow area)
  wrapper.bytes({ 0x48, 0x83, 0xC4, 0x20 });
  // pop rcx
  wrapper.bytes({ 0x59 });
  // push rbx
  wrapper.bytes({ 0x40, 0x53 });
  // sub rsp, 20h (reserve shadow area + align)
  wrapper.bytes({ 0x48, 0x83, 0xEC, 0x20 });
  // jmp originalfunction
  wrapper.bytes({ 0xE9 });
  wrapper.offset32(space.by_offset(0x518FE6));

  ModifiableCode hook = space.modify_code(0x518FE0, 0x08);
  hook.bytes({ 0xE9 });
  hook.offset32((uint64_t) wrapper.address);
  hook.bytes({ 0x90 });
}

static void hook_set_render_emitter_register(ExecutableAddressSpace &space, WrapperAddressSpace *wrapper_space) {
  ModifiableCode wrapper = wrapper_space->reserve(0x40);
  // mov rcx, rsi
  wrapper.bytes({ 0x48, 0x8B, 0xCE });
  // mov rdx, r14
  wrapper.bytes({ 0x49, 0x8B, 0xD6 });
  // mov rax, hook_func
  wrapper.bytes({ 0x48, 0xB8 });
  wrapper.u64((uint64_t) hook_render_emitter_register);
  // call rax
  wrapper.bytes({ 0xFF, 0xD0 });
  // mov rax, [r15]
  wrapper.bytes({ 0x49, 0x8B, 0x07 });
  // mov rcx, r15
  wrapper.bytes({ 0x49, 0x8B, 0xCF });
  // jmp originalfunction
  wrapper.bytes({ 0xE9 });
  wrapper.offset32(space.by_offset(0xBFB04D));

  ModifiableCode hook = space.modify_code(0xBFB047, 0x08);
  hook.bytes({ 0xE9 });
  hook.offset32((uint64_t) wrapper.address);
  hook.bytes({ 0x90 });
}

static void hook_set_render_emitter_destruct(ExecutableAddressSpace &space, WrapperAddressSpace *wrapper_space) {
  ModifiableCode wrapper = wrapper_space->reserve(0x40);
  // push rcx
  wrapper.bytes({ 0x51 });
  // push rdx
  wrapper.bytes({ 0x52 });
  // sub rsp, 28h (reserve shadow area + align)
  wrapper.bytes({ 0x48, 0x83, 0xEC, 0x28 });
  // mov rax, hook_func
  wrapper.bytes({ 0x48, 0xB8 });
  wrapper.u64((uint64_t) hook_render_emitter_destruct);
  // call rax
  wrapper.bytes({ 0xFF, 0xD0 });
  // add rsp, 28h (remove shadow area)
  wrapper.bytes({ 0x48, 0x83, 0xC4, 0x28 });
  // pop rdx
  wrapper.bytes({ 0x5A });
  // pop rcx
  wrapper.bytes({ 0x59 });
  // jmp originalfunction
  wrapper.bytes({ 0xE9 });
  wrapper.offset32(space.by_offset(0xC08B20));

  ModifiableCode hook = space.modify_code(0x214B4B8, 0x08);
  hook.u64((uint64_t) wrapper.address);
}

static void message_emitter_list(uint16_t type, const std::vector<uint8_t> &message, const TcpMessageSender &sender) {
  std::vector<uint8_t> response;

  {
    std::lock_guard<std::mutex> guard(emitter_lock);

    uint32_t size = tracked_render_emitters.size();
    message_append(response, size);

    for (const auto& it : tracked_render_emitters) {
      auto address = (uint64_t) it.second.render_emitter;
      std::string name = fmt::format("{:016x}h", address);

      message_append_string(response, name);

      WBundleDiskFile* file = bundle_file_find(it.second.file_index);

      if (file != nullptr) {
        std::wstring directory;
        bundle_format_file_directory(file->directory, directory);

        message_append_string(response, directory);
        message_append_string(response, std::wstring(file->file_name.text));
      } else {
        message_append_string(response, "<unknown>");
        message_append_string(response, "<unknown>");
      }

      WDiskBundle* bundle = bundle_file_identify(it.second.file_index);

      if (bundle != nullptr) {
        message_append_string(response, std::wstring(bundle->absolute_path.text));
      } else {
        message_append_string(response, "<unknown>");
      }
    }
  }

  sender(6, response);
}

template <typename T>
static void encode_buffer(std::vector<uint8_t>& response, const WXBuffer<T>& buffer,
                          std::function<void(std::vector<uint8_t>&, const T&)> item_encoder) {

  if (buffer.length >= 64) {
    throw std::exception("Cannot encode buffer, too many items");
  }

  uint8_t length = (uint8_t) buffer.length;
  message_append(response, length);

  for (size_t i = 0; i < buffer.length; i++) {
    item_encoder(response, buffer.data[i]);
  }
}

static void encode_float(std::vector<uint8_t>& response, const float& value) {
  message_append(response, value);
}

static void encode_buffer(std::vector<uint8_t>& response, const WXBuffer<float>& buffer) {
  encode_buffer<float>(response, buffer, encode_float);
}

static void encode_vector3(std::vector<uint8_t>& response, const WXVector3& value) {
  message_append(response, value.x);
  message_append(response, value.y);
  message_append(response, value.z);
}

static void encode_buffer(std::vector<uint8_t>& response, const WXBuffer<WXVector3>& buffer) {
  encode_buffer<WXVector3>(response, buffer, encode_vector3);
}

static void encode_vector2(std::vector<uint8_t>& response, const WXVector2& value) {
  message_append(response, value.x);
  message_append(response, value.y);
}

static void encode_buffer(std::vector<uint8_t>& response, const WXBuffer<WXVector2>& buffer) {
  encode_buffer<WXVector2>(response, buffer, encode_vector2);
}

static void encode_emitter_data(std::vector<uint8_t>& response, const TrackedRenderParticleEmitter& tracked_emitter) {
  WRenderParticleEmitter& emitter = *tracked_emitter.render_emitter;

  message_append(response, emitter.initializer_bitset);
  message_append(response, emitter.modificator_bitset);

  WXParticleEmitterModuleData& data = emitter.emitter_data;
  encode_buffer(response, data.alpha);
  encode_buffer(response, data.color);
  encode_buffer(response, data.lifetime);
  encode_buffer(response, data.position);
  encode_float(response, data.position_offset);
  encode_buffer(response, data.rotation);
  encode_buffer(response, data.rotation_3d);
  encode_buffer(response, data.rotation_rate);
  encode_buffer(response, data.rotation_rate_3d);
  encode_buffer(response, data.size);
  encode_buffer(response, data.size_3d);
  message_append(response, data.size_keep_ratio);
  encode_buffer(response, data.spawn_extents);
  encode_buffer(response, data.spawn_inner_radius);
  encode_buffer(response, data.spawn_outer_radius);
  message_append(response, data.spawn_world_space);
  message_append(response, data.spawn_surface_only);
  encode_vector3(response, data.p0A8);

  for (float i : data.spawn_to_local_matrix) {
    encode_float(response, i);
  }

  encode_buffer(response, data.velocity);
  message_append(response, data.velocity_world_space);
  encode_buffer(response, data.velocity_inherit_scale);
  encode_buffer(response, data.velocity_spread_scale);
  message_append(response, data.velocity_spread_conserve_momentum);
  encode_buffer(response, data.texture_animation_initial_frame);
  message_append(response, data.p140);
  message_append(response, data.p144);
  message_append(response, data.p148);

  encode_buffer(response, data.velocity_over_life);
  encode_buffer(response, data.acceleration_direction);
  encode_buffer(response, data.acceleration_scale);
  encode_buffer(response, data.rotation_over_life);
  encode_buffer(response, data.rotation_rate_over_life);
  encode_buffer(response, data.rotation_3d_over_life);
  encode_buffer(response, data.rotation_rate_3d_over_life);
  encode_buffer(response, data.color_over_life);
  encode_buffer(response, data.alpha_over_life);
  encode_buffer(response, data.size_over_life);
  encode_buffer(response, data.size_over_life_orientation);
  encode_buffer(response, data.texture_animation_speed);
  encode_buffer(response, data.velocity_turbulize_scale);
  encode_buffer(response, data.velocity_turbulize_timelife_limit);
  encode_float(response, data.velocity_turbulize_noise_interval);
  encode_float(response, data.velocity_turbulize_duration);
  encode_buffer(response, data.target_force_scale);
  encode_buffer(response, data.target_kill_radius);
  encode_float(response, data.target_max_force);
  encode_buffer(response, data.target_position);
  message_append(response, data.spawn_positive_x);
  message_append(response, data.spawn_negative_x);
  message_append(response, data.spawn_positive_y);
  message_append(response, data.spawn_negative_y);
  message_append(response, data.spawn_position_z);
  message_append(response, data.spawn_negative_z);
  message_append(response, data.spawn_velocity);
  message_append(response, data.collision_triggering_group_index);
  encode_float(response, data.collision_dynamic_friction);
  encode_float(response, data.collision_static_friction);
  encode_float(response, data.collision_restitution);
  encode_float(response, data.collision_velocity_dampening);
  message_append(response, data.collision_disable_gravity);
  message_append(response, data.collision_use_gpu);
  encode_float(response, data.collision_radius);
  message_append(response, data.collision_kill_when_collide);
  message_append(response, data.collision_self_emitter_index);
  encode_float(response, data.collision_spawn_probability);
  message_append(response, data.collision_spawn_parent_emitter_index);
  encode_float(response, data.alpha_by_distance_far);
  encode_float(response, data.alpha_by_distance_near);
}

static void message_emitter_details(uint16_t type, const std::vector<uint8_t> &message, const TcpMessageSender &sender) {
  std::vector<uint8_t> response;

  if (message.size() < 4) {
    sender(2, response);
    return;
  }

  size_t name_length = *(uint32_t*) &message[0];

  if (message.size() != 4 + name_length) {
    sender(2, response);
    return;
  }

  std::string requested_name((char*) &message[4], name_length);

  {
    std::lock_guard<std::mutex> guard(emitter_lock);

    for (const auto& it : tracked_render_emitters) {
      auto address = (uint64_t) it.second.render_emitter;
      std::string name = fmt::format("{:016x}h", address);

      if (name == requested_name) {
        response.push_back(1);

        try {
          encode_emitter_data(response, it.second);
          break;
        } catch (const std::exception& error) {
          response.clear();
          sender(2, response);
          return;
        }
      }
    }
  }

  if (response.empty()) {
    response.push_back(0);
  }

  sender(8, response);
}

void emitters_setup(TcpServer* tcp_server, WrapperAddressSpace* wrapper_space) {
  ExecutableAddressSpace space;

  vtable_WParticleEmitter = (void*) space.by_offset(0x1F3F478);
  vtable_WDependencyLoader = (void*) space.by_offset(0x1DDAD78);

  // Registers a WParticleEmitter after it has
  hook_set_emitter_register(space, wrapper_space);
  // Removes dead WParticleEmitters from tracking
  hook_set_emitter_destruct(space, wrapper_space);
  // Registers a WRenderParticleEmitter with data from associated WParticleEmitter
  hook_set_render_emitter_register(space, wrapper_space);
  // Removes dead WRenderParticleEmitter from tracking
  hook_set_render_emitter_destruct(space, wrapper_space);

  tcp_server->add_handler(5, message_emitter_list);
  tcp_server->add_handler(7, message_emitter_details);
}

typedef void (*emitter_config_parser_fn)(WMemoryFileReader* reader, WXParticleEmitterModuleData* something);

static void overwrite_all_emitters() {
  std::vector<char> config;

  {
    std::ifstream file(R"(C:\Projects\witch\fileoverride\logx\emitter.bin)", std::ios::binary);
    config.insert(config.begin(), std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
  }

  ExecutableAddressSpace space;
  auto parser = (emitter_config_parser_fn) space.by_offset(0x518AE0);

  WMemoryFileReader reader {
      (void*) space.by_offset(0x1CFC3E8),
      0x100052,
      0xA3,
      (void*) space.by_offset(0x1CFC4C0),
      (uint8_t*) config.data(),
      config.size(),
      0
  };

  std::lock_guard<std::mutex> guard(emitter_lock);

  for (const auto& it : tracked_render_emitters) {
    reader.position = 0;
    parser(&reader, &it.second.render_emitter->emitter_data);
  }
}

static bool last_state = false;

void emitters_loop() {
  /*
  bool current_state = (GetKeyState(VK_INSERT) & 0x8000) != 0;

  if (current_state != last_state) {
    last_state = current_state;

    if (current_state) {
      overwrite_all_emitters();
    }
  }
   */
}
<file_sep>/internal/src/logging/log.h
#pragma once

#define FMT_HEADER_ONLY
#include <spdlog/spdlog.h>

namespace logger {
  void setup_logger();

  std::string wide(const std::wstring& value);
  std::string wide(const wchar_t* value);

  template <typename T>
  inline uint64_t ptr(const T *p) { return (uint64_t) p; }

  extern std::shared_ptr<spdlog::logger> it;
}
<file_sep>/internal/src/bundles.h
#pragma once

#include "engine_types.h"
#include "server/tcp_server.h"
#include "memory/wrapper_address_space.h"
#include <cstdint>
#include <string>

WBundleDiskFile* bundle_file_find(uint32_t file_index);
WDiskBundle* bundle_file_identify(uint32_t file_index);
void bundle_format_file_directory(WDirectory *directory, std::wstring &path);

void bundles_setup(TcpServer* tcp_server, WrapperAddressSpace* wrapper_space);
<file_sep>/internal/src/memory/executable_address_space.h
#pragma once

#include <cstdint>
#include "../windows_api.h"
#include "wrapper_address_space.h"
#include "modifiable_code.h"

class ExecutableAddressSpace {
public:
  ExecutableAddressSpace() {
    base_address = (uint64_t) GetModuleHandleW(nullptr);
  }

  uint64_t by_offset(uint64_t offset) {
    return base_address + offset;
  }

  ModifiableCode modify_code(uint64_t offset, uint64_t length) {
    return ModifiableCode(base_address + offset, length);
  }

  WrapperAddressSpace create_wrapper_space() {
    for (uint64_t i = 1; i < 0x200; i++) {
      uint64_t offset = i * 0x10000;

      if (offset >= base_address) {
        break;
      }

      void* result = VirtualAlloc((void*) (base_address - offset), 0x10000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READ);

      if (result != nullptr) {
        return WrapperAddressSpace((uint64_t) result, 0x10000);
      }
    }

    throw std::exception("Unable to find vacant memory before executable region for wrappers.");
  }

  uint64_t base_address;
};
<file_sep>/internal/src/logging/log.cpp
#include "log.h"

#include "../windows_api.h"
#include <Psapi.h>
#include <filesystem>
#include <time.h>

namespace fs = std::experimental::filesystem;

#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/async.h>

namespace logger {
  static std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> wide_converter;

  std::string wide(const std::wstring& value) {
    return std::string(wide_converter.to_bytes(value));
  }

  std::string wide(const wchar_t* value) {
    return std::string(wide_converter.to_bytes(value));
  }

  static void setup_logger_throw() {
    HMODULE module;
    wchar_t dll_path_raw[MAX_PATH];

    if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR) &it, &module)) {
      throw std::exception("Could not get self module.");
    } else if (GetModuleFileNameW(module, dll_path_raw, sizeof(dll_path_raw)) == 0) {
      throw std::exception("Could not get self module path.");
    }

    fs::path path(dll_path_raw);
    fs::path log_directory = fs::absolute(path.parent_path().parent_path() / "log");

    std::error_code creation_error;

    if (!fs::create_directories(log_directory, creation_error)) {
      if (creation_error.value() != 0) {
        throw std::exception("Failed to create logs directory.");
      }
    }

    fs::path log_file = log_directory / ("debug_" + std::to_string(time(nullptr)) + ".log");

    auto currentLog = spdlog::create_async_nb<spdlog::sinks::basic_file_sink_mt>("main", log_file.string());

    spdlog::flush_every(std::chrono::seconds(2));

    currentLog->flush_on(spdlog::level::warn);
    currentLog->set_level(spdlog::level::trace);
    it = std::move(currentLog);
  }

  void setup_logger() {
    try {
      setup_logger_throw();
    } catch (const std::exception& e) {
      MessageBoxA(nullptr, e.what(), "Log initialization error.", MB_OK);
      it->error("Log initialization failed: {}", e.what());
    }
  }

  static std::shared_ptr<spdlog::logger> primordial_logger() {
    return spdlog::stdout_logger_mt("primordial");
  }

  std::shared_ptr<spdlog::logger> it = primordial_logger();
}
<file_sep>/internal/src/memory/wrapper_address_space.h
#pragma once

#include <cstdint>
#include "modifiable_code.h"

class WrapperAddressSpace {
public:
  WrapperAddressSpace(uint64_t base_address, size_t length) {
    this->base_address = base_address;
    this->unused_offset = 0;
    this->length = length;
  }

  WrapperAddressSpace(WrapperAddressSpace&& other) noexcept {
    base_address = other.base_address;
    unused_offset = other.unused_offset;
    length = other.length;

    other.base_address = 0;
  }

  ~WrapperAddressSpace() {
    if (base_address != 0) {
      VirtualFree((void*) base_address, 0, MEM_RELEASE);
      base_address = 0;
    }
  }

  ModifiableCode reserve(size_t segment_length) {
    if (base_address == 0 || unused_offset + segment_length > length) {
      throw std::exception("Wrapper address space is exhausted");
    }

    ModifiableCode code(base_address + unused_offset, segment_length);
    unused_offset += segment_length;
    return code;
  }

private:
  uint64_t base_address;
  size_t unused_offset;
  size_t length;
};
<file_sep>/CMakeLists.txt
cmake_minimum_required (VERSION 3.13)
project (sandbox)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF")

include(${CMAKE_ROOT}/Modules/FetchContent.cmake)

set(DEPENDENCY_DIR "${PROJECT_SOURCE_DIR}/dependencies")

FetchContent_Populate(spdlog SOURCE_DIR ${DEPENDENCY_DIR}/spdlog
  GIT_REPOSITORY https://github.com/gabime/spdlog.git
  GIT_TAG 74dbf4cf702b49c98642c9afe74d114a238a6a07
)

add_subdirectory (launcher)
add_subdirectory (internal)
<file_sep>/launcher/src/main.cpp
#include <Windows.h>
#include <Psapi.h>
#include <atomic>
#include <string>

typedef HRESULT (*fn_DirectInput8Create) (HINSTANCE moduleInstance, DWORD version, REFIID desiredInterface,
                                          LPVOID* result, LPUNKNOWN interfaceAggregation);

typedef void (*fn_GetStartupInfoW) (STARTUPINFOW* startup_info_out);

typedef void (*fn_initialize_mod) ();

class DllLoader;

static DllLoader* global_loader;

class DllLoader {
public:
  void preload() {
    HMODULE original_library = LoadLibraryW(L"C:\\Windows\\System32\\dinput8.dll");
    original_DirectInput8Create = (fn_DirectInput8Create) GetProcAddress(original_library, "DirectInput8Create");
    original_GetStartupInfoW = (fn_GetStartupInfoW) GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "GetStartupInfoW");

    auto module_data = (uint8_t*) GetModuleHandleW(nullptr);
    auto headers_dos = (IMAGE_DOS_HEADER*) &module_data[0];
    auto headers_nt = (IMAGE_NT_HEADERS*) &module_data[headers_dos->e_lfanew];
    IMAGE_DATA_DIRECTORY& imports_entry = headers_nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    auto import_dlls = (IMAGE_IMPORT_DESCRIPTOR*) &module_data[imports_entry.VirtualAddress];
    size_t import_dll_count = imports_entry.Size / sizeof(*import_dlls) - 1;

    for (size_t i = 0; i < import_dll_count; i++) {
      auto dll_name = (const char*) &module_data[import_dlls[i].Name];

      if (_stricmp(dll_name, "kernel32.dll") == 0) {
        auto functions = (uintptr_t*) &module_data[import_dlls[i].FirstThunk];
        auto functions_end = (uintptr_t*) &module_data[import_dlls[i + 1].FirstThunk];
        size_t function_count = functions_end - functions;

        for (size_t j = 0; j < function_count; j++) {
          if (functions[j] == (uintptr_t) original_GetStartupInfoW) {
            DWORD previous_flags;
            VirtualProtect(&functions[j], 0x08, PAGE_READWRITE, &previous_flags);

            functions[j] = (uintptr_t) DllLoader::static_hooked_GetStartupInfoW;

            VirtualProtect(&functions[j], 0x08, previous_flags, &previous_flags);
          }
        }
      }
    }
  }

  void check_load() {
    bool expected = false;

    if (loaded.compare_exchange_strong(expected, true)) {
      load();
    }
  }

  fn_DirectInput8Create get_original_dinput8() {
    check_load();
    return original_DirectInput8Create;
  }

private:
  void load() {
    load_mods_dlls(get_mods_directory());
  }

  void load_mods_dlls(const std::wstring& mods_directory) {
    WIN32_FIND_DATAW find_item;

    std::wstring search_pattern = mods_directory + L"*";
    HANDLE find_handle = FindFirstFileW(search_pattern.c_str(), &find_item);
    bool has_entry = find_handle != INVALID_HANDLE_VALUE;

    while (has_entry) {
      if ((find_item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 && _wcsnicmp(find_item.cFileName, L"mod", 3) == 0) {
        load_mod_dlls(mods_directory + find_item.cFileName);
      }

      has_entry = FindNextFileW(find_handle, &find_item);
    }

    FindClose(find_handle);
  }

  void load_mod_dlls(std::wstring mod_directory) {
    mod_directory.append(L"\\dlls\\");

    std::wstring search_pattern = mod_directory + L"*";

    WIN32_FIND_DATAW item;
    HANDLE handle = FindFirstFileW(search_pattern.c_str(), &item);
    bool has_entry = handle != INVALID_HANDLE_VALUE;

    while (has_entry) {
      if ((item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 && wcslen(item.cFileName) >= 5 &&
          _wcsnicmp(&item.cFileName[wcslen(item.cFileName) - 4], L".dll", 4) == 0) {

        std::wstring dll_path = mod_directory + item.cFileName;
        HMODULE module = LoadLibraryW(dll_path.c_str());

        if (module != nullptr) {
          auto initializer = (fn_initialize_mod) GetProcAddress(module, "InitializeMod");

          if (initializer != nullptr) {
            initializer();
          }
        }
      }

      has_entry = FindNextFileW(handle, &item);
    }

    FindClose(handle);
  }

  std::wstring get_mods_directory() {
    std::wstring path(get_executable_path());

    if (path.empty()) {
      return L"";
    }

    auto position = path.find_last_of('\\');

    if (position == std::wstring::npos) {
      return L"";
    }

    path.resize(position + 1);

    path.append(L"..\\..\\Mods\\");

    wchar_t resolved_path[4096];

    if (GetFullPathNameW(path.c_str(), 4096, resolved_path, nullptr) == 0) {
      return L"";
    }

    return resolved_path;
  }

  std::wstring get_executable_path() {
    wchar_t exe_full_path[4096];

    if (GetModuleFileNameW(GetModuleHandleW(nullptr), exe_full_path, 4096)) {
      return exe_full_path;
    } else {
      return L"";
    }
  }

  void hooked_GetStartupInfoW(STARTUPINFOW* startup_info_out) {
    check_load();
    original_GetStartupInfoW(startup_info_out);
  }

  static void static_hooked_GetStartupInfoW(STARTUPINFOW* startup_info_out) {
    global_loader->hooked_GetStartupInfoW(startup_info_out);
  }

  std::atomic<bool> loaded { false };

  fn_GetStartupInfoW original_GetStartupInfoW = nullptr;
  fn_DirectInput8Create original_DirectInput8Create = nullptr;
};

extern "C" __declspec(dllexport) HRESULT DirectInput8Create(
    HINSTANCE moduleInstance, DWORD version, REFIID desiredInterface, LPVOID* result, LPUNKNOWN interfaceAggregation) {

  return global_loader->get_original_dinput8()(moduleInstance, version, desiredInterface, result, interfaceAggregation);
}

std::atomic<bool> preloaded { false };

BOOL APIENTRY DllMain(HANDLE dllInstanceHandle, DWORD callReason, LPVOID reserved) {
  if (callReason == DLL_PROCESS_ATTACH) {
    bool expected = false;

    if (preloaded.compare_exchange_strong(expected, true)) {
      global_loader = new DllLoader;
      global_loader->preload();
    }
  }

  return TRUE;
}
<file_sep>/internal/src/engine_types.cpp
#include "engine_types.h"
<file_sep>/internal/src/engine_types.h
#pragma once

#include <cstdint>

#define _pad(x,y) uint8_t x[y]

#pragma pack(push, 1)

struct WXString {
  /* 000h */ char* text;
  // Including null terminator
  /* 008h */ uint32_t buffer_length;
  /* 00Ch SIZE */
};

struct WXWideString {
  /* 000h */ wchar_t* text;
  // Including null terminator
  /* 008h */ uint32_t buffer_length;
  /* 00Ch SIZE */
};

template <typename T>
struct WXSortedListEntry {
  /* 000h */ uint32_t maybe_hash;
  /* 004h */ _pad(p004, 0x04);
  /* 008h */ T* value;
  /* 010h SIZE */
};

template <typename T>
struct WXSortedList {
  // Capacity is determined with an equivalent to msize
  /* 000h */ WXSortedListEntry<T>* entries;
  /* 008h */ uint32_t count;
  /* 00Ch */ uint8_t is_dirty;
  /* 00Dh */ _pad(p00D, 0x03);
  /* 010h SIZE */
};

struct WBundleDiskFile;

struct WDirectory {
  /* 000h */ void* vtable;
  /* 008h */ WXSortedList<WBundleDiskFile> files;
  /* 018h */ WXSortedList<WDirectory> children;
  /* 028h */ WDirectory* parent;
  /* 030h */ WXWideString name;
  /* 03Ch */ _pad(p03C, 0x14);
  /* 050h SIZE */
};

struct WBundleDiskFile {
  /* 000h */ void* vtable;
  /* 008h */ WDirectory* directory;
  /* 010h */ void* whatever;
  /* 018h */ WXWideString file_name;
  /* 024h */ _pad(p024, 0x1C);
  /* 040h */ uint32_t file_index;
  /* 044h */
};

struct WXVector3 {
  /* 000h */ float x;
  /* 004h */ float y;
  /* 008h */ float z;
  /* 00Ch SIZE */
};

struct WXVector2 {
  /* 000h */ float x;
  /* 004h */ float y;
  /* 008h SIZE */
};

template <typename T>
struct WXBuffer {
  /* 000h */ T* data;
  /* 008h */ uint32_t length;
  /* 00Ch SIZE */
};

struct WXParticleEmitterModuleData {
  // CParticleInitializerAlpha
  /* 000h */ WXBuffer<float> alpha;
  // CParticleInitializerColor
  /* 00Ch */ WXBuffer<WXVector3> color;
  // CParticleInitializerLifeTime
  /* 018h */ WXBuffer<float> lifetime;
  // CParticleInitializerPosition
  /* 024h */ WXBuffer<WXVector3> position;
  /* 030h */ float position_offset;
  // CParticleInitializerRotation
  /* 034h */ WXBuffer<float> rotation;
  // CParticleInitializerRotation3D
  /* 040h */ WXBuffer<WXVector3> rotation_3d;
  // CParticleInitializerRotationRate
  /* 04Ch */ WXBuffer<float> rotation_rate;
  // CParticleInitializerRotationRate3D
  /* 058h */ WXBuffer<WXVector3> rotation_rate_3d;
  // CParticleInitializerSize and CParticleInitializerSize3d
  /* 064h */ WXBuffer<WXVector2> size;
  /* 070h */ WXBuffer<WXVector3> size_3d;
  /* 07Ch */ uint8_t size_keep_ratio;
  /* 07Dh */ _pad(p07D, 3);
  // CParticleInitializerSpawnBox and CParticleInitializerSpawnCircle
  /* 080h */ WXBuffer<WXVector3> spawn_extents;
  /* 08Ch */ WXBuffer<float> spawn_inner_radius;
  /* 098h */ WXBuffer<float> spawn_outer_radius;
  /* 0A4h */ uint8_t spawn_world_space;
  /* 0A5h */ uint8_t spawn_surface_only;
  /* 0A6h */ _pad(p0A6, 2);
  /* 0A8h */ WXVector3 p0A8;
  /* 0B4h */ _pad(p0B4, 12);
  /* 0C0h */ float spawn_to_local_matrix[16];
  /* 100h */ uint8_t spawn_positive_x;
  /* 101h */ uint8_t spawn_negative_x;
  /* 102h */ uint8_t spawn_positive_y;
  /* 103h */ uint8_t spawn_negative_y;
  /* 104h */ uint8_t spawn_position_z;
  /* 105h */ uint8_t spawn_negative_z;
  /* 106h */ uint8_t spawn_velocity;
  /* 107h */ _pad(p107, 1);
  // CParticleInitializerVelocity
  /* 108h */ WXBuffer<WXVector3> velocity;
  /* 114h */ uint8_t velocity_world_space;
  /* 115h */ _pad(p115, 3);
  /* 118h */ WXBuffer<float> velocity_inherit_scale;
  /* 124h */ WXBuffer<float> velocity_spread_scale;
  /* 130h */ uint8_t velocity_spread_conserve_momentum;
  /* 131h */ _pad(p131, 3);
  /* 134h */ WXBuffer<float> texture_animation_initial_frame;
  /* 140h */ uint32_t p140; // or float
  /* 144h */ uint32_t p144; // or float
  /* 148h */ uint32_t p148; // or float
  // CParticleModificatorVelocityOverLife (modulate defined by flags, absolute unspecified?)
  /* 14Ch */ WXBuffer<WXVector3> velocity_over_life;
  // CParticleModificatorAcceleration (world_space defined by flags)
  /* 158h */ WXBuffer<WXVector3> acceleration_direction;
  /* 164h */ WXBuffer<float> acceleration_scale;
  // CParticleModificatorRotationOverLife (modulate defined by flags)
  /* 170h */ WXBuffer<float> rotation_over_life;
  // CParticleModificatorRotationRateOverLife (modulate defined by flags)
  /* 17Ch */ WXBuffer<float> rotation_rate_over_life;
  // CParticleModificatorRotation3DOverLife (modulate defined by flags)
  /* 188h */ WXBuffer<WXVector3> rotation_3d_over_life;
  // CParticleModificatorRotationRate3DOverLife
  /* 194h */ WXBuffer<WXVector3> rotation_rate_3d_over_life;
  // CParticleModificatorColorOverLife (modulate defined by flags)
  /* 1A0h */ WXBuffer<WXVector3> color_over_life;
  // CParticleModificatorAlphaOverLife (modulate defined by flags)
  /* 1ACh */ WXBuffer<float> alpha_over_life;
  // CParticleModificatorSizeOverLife / CParticleModificatorSize3DOverLife (modulate defined by flags)
  /* 1B8h */ WXBuffer<WXVector2> size_over_life;
  // ... if drawer is CParticleDrawerEmitterOrientation (in compact, defined by flags)
  /* 1C4h */ WXBuffer<WXVector3> size_over_life_orientation;
  // CParticleModificatorTextureAnimation (initial frame is... missing? animation mode is defined by flags)
  /* 1D0h */ WXBuffer<float> texture_animation_speed;
  // CParticleModificatorVelocityTurbulize
  /* 1DCh */ WXBuffer<WXVector3> velocity_turbulize_scale;
  /* 1E8h */ WXBuffer<float> velocity_turbulize_timelife_limit;
  /* 1F4h */ float velocity_turbulize_noise_interval;
  /* 1F8h */ float velocity_turbulize_duration;
  // CParticleModificatorTarget and CParticleModificatorTargetNode (which is applied defined by flags)
  /* 1FCh */ WXBuffer<float> target_force_scale;
  /* 208h */ WXBuffer<float> target_kill_radius;
  /* 214h */ float target_max_force;
  /* 218h */ WXBuffer<WXVector3> target_position;
  /* 224h */ float collision_spawn_probability;
  /* 228h */ uint32_t collision_spawn_parent_emitter_index;
  /* 22Ch */ _pad(p22C, 4);
  /* 230h */ uint64_t collision_triggering_group_index;
  /* 238h */ float collision_dynamic_friction;
  /* 23Ch */ float collision_static_friction;
  /* 240h */ float collision_restitution;
  /* 244h */ float collision_velocity_dampening;
  /* 248h */ uint8_t collision_disable_gravity;
  /* 249h */ uint8_t collision_use_gpu;
  /* 24Ah */ _pad(p24A, 2);
  /* 24Ch */ float collision_radius;
  /* 250h */ uint8_t collision_kill_when_collide;
  /* 251h */ _pad(p251, 3);
  /* 254h */ uint32_t collision_self_emitter_index;
  // CParticleModificatorAlphaByDistance
  /* 258h */ float alpha_by_distance_far;
  /* 25Ch */ float alpha_by_distance_near;
  /* 260h SIZE */
};

struct WRenderParticleEmitter {
  /* 000h */ _pad(p000, 0x10);
  /* 010h */ WXParticleEmitterModuleData emitter_data;
  /* 270h */ _pad(p270, 0x74);
  /* 2E4h */ uint32_t modificator_bitset;
  /* 2E8h */ _pad(p2E8, 0x0C);
  /* 2F4h */ uint32_t initializer_bitset;
  /* 2F8h */
};

struct WName {
  /* 000h */ uint32_t offset;
  /* 004h SIZE */
};

struct WBundleDataHandleReader {
  /* 000h */ void* vtable_one;
  /* 008h */ uint32_t hardcoded_0A;
  /* 00Ch */ uint32_t hardcoded_A3;
  /* 010h */ void* vtable_two;
  /* 018h */ void* p018;
  /* 020h */ WXBuffer<uint8_t>* buffer;
  /* 028h */ uint32_t read_cursor;
  /* 02Ch */ uint32_t read_limit;
  /* 030h */
};

struct WMemoryFileReader {
  /* 000h */ void* vtable_one;
  /* 008h */ uint32_t hardcoded_100052;
  /* 00Ch */ uint32_t hardcoded_A3;
  /* 010h */ void* vtable_two;
  /* 018h */ uint8_t* buffer;
  /* 020h */ uint64_t size;
  /* 028h */ uint64_t position;
  /* 030h SIZE */
};

struct WXBundleFileMapping {
  /* 000h */ _pad(p000, 0x10);
  /* 010h */ uint32_t file_id;
  /* 014h */ _pad(p014, 0x0C);
  /* 020h SIZE */
};

struct WXBitSet {
  /* 000h */ uint32_t* words;
  /* 008h */ uint32_t word_count;
  /* 00Ch */ uint32_t bit_count;
  /* 010h SIZE */
};

struct WXBundleFileLocation {
  /* 000h */ uint32_t file_index;
  /* 004h */ uint32_t bundle_index;
  /* 008h */ uint32_t p008;
  /* 00Ch */ uint32_t p00C;
  /* 010h */ uint32_t fallback_file_id_maybe;
  /* 014h SIZE */
};

struct WXBundleFileIndex {
  /* 000h */ _pad(p000, 0x30);
  /* 030h */ WXBundleFileMapping* mappings;
  /* 038h */ uint32_t mapping_count;
  /* 03Ch */ WXBundleFileLocation* locations;
};

struct WDiskBundle {
  /* 000h */ void* vtable;
  /* 008h */ WXString name;
  /* 014h */ WXString relative_path;
  /* 020h */ uint32_t index;
  /* 024h */ _pad(p024, 0x0C);
  /* 030h */ WXWideString absolute_path;
  /* 03Ch */
};

struct WXBundleManager {
  /* 000h */ _pad(p000, 0x10);
  /* 010h */ WXBundleFileIndex* file_index;
  /* 018h */ WXBitSet bundle_bitset_1;
  /* 028h */ WXBitSet bundle_bitset_2;
  /* 038h */ WXBitSet bundle_bitset_3;
  /* 048h */ _pad(p048, 0x08);
  /* 050h */ WDiskBundle** bundles;
  /* 058h */ int32_t bundle_count;
  /* 05Ch */ WBundleDiskFile** files;
  /* 064h */ int32_t file_count;
  /* 068h */
};

struct WDepot {
  /* 000h */ WDirectory base;
  /* 050h */ void* vtable_two;
  /* 058h */ _pad(p058, 0x100);
  /* 158h */ WXBundleManager* bundle_manager;
  /* 160h */
};

typedef void WIRTTIType;

struct WIReferencable {
  /* 000h */ void* vtable;
  /* 008h */ void* something;
  /* 010h SIZE */
};

struct WISerializable {

};

struct WProperty {
  /* 000h */ void* vtable;
  /* 008h */ WIRTTIType* rtti_type;
  /* 010h */ _pad(p008, 0x10);
  /* 020h */ uint32_t offset;
  /* 024h */ uint32_t flags;
  /* 028h */ _pad(p028, 0x08);
  /* 030h SIZE */
};

struct WParticleEmitter {
  /* 000h */ void* vtable_one;
  /* 008h */ _pad(p008, 0x110);
  /* 118h SIZE */
};

struct WDependencyLoader {
  /* 000h */ void* vtable_one;
  /* 008h */ _pad(p008, 0x10);
  /* 018h */ WBundleDiskFile* file;
  /* 020h */
};

#pragma pack(pop)
<file_sep>/internal/src/emitters.h
#pragma once

#include "memory/wrapper_address_space.h"
#include "server/tcp_server.h"

void emitters_setup(TcpServer* tcp_server, WrapperAddressSpace* wrapper_space);
void emitters_loop();
<file_sep>/launcher/CMakeLists.txt
add_library(launcher SHARED
  src/main.cpp
)
<file_sep>/internal/src/main.cpp
#include "memory/wrapper_address_space.h"
#include "server/tcp_server.h"
#include "emitters.h"
#include "logging/log.h"
#include "memory/executable_address_space.h"
#include "bundles.h"

static WrapperAddressSpace* wrapper_space;
static TcpServer* tcp_server;

static void loop_hook() {
  emitters_loop();
}

extern "C" __declspec(dllexport) void InitializeMod() {
  logger::setup_logger();
  logger::it->info("Beginning initialization");

  tcp_server = tcp_server_create(3548);
  tcp_server->start();

  ExecutableAddressSpace space;
  wrapper_space = new WrapperAddressSpace(space.create_wrapper_space());

  emitters_setup(tcp_server, wrapper_space);
  bundles_setup(tcp_server, wrapper_space);

  {
    ModifiableCode wrapper = wrapper_space->reserve(0x40);
    // mov rax, loop_hook
    wrapper.bytes({ 0x48, 0xB8 });
    wrapper.u64((uint64_t) loop_hook);
    // call rax
    wrapper.bytes({ 0xFF, 0xD0 });
    // mov rax, [rbx]
    wrapper.bytes({ 0x48, 0x8B, 0x03 });
    // mov rcx, rbx
    wrapper.bytes({ 0x48, 0x8B, 0xCB });
    // jmp 1400446B6
    wrapper.bytes({ 0xE9 });
    wrapper.offset32(space.by_offset(0x446B6));

    ModifiableCode hook = space.modify_code(0x446B0, 0x06);
    hook.bytes({ 0xE9 });
    hook.offset32((uint64_t) wrapper.address);
    hook.bytes({ 0x90 });
  }

  {
    *(uint32_t*) space.by_offset(0x2CB4C20) = 0x200;
  }
}
<file_sep>/internal/CMakeLists.txt
add_library(internal SHARED
  src/main.cpp
  src/engine_types.h
  src/engine_types.cpp
  src/memory/modifiable_code.h
  src/memory/wrapper_address_space.h
  src/memory/executable_address_space.h
  src/server/message_builder.h
  src/server/tcp_server.cpp
  src/server/tcp_server.h
  src/logging/log.cpp
  src/logging/log.h
  src/emitters.cpp
  src/emitters.h
  src/bundles.cpp
  src/bundles.h
)

target_include_directories(internal PRIVATE ${PROJECT_SOURCE_DIR}/dependencies/spdlog/include)
target_link_libraries(internal ws2_32)
target_compile_options(internal PRIVATE /MT)
<file_sep>/internal/src/memory/modifiable_code.h
#pragma once

#include <cstdint>
#include <exception>
#include "../windows_api.h"

class ModifiableCode {
public:
  ModifiableCode(uint64_t address, uint64_t length) {
    this->address = (uint8_t*) address;
    this->length = length;
    this->write_offset = 0;

    VirtualProtect((void*) this->address, this->length, PAGE_READWRITE, (DWORD*) &previous_protection);
  }

  ModifiableCode(ModifiableCode&& other) noexcept {
    address = other.address;
    length = other.length;
    previous_protection = other.previous_protection;
    write_offset = other.write_offset;

    other.address = nullptr;
  }

  ~ModifiableCode() {
    if (this->address != nullptr) {
      VirtualProtect((void *) this->address, this->length, previous_protection, (DWORD *) &previous_protection);
    }
  }

  void bytes(std::initializer_list<uint8_t> bytes) {
    check_writable(bytes.size());

    for (uint8_t byte : bytes) {
      address[write_offset++] = byte;
    }
  }

  void u32(uint32_t value) {
    check_writable(sizeof(uint32_t));

    *(uint32_t*) (&address[write_offset]) = value;
    write_offset += sizeof(uint32_t);
  }

  void u64(uint64_t value) {
    check_writable(sizeof(uint64_t));

    *(uint64_t*) (&address[write_offset]) = value;
    write_offset += sizeof(uint64_t);
  }

  void offset32(uint64_t target) {
    uint64_t source = ((uint64_t) address) + write_offset + 4;
    u32((uint32_t) (target - source));
  }

  uint8_t* address;

private:
  void check_writable(size_t byte_count) {
    if (byte_count + write_offset > length) {
      throw std::exception("Writing out of bounds of modifiable region.");
    }
  }

  uint64_t length;
  uint32_t previous_protection {};
  uint64_t write_offset;
};
<file_sep>/internal/src/server/message_builder.h
#pragma once

#include <vector>
#include <cstdint>
#include "../logging/log.h"

template <class Type>
std::vector<uint8_t>& message_append(std::vector<uint8_t>& message, const Type& value) {
  auto value_array = reinterpret_cast<const uint8_t*>(&value);
  message.insert(message.end(), value_array, value_array + sizeof(value));
  return message;
}

std::vector<uint8_t>& message_append(std::vector<uint8_t>& message, const void* value, size_t length) {
  auto value_array = reinterpret_cast<const uint8_t*>(value);
  message.insert(message.end(), value_array, value_array + length);
  return message;
}

std::vector<uint8_t>& message_append_string(std::vector<uint8_t>& message, const std::string& string) {
  uint32_t length = string.length();
  message_append(message, length);
  message_append(message, string.c_str(), length);
  return message;
}

std::vector<uint8_t>& message_append_string(std::vector<uint8_t>& message, const std::wstring& string) {
  return message_append_string(message, logger::wide(string));
}
<file_sep>/internal/src/server/tcp_server.h
#pragma once

#include <stdint.h>
#include <functional>

typedef std::function<bool(uint16_t, const std::vector<uint8_t>&)> TcpMessageSender;
typedef std::function<void(uint16_t, const std::vector<uint8_t>&, const TcpMessageSender&)> TcpMessageHandler;

class TcpServer {
public:
  virtual ~TcpServer() = default;
  virtual void add_handler(uint16_t type, TcpMessageHandler handler) = 0;
  virtual void start() = 0;
};

TcpServer* tcp_server_create(int16_t port);
<file_sep>/internal/src/server/tcp_server.cpp
#include <utility>

#include "tcp_server.h"
#include "../logging/log.h"

#include <thread>
#include <mutex>
#include <WinSock2.h>

class OnLeave {
public:
  explicit OnLeave (std::function<void()> function) : function(std::move(function)) {

  }

  ~OnLeave () {
    function();
  }

private:
  std::function<void ()> function;
};

class ActualTcpServer : public TcpServer {
public:
  explicit ActualTcpServer(uint16_t port): port(port), stop_event(nullptr) {
    std::lock_guard<std::mutex> guard(mutex);
    stop_event = CreateEventW(nullptr, true, false, nullptr);
  }

  ~ActualTcpServer() override {
    std::unique_lock<std::mutex> guard(mutex);
    SetEvent(stop_event);

    while(thread_count > 0) {
      condition.wait(guard);
    }
  }

  void add_handler(uint16_t type, TcpMessageHandler handler) override {
    logger::it->error("TCP server: registering handler for message type {}.", type);

    std::lock_guard<std::mutex> guard(mutex);
    handlers[type] = handler;
  }

  void start() override {
    std::lock_guard<std::mutex> guard(mutex);

    if (!started) {
      logger::it->info("TCP server: starting on port {}.", port);

      started = true;

      if (!try_start_thread(std::bind(&ActualTcpServer::accept_handler, this))) {
        logger::it->error("TCP server: failed to start accept thread.");
      }
    } else {
      logger::it->error("TCP server: already started, not starting.");
    }
  }

private:
  bool try_start_thread(std::function<void(void)>&& function) {
    thread_count++;

    try {
      std::thread thread(function);
      thread.detach();
      return true;
    } catch (const std::system_error& error) {
      thread_count--;
      return false;
    }
  }

  bool try_start_thread_lock(std::function<void(void)>&& function) {
    std::lock_guard<std::mutex> guard(mutex);
    return try_start_thread(std::move(function));
  }

  void end_thread() {
    std::lock_guard<std::mutex> guard(mutex);
    thread_count--;
    condition.notify_all();
  }

  void accept_handler() {
    uint32_t handle = UINT32_MAX;
    void* accept_event = nullptr;

    SetThreadDescription(GetCurrentThread(), L"TCP server accept thread");

    OnLeave exit([this, &handle, &accept_event] {
      logger::it->debug("TCP server: accept thread shutting down.");

      if (handle != UINT32_MAX) {
        closesocket(handle);
      }

      if (accept_event != nullptr) {
        WSACloseEvent(accept_event);
      }

      end_thread();
    });

    logger::it->debug("TCP server: creating listen socket.");
    handle = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, 0);

    if (handle == UINT32_MAX) {
      WSADATA startup_data;
      WSAStartup(MAKEWORD(2,2), &startup_data);

      handle = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, 0);

      if (handle == UINT32_MAX) {
        logger::it->debug("TCP server: failed to create listen socket.");
        return;
      }
    }

    if (!socket_configure(handle)) {
      logger::it->debug("TCP server: failed to set listen socket to non-blocking.");
      return;
    }

    sockaddr_in bind_address{};
    bind_address.sin_family = AF_INET,
    bind_address.sin_port = htons(port),
    bind_address.sin_addr.s_addr = INADDR_ANY;

    if (bind(handle, (sockaddr*) &bind_address, sizeof(bind_address)) == SOCKET_ERROR) {
      logger::it->debug("TCP server: failed to bind listen socket.");
      return;
    }

    if (listen(handle, SOMAXCONN) == SOCKET_ERROR) {
      logger::it->debug("TCP server: failed to listen on listen socket.");
      return;
    }

    accept_event = WSACreateEvent();

    if (accept_event == WSA_INVALID_EVENT) {
      logger::it->error("TCP server: failed to create accept event object.");
      return;
    } else if (WSAEventSelect(handle, accept_event, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR) {
      logger::it->error("TCP server: failed to configure accept event object.");
      return;
    }

    void* events[2] = { stop_event, accept_event };

    logger::it->debug("TCP server: beginning accept loop.");

    while (true) {
      uint32_t result = WSAWaitForMultipleEvents(2, events, false, 30000, false);

      if (result == WSA_WAIT_TIMEOUT) {
        logger::it->debug("TCP server: accept thread still alive.");
        continue;
      } else if (result == WSA_WAIT_EVENT_0) {
        logger::it->debug("TCP server: accept thread stopping as requested.");
        return;
      } else if (result == WSA_WAIT_FAILED) {
        logger::it->error("TCP server: accept wait failed, aborting.");
        return;
      }

      WSANETWORKEVENTS fired_events;

      if (WSAEnumNetworkEvents(handle, accept_event, &fired_events) == SOCKET_ERROR) {
        logger::it->error("TCP server: accept event enumeration failed, aborting.");
        return;
      } else if ((fired_events.lNetworkEvents & FD_CLOSE) != 0) {
        logger::it->error("TCP server: accept socket closed, aborting.");
        return;
      } else if ((fired_events.lNetworkEvents & FD_ACCEPT) != 0) {
        uint32_t peer = WSAAccept(handle, nullptr, nullptr, nullptr, 0);

        if (peer != UINT32_MAX) {
          logger::it->debug("TCP server: accepting a new connection.");

          if (!try_start_thread_lock(std::bind(&ActualTcpServer::peer_handler, this, peer))) {
            logger::it->error("TCP server: failed to create peer socket thread.");
            closesocket(peer);
          }
        } else {
          logger::it->warn("TCP server: did not find anything to accept.");
        }
      }
    }
  }

  void peer_handler(uint32_t peer) {
    void* io_event = nullptr;

    SetThreadDescription(GetCurrentThread(), L"TCP server peer thread");

    OnLeave exit([this, peer, &io_event] {
      logger::it->debug("TCP server: peer thread shutting down.");

      if (io_event != nullptr) {
        WSACloseEvent(io_event);
      }

      closesocket(peer);
      end_thread();
    });

    if (!socket_configure(peer)) {
      logger::it->debug("TCP server: failed to set peer socket to non-blocking.");
      return;
    }

    io_event = WSACreateEvent();

    WSAEventSelect(peer, io_event, FD_READ | FD_WRITE | FD_CLOSE);

    std::vector<uint8_t> header;
    header.resize(6);

    bool success = true;

    TcpMessageSender sender = [this, peer, io_event, &header, &success] (uint16_t type, const std::vector<uint8_t>& message) -> bool {
      logger::it->debug("TCP server: sending response message: type {}, length {}.", type, message.size());

      *(uint16_t*) &header[0] = type;
      *(uint32_t*) &header[2] = message.size();

      success = peer_write(peer, io_event, header) && peer_write(peer, io_event, message);
      return success;
    };

    std::vector<uint8_t> unknown_response;
    std::vector<uint8_t> message_body;

    while (peer_read(peer, io_event, header)) {
      uint16_t message_type = *(uint16_t*) &header[0];
      uint32_t message_length = *(uint32_t*) &header[2];

      logger::it->debug("TCP server: received message header: type {}, length {}.", message_type, message_length);

      if (message_length > 0x100000) {
        logger::it->error("TCP server: message length too high, aborting connection.");
        return;
      }

      message_body.resize(message_length);

      if (!peer_read(peer, io_event, message_body)) {
        logger::it->debug("TCP server: message body not read, closing peer.");
        return;
      }

      TcpMessageHandler* handler;

      auto it = handlers.find(message_type);

      if (it == handlers.end()) {
        unknown_response.resize(2);
        *(uint16_t*) &unknown_response[0] = message_type;

        sender(1, unknown_response);
      } else {
        it->second(message_type, message_body, sender);
      }

      if (!success) {
        logger::it->debug("TCP server: message sending not performed, closing peer.");
        return;
      }
    }

    logger::it->debug("TCP server: message header not read, closing peer.");
  }

  bool socket_configure(uint32_t handle) {
    u_long non_blocking = 1;
    return ioctlsocket(handle, FIONBIO, &non_blocking) != SOCKET_ERROR;
  }

  bool network_wait(uint32_t peer, void* io_event) {
    void* events[] = { stop_event, io_event };
    uint32_t result = WSAWaitForMultipleEvents(2, events, false, WSA_INFINITE, false);

    if (result == WSA_WAIT_EVENT_0) {
      logger::it->debug("TCP server: on network wait, detected stop instruction.");
      return false;
    } else if (result == WSA_WAIT_FAILED) {
      logger::it->error("TCP server: on network wait, received wait failure.");
      return false;
    }

    WSANETWORKEVENTS fired_events;

    if (WSAEnumNetworkEvents(peer, io_event, &fired_events) == SOCKET_ERROR) {
      logger::it->error("TCP server: on network wait, failed to enumerate events.");
      return false;
    } else if ((fired_events.lNetworkEvents & FD_CLOSE) != 0) {
      logger::it->warn("TCP server: on network wait, detected socket close.");
      return false;
    }

    return true;
  }

  bool peer_read(uint32_t peer, void* io_event, std::vector<uint8_t>& buffer) {
    size_t total_received = 0;

    while (total_received < buffer.size()) {
      int32_t received = recv(peer, (char*) &buffer[total_received], buffer.size() - total_received, 0);

      if (received == 0) {
        logger::it->debug("TCP server: on peer read, detected close via recv.");
        return false;
      } else if (received == SOCKET_ERROR) {
        int32_t last_error = WSAGetLastError();

        if (last_error == WSAEWOULDBLOCK) {
          if (!network_wait(peer, io_event)) {
            return false;
          }
        }
      } else {
        total_received += received;
      }
    }

    return true;
  }

  bool peer_write(uint32_t peer, void* io_event, const std::vector<uint8_t>& buffer) {
    size_t total_sent = 0;

    while (total_sent < buffer.size()) {
      int32_t sent = send(peer, (const char*) &buffer[total_sent], buffer.size() - total_sent, 0);

      if (sent == SOCKET_ERROR) {
        int32_t last_error = WSAGetLastError();

        if (last_error == WSAEWOULDBLOCK) {
          if (!network_wait(peer, io_event)) {
            return false;
          }
        }
      } else {
        total_sent += sent;
      }
    }

    return true;
  }

  std::mutex mutex;
  std::condition_variable condition;
  bool started = false;
  uint32_t thread_count = 0;
  void* stop_event;
  std::unordered_map<uint16_t, TcpMessageHandler> handlers;
  int port;
};

TcpServer* tcp_server_create(int16_t port) {
  return new ActualTcpServer(port);
}
