writecodeblocks noop clean

Feature/pc writecodeblocks noop clean
This commit is contained in:
James Osborne
2026-06-07 13:00:51 -04:00
committed by GitHub
3 changed files with 206 additions and 3 deletions
+79 -2
View File
@@ -251,6 +251,9 @@ ClientFunctionIndex::ClientFunctionIndex(const std::string& root_dir, bool raise
add_directory(item_path);
} else if (item_path.ends_with(".s") && std::filesystem::is_regular_file(item_path)) {
client_functions_log.debug_f("Adding {} from {}", item_name, item_path);
if (item_name.find("Dragon") != std::string::npos) {
client_functions_log.warning_f("Dragon source load debug: adding {} from {}", item_name, item_path);
}
if (!source_files.emplace(item_name, phosg::load_file(item_path)).second) {
throw std::runtime_error(std::format("Duplicate source filename: {}", item_name));
}
@@ -285,6 +288,20 @@ ClientFunctionIndex::ClientFunctionIndex(const std::string& root_dir, bool raise
throw std::runtime_error(std::format("({} preprocessing) {}", source_filename, e.what()));
}
if (source_filename.find("Dragon") != std::string::npos) {
client_functions_log.warning_f(
"Dragon preprocess debug: source={} produced {} version chunk(s)",
source_filename,
preprocessed.size());
for (const auto& [debug_sv, debug_source] : preprocessed) {
client_functions_log.warning_f(
"Dragon preprocess debug: source={} sv={} chunk_size={}",
source_filename,
str_for_specific_version(debug_sv),
debug_source.size());
}
}
for (const auto& [specific_version, source] : preprocessed) {
std::shared_ptr<Function> fn = std::make_shared<Function>();
fn->short_name = source_filename.substr(0, source_filename.size() - 2);
@@ -444,6 +461,37 @@ ClientFunctionIndex::ClientFunctionIndex(const std::string& root_dir, bool raise
}
}
}
for (const char* probe_name : {"DragonVisualFix", "PsoPeepsDragonVisualFixPC", "RaresInQuests"}) {
for (uint32_t probe_sv : {0x324F4A57u, SPECIFIC_VERSION_X86_INDETERMINATE}) {
std::string key = cache_key(probe_name, probe_sv);
auto all_it = this->all_functions.find(key);
auto map_it = this->functions_by_specific_version.find(probe_sv);
bool in_version_map = false;
if (map_it != this->functions_by_specific_version.end()) {
in_version_map = map_it->second.count(key);
}
client_functions_log.warning_f(
"Client function probe: name={} sv={} key={} all_functions={} version_map={} map_size={}",
probe_name,
str_for_specific_version(probe_sv),
key,
all_it != this->all_functions.end(),
in_version_map,
map_it == this->functions_by_specific_version.end() ? 0 : map_it->second.size());
if (all_it != this->all_functions.end()) {
const auto& fn = all_it->second;
client_functions_log.warning_f(
"Client function probe detail: short={} long={} visibility={} specific_version={} arch={} menu_item_id={:08X}",
fn->short_name,
fn->long_name,
phosg::name_for_enum(fn->visibility),
str_for_specific_version(fn->specific_version),
name_for_architecture(fn->arch),
static_cast<uint32_t>(fn->menu_item_id));
}
}
}
}
std::shared_ptr<const Menu> ClientFunctionIndex::patch_switches_menu(
@@ -455,15 +503,44 @@ std::shared_ptr<const Menu> ClientFunctionIndex::patch_switches_menu(
auto map_it = this->functions_by_specific_version.find(specific_version);
if (map_it != this->functions_by_specific_version.end()) {
client_functions_log.warning_f(
"Patch menu debug: building menu for specific_version={} with {} function entries",
str_for_specific_version(specific_version),
map_it->second.size());
for (auto [name, fn] : map_it->second) {
if (fn->appears_in_patches_menu() && !server_auto_patches_enabled.count(fn->short_name)) {
bool appears = fn->appears_in_patches_menu();
bool server_auto = server_auto_patches_enabled.count(fn->short_name);
bool client_enabled = client_auto_patches_enabled.count(fn->short_name);
bool dragon_debug =
(fn->short_name.find("Dragon") != std::string::npos) ||
(fn->long_name.find("Dragon") != std::string::npos);
if (dragon_debug || appears) {
client_functions_log.warning_f(
"Patch menu debug: key={} short={} long={} visibility={} appears={} server_auto={} client_enabled={} menu_item_id={:08X}",
name,
fn->short_name,
fn->long_name,
phosg::name_for_enum(fn->visibility),
appears,
server_auto,
client_enabled,
static_cast<uint32_t>(fn->menu_item_id));
}
if (appears && !server_auto) {
std::string item_text;
item_text.push_back(client_auto_patches_enabled.count(fn->short_name) ? '*' : '-');
item_text.push_back(client_enabled ? '*' : '-');
item_text += fn->long_name.empty() ? fn->short_name : fn->long_name;
ret->items.emplace_back(
fn->menu_item_id, item_text, fn->description, MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL_RUNS_CODE);
}
}
} else {
client_functions_log.warning_f(
"Patch menu debug: no functions for specific_version={}",
str_for_specific_version(specific_version));
}
return ret;
}
+24
View File
@@ -0,0 +1,24 @@
.meta visibility="all"
.meta name="Dragon fix"
.meta description="Skips the bad Dragon\nBML selector path."
entry_ptr:
reloc0:
.offsetof start
start:
.include WriteCodeBlocks
.versions 2OJW
.data 0x00420326
.data 2
.data 0x14EB
.all_versions
.data 0x00000000
.data 0x00000000
@@ -118,7 +118,109 @@ first_patch_header:
.versions 2OJW 2OJZ 50YJ 59NJ 59NL
.versions 2OJW
start:
push ebx
push esi
push edi
sub esp, 8 # [esp] = oldProtect, [esp + 4] = tempProtect
jmp get_patch_data_ptr
get_patch_data_ptr_ret:
pop ebx # ebx = patch header
# Resolve kernel32!VirtualProtect using pso.exe's 2OJW IAT:
# GetModuleHandleA = 0x0064D114
# GetProcAddress = 0x0064D128
#
# Build "KERNEL32.dll\0" on stack.
sub esp, 16
mov dword [esp], 0x4E52454B # "KERN"
mov dword [esp + 4], 0x32334C45 # "EL32"
mov dword [esp + 8], 0x6C6C642E # ".dll"
mov dword [esp + 12], 0
push esp
call [0x0064D114] # GetModuleHandleA
add esp, 16
test eax, eax
jz fail
mov esi, eax # esi = kernel32 module handle
# Build "VirtualProtect\0" on stack.
sub esp, 16
mov dword [esp], 0x74726956 # "Virt"
mov dword [esp + 4], 0x506C6175 # "ualP"
mov dword [esp + 8], 0x65746F72 # "rote"
mov dword [esp + 12], 0x00007463 # "ct\0\0"
push esp
push esi
call [0x0064D128] # GetProcAddress
add esp, 16
test eax, eax
jz fail
mov edi, eax # edi = VirtualProtect
apply_next_patch:
cmp dword [ebx + 4], 0
jne copy_code_and_apply_again
add esp, 8
pop edi
pop esi
pop ebx
mov eax, 1
ret
copy_code_and_apply_again:
# VirtualProtect(dest, size, PAGE_EXECUTE_READWRITE, &oldProtect)
lea eax, [esp]
push eax
push 0x40
push dword [ebx + 4]
push dword [ebx]
call edi
test eax, eax
jz fail
xor ecx, ecx # ecx = offset
mov edx, [ebx] # edx = dest addr
copy_next_byte:
mov al, [ebx + ecx + 8] # copy one byte to dest
mov [edx + ecx], al
inc ecx # offset++
cmp [ebx + 4], ecx # check if all bytes have been copied
jne copy_next_byte
# VirtualProtect(dest, size, oldProtect, &tempProtect)
mov esi, [ebx + 4] # esi = size; VirtualProtect preserves esi
mov edx, [esp] # edx = oldProtect
lea eax, [esp + 4]
push eax
push edx
push esi
push dword [ebx]
call edi
lea ebx, [ebx + esi + 8] # advance to next block
jmp apply_next_patch
fail:
add esp, 8
pop edi
pop esi
pop ebx
xor eax, eax
ret
get_patch_data_ptr:
call get_patch_data_ptr_ret
first_patch_header:
.versions 2OJZ 50YJ 59NJ 59NL
start:
push ebx