harvester_drop_experiment.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. #include "harvester_drop_experiment.h"
  2. #include <Windows.h>
  3. #include <cstddef>
  4. #include <cstdint>
  5. #include <cstring>
  6. #include "../../../core/logging/log.h"
  7. #include "../../../core/settings/settings.h"
  8. #include "../../hooking/detour.h"
  9. namespace sunrise::client::hooks::harvester_drop {
  10. namespace {
  11. hooking::detour::Handle g_hook{};
  12. std::uintptr_t g_base{};
  13. using SetStage = std::intptr_t (__fastcall*)(void*, int);
  14. using SetChannel = std::intptr_t (__fastcall*)(std::uint32_t, const std::uint32_t*, const float*);
  15. // 80FE22CB source+0x1F0 names the model channel; FNV-1("doors").
  16. constexpr std::uint32_t kDoorsChannel = 0x80296344;
  17. template<class T> T read(std::uintptr_t address) noexcept {
  18. T value{};
  19. std::memcpy(&value, reinterpret_cast<const void*>(address), sizeof(value));
  20. return value;
  21. }
  22. // Resolve the source exactly as 1029330 does. The runtime component is a separate
  23. // allocation: offsets into an entity's source slab are not runtime pointers.
  24. bool is_harvester_delivery(std::uintptr_t runtime) noexcept {
  25. __try {
  26. const auto handle = read<std::uint32_t>(runtime);
  27. if (handle == 0xffffffff) return false;
  28. const auto high = static_cast<std::uint32_t>(static_cast<std::int32_t>(handle) >> 13);
  29. const auto pool = (high & 0xffffU) & ((static_cast<std::uint64_t>(high) | 0xffc0000ULL) >> 18);
  30. const auto table = read<std::uintptr_t>(read<std::uintptr_t>(g_base + 0x2439C70));
  31. const auto record = table + pool * 64;
  32. const auto item = read<std::uintptr_t>(record + 8)
  33. + read<std::uint32_t>(record + 48) * static_cast<std::uintptr_t>(handle & 0x1fff);
  34. const auto mask = static_cast<std::uintptr_t>(static_cast<std::intptr_t>(read<std::int32_t>(record + 52)));
  35. const auto slab = item - (read<std::uintptr_t>(item + 8) & mask);
  36. const auto source = slab + read<std::uintptr_t>(runtime + 8);
  37. // The native reference resolves the delivery RESOURCE, not the owning
  38. // actor's source slab. Its root is a resource header (size/offsets), so
  39. // checking slab+4 for the actor tag rejected every real delivery.
  40. return read<std::uint32_t>(source) == 0x80C0E5D2
  41. && read<std::uint32_t>(source + 4) == 0x8080670A;
  42. } __except (EXCEPTION_EXECUTE_HANDLER) { return false; }
  43. }
  44. // The live capture proved all four delivery action bindings are empty. Drive the
  45. // separately authored model channel instead, using the native named-scalar route
  46. // used by actor command A9BFA0. Match only this delivery resource, never other AI.
  47. void set_doors(void* self, int stage) noexcept {
  48. const auto runtime = reinterpret_cast<std::uintptr_t>(self);
  49. if (!self || (stage != 0 && stage != 1) || !is_harvester_delivery(runtime)) return;
  50. __try {
  51. const auto entity = read<std::uint32_t>(runtime + 44);
  52. const auto priorStage = read<std::uint8_t>(runtime + 480);
  53. if (entity == 0xffffffff || priorStage == stage) return;
  54. const float value = stage == 1 ? 1.0f : 0.0f;
  55. alignas(16) const float values[4]{value, value, value, value};
  56. reinterpret_cast<SetChannel>(g_base + 0x576420)(entity, &kDoorsChannel, values);
  57. core::log::writef(core::log::Channel::client, core::log::Level::info,
  58. "ev=harvester_doors stage=%d previous=%u entity=%08x channel=%08x value=%.1f result=requested",
  59. stage, static_cast<unsigned>(priorStage), entity, kDoorsChannel, static_cast<double>(value));
  60. } __except (EXCEPTION_EXECUTE_HANDLER) {
  61. core::log::write(core::log::Channel::client, core::log::Level::error,
  62. "ev=harvester_doors result=exception");
  63. }
  64. }
  65. std::intptr_t __fastcall set_stage(void* self, int stage) noexcept {
  66. set_doors(self, stage);
  67. return reinterpret_cast<SetStage>(g_hook.original)(self, stage);
  68. }
  69. }
  70. bool install() noexcept {
  71. if (g_hook.attached) return install_exit();
  72. if (!core::settings::get().server.activation.missionScripting) return true;
  73. g_base = reinterpret_cast<std::uintptr_t>(GetModuleHandleW(nullptr));
  74. constexpr unsigned char stagePrefix[]{0x48,0x89,0x5c,0x24,0x08,0x57,0x48,0x83,0xec,0x20,
  75. 0x48,0x63,0xda,0x48,0x8b,0xf9,0x88,0x99,0xe0,0x01,0x00,0x00};
  76. constexpr unsigned char channelPrefix[]{0x48,0x89,0x5c,0x24,0x08,0x48,0x89,0x74,0x24,0x10,
  77. 0x55,0x57,0x41,0x56,0x48,0x8d,0xac,0x24,0x70,0xf4,0xff,0xff,0x48,0x81,0xec,0x90,0x0c,0x00,0x00};
  78. if (!g_base || std::memcmp(reinterpret_cast<void*>(g_base + 0x102A5B0), stagePrefix, sizeof(stagePrefix))
  79. || std::memcmp(reinterpret_cast<void*>(g_base + 0x576420), channelPrefix, sizeof(channelPrefix))) {
  80. core::log::write(core::log::Channel::client, core::log::Level::warn,
  81. "ev=harvester_doors stage=install result=build_mismatch");
  82. return false;
  83. }
  84. const bool ok = hooking::detour::install({reinterpret_cast<void*>(g_base + 0x102A5B0),
  85. reinterpret_cast<void*>(&set_stage)}, g_hook);
  86. core::log::writef(core::log::Channel::client, core::log::Level::info,
  87. "ev=harvester_doors stage=install result=%s", ok ? "ok" : "failed");
  88. const bool exitOk = install_exit();
  89. return ok && exitOk;
  90. }
  91. }