From 5728a91e7f658142ec5a67332f8218eb8ddedb23 Mon Sep 17 00:00:00 2001 From: DreadHeadHippy <155098676+DreadHeadHippy@users.noreply.github.com> Date: Tue, 20 Oct 2026 09:56:30 -0400 Subject: [PATCH] Replay export: the export dialog's 8, 12 and 18 Mbps choices apply again Every export was forced to 100 Mbps, about 750 MB per minute of replay, whichever quality was chosen. The bitrate patch is removed; the frame pacing fix stays. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Engine/Game/Build/20260929/replay_export.h | 11 ---- Extension/Rendering/replay_export.cpp | 58 ++-------------------- Extension/Rendering/replay_export.h | 1 - 3 files changed, 3 insertions(+), 67 deletions(-) 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; }