resources.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. #include "resources.h"
  2. #include "readiness_rules.h"
  3. #include <Windows.h>
  4. #include <cstring>
  5. #include "../../patterns/image_scan.h"
  6. #include "../../patterns/signature_text.h"
  7. #include "../../../core/logging/log.h"
  8. namespace sunrise::client::hooks::ember_movies {
  9. namespace {
  10. using namespace patterns;
  11. using Manager=void*(__fastcall*)();
  12. using Create=std::uint32_t*(__fastcall*)(void*,std::uint32_t*,int,int,int,const char*);
  13. using Add=void(__fastcall*)(void*,const std::uint32_t*);
  14. using Submit=void(__fastcall*)(void*,std::uint32_t);
  15. using State=int(__fastcall*)(void*);
  16. Manager manager{}; Create create{}; Add add{}; Submit submit{},destroy{}; State status{};
  17. std::uintptr_t tableGlobal{};
  18. INIT_ONCE init=INIT_ONCE_STATIC_INIT;
  19. bool available{};
  20. template<class T> T read(std::uintptr_t at) { T value{};std::memcpy(&value,reinterpret_cast<void*>(at),sizeof(value));return value; }
  21. void* call(std::byte* code,unsigned offset) {
  22. if (!code || code[offset]!=std::byte{0xE8}) return nullptr;
  23. return code+offset+5+read<std::int32_t>(reinterpret_cast<std::uintptr_t>(code+offset+1));
  24. }
  25. bool resolve_native() {
  26. // B46E10 supplies the root lifecycle API. Its startup assets use kind 2;
  27. // movies use kind 1, as selected from their package type_info by native 426920.
  28. constexpr auto loadSig=signature<signature_length("40 53 56 57 48 81 EC 50 01 00 00 48 8B 05 ? ? ? ? 48 33 C4 48 89 84 24 40 01 00 00 48 63 DA 48 8B F1")>(
  29. "40 53 56 57 48 81 EC 50 01 00 00 48 8B 05 ? ? ? ? 48 33 C4 48 89 84 24 40 01 00 00 48 63 DA 48 8B F1");
  30. // B44020: inspect the completed root, then release it; no global I/O drain needed.
  31. constexpr auto endSig=signature<signature_length("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 83 EC 20 48 63 EA 4C 8B F1 E8")>(
  32. "48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 83 EC 20 48 63 EA 4C 8B F1 E8");
  33. auto* load=scan_main_image_unique(loadSig,"ember_resource_load");
  34. auto* end=scan_main_image_unique(endSig,"ember_resource_release");
  35. auto mgr=reinterpret_cast<Manager>(call(load,0x96));
  36. create=reinterpret_cast<Create>(call(load,0xD1));
  37. add=reinterpret_cast<Add>(call(load,0x14C));
  38. submit=reinterpret_cast<Submit>(call(load,0x157));
  39. status=reinterpret_cast<State>(call(end,0x85));
  40. destroy=reinterpret_cast<Submit>(call(end,0x9F));
  41. if (!mgr || !create || !add || !submit || !status || !destroy || !end
  42. || end[0x2E]!=std::byte{0x48} || end[0x2F]!=std::byte{0x8B} || end[0x30]!=std::byte{0x05}) return false;
  43. tableGlobal=reinterpret_cast<std::uintptr_t>(end+0x35)+read<std::int32_t>(reinterpret_cast<std::uintptr_t>(end+0x31));
  44. manager=mgr; return true;
  45. }
  46. BOOL CALLBACK initialize(PINIT_ONCE,void*,void**) { available=resolve_native();return TRUE; }
  47. bool resolve() { InitOnceExecuteOnce(&init,initialize,nullptr,nullptr);return available; }
  48. std::uintptr_t row(std::uint32_t handle) {
  49. if (handle==0xFFFFFFFFU || !tableGlobal) return 0;
  50. const auto high=static_cast<std::uint32_t>(static_cast<std::int32_t>(handle)>>13);
  51. const auto pool=(high&0xFFFFU)&((static_cast<std::uint64_t>(high)|0xFFC0000ULL)>>18);
  52. const auto head=read<std::uintptr_t>(tableGlobal);
  53. if (!head) return 0;
  54. const auto table=read<std::uintptr_t>(head);
  55. return table ? table+64*pool : 0;
  56. }
  57. void* blob(std::uint32_t handle,std::uint32_t expectedClass=0) {
  58. const auto pool=row(handle);if (!pool) return nullptr;
  59. const auto storage=read<std::uintptr_t>(pool+8);
  60. const auto stride=read<std::uint32_t>(pool+48);
  61. if (!storage || !stride) return nullptr;
  62. const auto item=storage+stride*static_cast<std::uintptr_t>(handle&0x1FFFU);
  63. // Unloaded package entries contain FEFE free-list markers, not a definition.
  64. if (expectedClass && read<std::uint32_t>(item)!=expectedClass) return nullptr;
  65. const auto mask=static_cast<std::uintptr_t>(static_cast<std::intptr_t>(read<std::int32_t>(pool+52)));
  66. const auto at=item-(read<std::uintptr_t>(item+8)&mask);
  67. return reinterpret_cast<void*>(at);
  68. }
  69. }
  70. bool sunburn_resident() noexcept {
  71. if (!resolve()) return false;
  72. __try { return blob(0x80B82489U,0x80809C0FU)!=nullptr; }
  73. __except(EXCEPTION_EXECUTE_HANDLER) { return false; }
  74. }
  75. bool MovieResource::begin(std::uint32_t asset) noexcept {
  76. if (held()) return asset_==asset;
  77. if ((asset!=0x80BCA001U && asset!=0x80BCA003U) || !resolve()) return false;
  78. auto* mgr=manager(); if (!mgr) return false;
  79. create(mgr,&root_,8,2,0,"mission_ember_movie");
  80. if (!held()) return false;
  81. auto* root=blob(root_);if (!root) return false;
  82. // Explicitly retain the small metadata records dereferenced by 41A810 and
  83. // the subtitle reader. Native playback streams the 640MB video entry itself.
  84. for (const auto tag : movie_metadata(asset)) {
  85. const std::uint32_t request[]{movie_resource_kind,tag};
  86. add(root,request);
  87. }
  88. submit(mgr,root_);asset_=asset;
  89. core::log::writef(core::log::Channel::client,core::log::Level::info,
  90. "ev=ember_movie result=resource_requested asset=%08X root=%08X kind=1 metadata=4",asset_,root_);
  91. return true;
  92. }
  93. int MovieResource::state() const noexcept {
  94. if (!held() || !status) return -1;
  95. __try { auto* root=blob(root_);return root ? status(root) : -1; }
  96. __except(EXCEPTION_EXECUTE_HANDLER) { return -1; }
  97. }
  98. bool MovieResource::ready() const noexcept {
  99. if (state()!=2) return false;
  100. __try {
  101. auto* wrapper=blob(asset_,0x80808495U);
  102. if (!wrapper) return false;
  103. const auto header=read<std::uint32_t>(reinterpret_cast<std::uintptr_t>(wrapper)+8);
  104. if (header!=asset_-1) return false;
  105. auto* info=blob(header,0x80808499U);
  106. const auto metadata=movie_metadata(asset_);
  107. if (read<std::uint32_t>(reinterpret_cast<std::uintptr_t>(wrapper)+12)!=metadata[2]
  108. || !blob(metadata[2],0x80809A88U) || !blob(metadata[3],0x80806B8FU)) return false;
  109. return movie_resources_ready(2,asset_,true,header,info!=nullptr,
  110. info ? read<std::uint32_t>(reinterpret_cast<std::uintptr_t>(info)+0x18) : 0xFFFFFFFFU);
  111. } __except(EXCEPTION_EXECUTE_HANDLER) { return false; }
  112. }
  113. bool MovieResource::release() noexcept {
  114. if (!held()) return true;
  115. const auto value=state();
  116. if (!resource_can_release(value)) return false;
  117. destroy(manager(),root_);
  118. core::log::writef(core::log::Channel::client,core::log::Level::info,
  119. "ev=ember_movie result=resource_released asset=%08X root=%08X",asset_,root_);
  120. root_=0xFFFFFFFFU;asset_=0;return true;
  121. }
  122. }