#include "resources.h" #include "readiness_rules.h" #include #include #include "../../patterns/image_scan.h" #include "../../patterns/signature_text.h" #include "../../../core/logging/log.h" namespace sunrise::client::hooks::ember_movies { namespace { using namespace patterns; using Manager=void*(__fastcall*)(); using Create=std::uint32_t*(__fastcall*)(void*,std::uint32_t*,int,int,int,const char*); using Add=void(__fastcall*)(void*,const std::uint32_t*); using Submit=void(__fastcall*)(void*,std::uint32_t); using State=int(__fastcall*)(void*); Manager manager{}; Create create{}; Add add{}; Submit submit{},destroy{}; State status{}; std::uintptr_t tableGlobal{}; INIT_ONCE init=INIT_ONCE_STATIC_INIT; bool available{}; template T read(std::uintptr_t at) { T value{};std::memcpy(&value,reinterpret_cast(at),sizeof(value));return value; } void* call(std::byte* code,unsigned offset) { if (!code || code[offset]!=std::byte{0xE8}) return nullptr; return code+offset+5+read(reinterpret_cast(code+offset+1)); } bool resolve_native() { // B46E10 supplies the root lifecycle API. Its startup assets use kind 2; // movies use kind 1, as selected from their package type_info by native 426920. constexpr auto loadSig=signature( "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"); // B44020: inspect the completed root, then release it; no global I/O drain needed. constexpr auto endSig=signature( "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"); auto* load=scan_main_image_unique(loadSig,"ember_resource_load"); auto* end=scan_main_image_unique(endSig,"ember_resource_release"); auto mgr=reinterpret_cast(call(load,0x96)); create=reinterpret_cast(call(load,0xD1)); add=reinterpret_cast(call(load,0x14C)); submit=reinterpret_cast(call(load,0x157)); status=reinterpret_cast(call(end,0x85)); destroy=reinterpret_cast(call(end,0x9F)); if (!mgr || !create || !add || !submit || !status || !destroy || !end || end[0x2E]!=std::byte{0x48} || end[0x2F]!=std::byte{0x8B} || end[0x30]!=std::byte{0x05}) return false; tableGlobal=reinterpret_cast(end+0x35)+read(reinterpret_cast(end+0x31)); manager=mgr; return true; } BOOL CALLBACK initialize(PINIT_ONCE,void*,void**) { available=resolve_native();return TRUE; } bool resolve() { InitOnceExecuteOnce(&init,initialize,nullptr,nullptr);return available; } std::uintptr_t row(std::uint32_t handle) { if (handle==0xFFFFFFFFU || !tableGlobal) return 0; const auto high=static_cast(static_cast(handle)>>13); const auto pool=(high&0xFFFFU)&((static_cast(high)|0xFFC0000ULL)>>18); const auto head=read(tableGlobal); if (!head) return 0; const auto table=read(head); return table ? table+64*pool : 0; } void* blob(std::uint32_t handle,std::uint32_t expectedClass=0) { const auto pool=row(handle);if (!pool) return nullptr; const auto storage=read(pool+8); const auto stride=read(pool+48); if (!storage || !stride) return nullptr; const auto item=storage+stride*static_cast(handle&0x1FFFU); // Unloaded package entries contain FEFE free-list markers, not a definition. if (expectedClass && read(item)!=expectedClass) return nullptr; const auto mask=static_cast(static_cast(read(pool+52))); const auto at=item-(read(item+8)&mask); return reinterpret_cast(at); } } bool sunburn_resident() noexcept { if (!resolve()) return false; __try { return blob(0x80B82489U,0x80809C0FU)!=nullptr; } __except(EXCEPTION_EXECUTE_HANDLER) { return false; } } bool MovieResource::begin(std::uint32_t asset) noexcept { if (held()) return asset_==asset; if ((asset!=0x80BCA001U && asset!=0x80BCA003U) || !resolve()) return false; auto* mgr=manager(); if (!mgr) return false; create(mgr,&root_,8,2,0,"mission_ember_movie"); if (!held()) return false; auto* root=blob(root_);if (!root) return false; // Explicitly retain the small metadata records dereferenced by 41A810 and // the subtitle reader. Native playback streams the 640MB video entry itself. for (const auto tag : movie_metadata(asset)) { const std::uint32_t request[]{movie_resource_kind,tag}; add(root,request); } submit(mgr,root_);asset_=asset; core::log::writef(core::log::Channel::client,core::log::Level::info, "ev=ember_movie result=resource_requested asset=%08X root=%08X kind=1 metadata=4",asset_,root_); return true; } int MovieResource::state() const noexcept { if (!held() || !status) return -1; __try { auto* root=blob(root_);return root ? status(root) : -1; } __except(EXCEPTION_EXECUTE_HANDLER) { return -1; } } bool MovieResource::ready() const noexcept { if (state()!=2) return false; __try { auto* wrapper=blob(asset_,0x80808495U); if (!wrapper) return false; const auto header=read(reinterpret_cast(wrapper)+8); if (header!=asset_-1) return false; auto* info=blob(header,0x80808499U); const auto metadata=movie_metadata(asset_); if (read(reinterpret_cast(wrapper)+12)!=metadata[2] || !blob(metadata[2],0x80809A88U) || !blob(metadata[3],0x80806B8FU)) return false; return movie_resources_ready(2,asset_,true,header,info!=nullptr, info ? read(reinterpret_cast(info)+0x18) : 0xFFFFFFFFU); } __except(EXCEPTION_EXECUTE_HANDLER) { return false; } } bool MovieResource::release() noexcept { if (!held()) return true; const auto value=state(); if (!resource_can_release(value)) return false; destroy(manager(),root_); core::log::writef(core::log::Channel::client,core::log::Level::info, "ev=ember_movie result=resource_released asset=%08X root=%08X",asset_,root_); root_=0xFFFFFFFFU;asset_=0;return true; } }