diff --git a/Engine/Game/Build/20260929/replay_export.h b/Engine/Game/Build/20260929/replay_export.h index 0e099704..560d936a 100644 --- a/Engine/Game/Build/20260929/replay_export.h +++ b/Engine/Game/Build/20260929/replay_export.h @@ -11,17 +11,6 @@ inline constexpr std::uintptr_t submit_frame = 0x34f4610; inline constexpr std::array submit_frame_prefix{ 0x40,0x57,0x48,0x81,0xec,0x50,0x08,0x00,0x00,0x48,0x8b,0x05,0xa0,0xfd,0xcc,0x03, 0x48,0x33,0xc4,0x48,0x89,0x84,0x24,0x20}; -// Maps the export dialog's 8/12/18 Mbps selection to the H.264 encoder rate. -// Raise all three outputs so export cannot silently fall back to the low-bitrate -// preset. The replacement rate is 100 Mbps (100000 kbps). -inline constexpr std::uintptr_t bitrate_switch = 0x62c240; -inline constexpr std::array bitrate_switch_prefix{ - 0x2b,0x85,0xc9,0x74,0x18,0x83,0xe9,0x01,0x74,0x05,0x83,0xf9,0x01,0x74,0x07,0xb8, - 0xe0,0x2e,0x00,0x00,0xeb,0x0c,0xb8,0x50,0x46,0x00,0x00,0xeb,0x05,0xb8,0x40,0x1f, - 0x00,0x00}; -inline constexpr std::array bitrate_immediate_offsets{16, 23, 30}; -inline constexpr std::array stock_bitrates_kbps{12000, 18000, 8000}; -inline constexpr std::uint32_t replay_bitrate_kbps = 100000; // The encoder the replay editor exports with; the engine has others. inline constexpr std::uintptr_t replay_encoder_vtable = 0x608c0b0; // Encoder byte: a frame may be encoded. A world update sets it and an encoded diff --git a/Extension/Rendering/replay_export.cpp b/Extension/Rendering/replay_export.cpp index 7ea46357..1248d279 100644 --- a/Extension/Rendering/replay_export.cpp +++ b/Extension/Rendering/replay_export.cpp @@ -19,52 +19,6 @@ using Submit = void (*)(std::uintptr_t encoder, std::uintptr_t pixels, std::uint std::atomic original{}; std::atomic image{}; -bool force_export_bitrate(std::uintptr_t base) noexcept { - const DWORD saved_error = GetLastError(); - const auto finish = [saved_error](bool result) { - SetLastError(saved_error); - return result; - }; - if (!base || native::bitrate_switch >= supported_build::game_image_size || - native::bitrate_switch_prefix.size() > supported_build::game_image_size - native::bitrate_switch) - return finish(false); - - const auto address = base + native::bitrate_switch; - auto* const destination = reinterpret_cast(address); - std::array actual{}; - if (!memory::read_bytes(address, actual.data(), actual.size())) return finish(false); - - bool needs_write{}; - for (std::size_t i = 0; i < actual.size(); ++i) { - bool bitrate_byte{}; - for (std::size_t slot = 0; slot < native::bitrate_immediate_offsets.size(); ++slot) { - const auto offset = native::bitrate_immediate_offsets[slot]; - if (i < offset || i >= offset + sizeof(std::uint32_t)) continue; - bitrate_byte = true; - if (i == offset) { - std::uint32_t current{}; - std::memcpy(¤t, actual.data() + offset, sizeof(current)); - if (current != native::stock_bitrates_kbps[slot] && current != native::replay_bitrate_kbps) - return finish(false); - needs_write |= current != native::replay_bitrate_kbps; - } - break; - } - if (!bitrate_byte && actual[i] != native::bitrate_switch_prefix[i]) return finish(false); - } - if (!needs_write) return finish(true); - - DWORD previous{}; - if (!VirtualProtect(destination, actual.size(), PAGE_EXECUTE_READWRITE, &previous)) return finish(false); - for (const auto offset : native::bitrate_immediate_offsets) - std::memcpy(actual.data() + offset, &native::replay_bitrate_kbps, sizeof(native::replay_bitrate_kbps)); - std::memcpy(destination, actual.data(), actual.size()); - const bool flushed = FlushInstructionCache(GetCurrentProcess(), destination, actual.size()) != FALSE; - DWORD discarded{}; - const bool restored = VirtualProtect(destination, actual.size(), previous, &discarded) != FALSE; - return finish(flushed && restored); -} - void submit(std::uintptr_t encoder, std::uintptr_t pixels, std::uintptr_t pitch, std::uintptr_t unused) { // Encoding is done on this thread before the call returns, so letting every // frame through also holds the renderer to the pace the encoder keeps. @@ -77,27 +31,21 @@ void submit(std::uintptr_t encoder, std::uintptr_t pixels, std::uintptr_t pitch, bool start(std::uintptr_t base) noexcept { try { - const bool bitrate = force_export_bitrate(base); - logging::write(bitrate ? logging::Level::info : logging::Level::warning, - logging::Channel::graphics, bitrate - ? "Replay video export bitrate override active: 100 Mbps H.264." - : "Replay video export bitrate override skipped: game code did not match the supported build."); - std::array bytes{}; if (!base || !memory::read_bytes(base + native::submit_frame, bytes.data(), bytes.size()) || bytes != native::submit_frame_prefix) { logging::write(logging::Level::warning, logging::Channel::graphics, "Replay export frame pacing hook skipped: game code did not match the supported build."); - return bitrate; + return false; } image = base; auto* target = reinterpret_cast(base + native::submit_frame); void* previous{}; - if (hook_prepare(target, reinterpret_cast(&submit), &previous) != HookOk) return bitrate; + if (hook_prepare(target, reinterpret_cast(&submit), &previous) != HookOk) return false; original = reinterpret_cast(previous); if (hook_enable(target) != HookOk) { hook_remove(target); - return bitrate; + return false; } return true; } catch (...) { diff --git a/Extension/Rendering/replay_export.h b/Extension/Rendering/replay_export.h index 6d069e8b..296ec215 100644 --- a/Extension/Rendering/replay_export.h +++ b/Extension/Rendering/replay_export.h @@ -7,6 +7,5 @@ namespace dingosdk::replay_export { // drops the rest. Rendering faster than it updates (an uncapped frame rate), // an export came out with a fraction of its frames and played that much too // fast. Every rendered frame of a replay export is encoded instead. -// The export dialog's 8/12/18 Mbps choice is also raised to 100 Mbps H.264. bool start(std::uintptr_t base) noexcept; }