From e668b52cd092d13f2ed76bda4a990f9638968206 Mon Sep 17 00:00:00 2001 From: KeybadeBlox Date: Thu, 5 Feb 2026 21:11:11 -0500 Subject: [PATCH 1/2] Add to GameObj::removeChildrenFromObjList() --- decompile/src/JSRF/Core.cpp | 6 ++++++ decompile/src/JSRF/Core.hpp | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/decompile/src/JSRF/Core.cpp b/decompile/src/JSRF/Core.cpp index 84141ab..3a5415c 100644 --- a/decompile/src/JSRF/Core.cpp +++ b/decompile/src/JSRF/Core.cpp @@ -184,7 +184,13 @@ void GameObj::removeFromObjList(GameObj * obj) { void GameObj::removeChildrenFromObjList(GameObj * firstChild) { if (firstChild != NULL) do { if (firstChild->flags > -1) { + firstChild->flags = GameObjFlags(firstChild->flags | GOF_DELETEAFTEREXEC); + g_game->removeFromDrawList(firstChild); + g_game->unsetObj(firstChild->index); } + + this->removeChildrenFromObjList(firstChild->firstChild); + firstChild = firstChild->nextSibling; } while (firstChild != NULL); } diff --git a/decompile/src/JSRF/Core.hpp b/decompile/src/JSRF/Core.hpp index 90be3fe..b876b63 100644 --- a/decompile/src/JSRF/Core.hpp +++ b/decompile/src/JSRF/Core.hpp @@ -139,7 +139,6 @@ struct GameObj { void removeFromObjList (GameObj * obj); void removeChildrenFromObjList(GameObj * firstChild); - // Each frame, one of these trios of methods is called depending on // which state the game is in // Default implementation of each does nothing; inheriting objects From 76e39fdd2787d9292fd5ce1d114233605d76a6da Mon Sep 17 00:00:00 2001 From: KeybadeBlox Date: Thu, 5 Feb 2026 22:31:36 -0500 Subject: [PATCH 2/2] Begin Ghidra mangling script Looks like we'll be writing our own Ghidra scripts. At least these should enable pretty thorough sharing of work and decent UX. --- ghidra/ghidra_scripts/MSVC7Mangle.java | 111 +++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 ghidra/ghidra_scripts/MSVC7Mangle.java diff --git a/ghidra/ghidra_scripts/MSVC7Mangle.java b/ghidra/ghidra_scripts/MSVC7Mangle.java new file mode 100644 index 0000000..be76e65 --- /dev/null +++ b/ghidra/ghidra_scripts/MSVC7Mangle.java @@ -0,0 +1,111 @@ +// Applies Visual C++ 7.0 name mangling to the symbols within the selected +// address range (or the whole program if nothing is selected). +// +// Be aware that the mangling implementation is only partial. +// +// @category Symbol + +import ghidra.app.script.GhidraScript; +import ghidra.program.model.listing.Data; +import ghidra.program.model.listing.Function; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.Enum; +import ghidra.program.model.data.IntegerDataType; +import ghidra.program.model.data.VoidDataType; +import ghidra.program.model.symbol.Reference; +import ghidra.program.model.symbol.SourceType; +import ghidra.program.model.symbol.Symbol; +import ghidra.program.model.symbol.SymbolIterator; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + + +public class MSVC7Mangle extends GhidraScript{ + @Override + public void run() throws Exception { + final SymbolIterator iter = currentProgram.getSymbolTable() + .getPrimarySymbolIterator(currentSelection, true); + + while (iter.hasNext() && !monitor.isCancelled()) { + final Symbol s = iter.next(); + + switch (s.getObject()) { + case Function f -> demangleFn(f); + case Data d -> demangleData(s); + default -> {} + } + } + } + + private void demangleFn(final Function f) throws Exception { + // Gather everything needed for mangling + final List name = Arrays.asList(f.getName(true) + .split("::")); + Collections.reverse(name); + final String callc = f.getCallingConventionName(); + final DataType ret = f.getReturnType(); + final DataType[] args = Arrays.stream(f.getSignature(true) + .getArguments()) + .map(x -> x.getDataType()) + .toArray(DataType[]::new); + + // Construct mangled name + final String mangled = + "?" + String.join("@", name) + "@@" + + switch (callc) { + case "__cdecl" -> "YA"; + case "__thiscall" -> isVirtual(f) ? "UAE" : + "QAE"; + case "__fastcall" -> ""; // TODO + default -> throw new Exception( + "Need to specify calling convention" + ); + } + + mangleType(ret) + + mangleArgs(args) + + "Z"; + + f.setName(mangled, SourceType.USER_DEFINED); + } + + private void demangleData(final Symbol s) { + // TODO + printf("TODO: data symbol \"%s\"\n", s.getName(true)); + } + + private boolean isVirtual(final Function f) { + /* Attempt to determine whether a method is virtual + We essentially try to figure out if any references are from a vtable. + */ + final Reference[] refs = getReferencesTo(f.getEntryPoint()); + + // TODO + + return false; + } + + private String mangleType(final DataType t) throws Exception { + /* Mangle a data type in a function name */ + return switch(t) { + case Enum e -> "W4" + e.getName() + "@@"; + case IntegerDataType x -> "H"; + case VoidDataType x -> "X"; + default -> throw new Exception( + "Unhandled data type \"" + t.toString() + "\"" + ); + }; + } + + private String mangleArgs(final DataType[] args) throws Exception { + /* Mangle the arguments for a function */ + if (args.length == 0) return "X"; + else { + String encoded = ""; + for (int i = 0; i < args.length; i++) + encoded += mangleType(args[i]); + return encoded + "@"; + } + } +}