Compare commits

..

3 commits

Author SHA1 Message Date
KeybadeBlox
f50945266c Update symbol table
Mostly defined types for a few symbols in SaveData.
2026-04-30 14:56:32 -04:00
KeybadeBlox
6916d3a90c Support name mangling method pointers
ActSequence uses an array of method pointers, meaning we need to support
mangling this obscure corner of the language.  The name mangling code is
admittedly feeling pretty messy, as this appears to require a lot of
special casing.
2026-04-30 14:51:31 -04:00
KeybadeBlox
a6267f4bf0 Update objdiff.json for new name for SaveData 2026-04-30 14:49:31 -04:00
3 changed files with 135 additions and 44 deletions

View file

@ -30,8 +30,7 @@
},
"symbol_mappings": {
"?Exec0Default__jumptable@CActSequence@@3EA": "$L1709",
"?Exec0Default__jumptargets@CActSequence@@3PAPAXA": "$L1710",
"?fSequenceMethods@CActSequence@@3PAP6EXPAU1@@ZA": "?fSequenceMethods@@3PAP8CActSequence@@AEXXZA"
"?Exec0Default__jumptargets@CActSequence@@3PAPAXA": "$L1710"
}
},
{
@ -59,8 +58,8 @@
"source_path": "src/JSRF/SaveData.cpp"
},
"symbol_mappings": {
"?finalizeGameData@@YAXXZ": "_$E2",
"?initGameData@@YAXXZ": "_$E1"
"?finalizeSaveData@@YAXXZ": "_$E2",
"?initSaveData@@YAXXZ": "_$E1"
}
},
{

View file

@ -122,7 +122,7 @@ public class MSVC7Mangle extends GhidraScript {
// Get mangled name
final String mangled = switch (s.getObject()) {
case Function f -> mangleFn (f);
case Data d -> mangleData(d, name);
case Data d -> mangleData(d, name, getClassNamespace(s));
default -> null;
};
@ -190,12 +190,12 @@ public class MSVC7Mangle extends GhidraScript {
final ArrayList<String> dict = new ArrayList<>();
final List<String> nameParts = Arrays.asList(nameRaw.split("::"));
final List<String> nameParts = Arrays.asList(nameRaw.split("::"));
Collections.reverse(nameParts);
final boolean isMethod = f.getCallingConventionName()
.equals("__thiscall") &&
nameParts.size() >= 2;
final String name = mangleIdentifier(nameRaw, isMethod, f.getReturnType(), dict);
final boolean isMethod = f.getCallingConventionName()
.equals("__thiscall") &&
nameParts.size() >= 2;
final String name = mangleIdentifier(nameRaw, isMethod, false, f.getReturnType(), dict);
// Special methods with unique formats
if (isMethod) {
@ -204,7 +204,7 @@ public class MSVC7Mangle extends GhidraScript {
if (unqualified.equals( clsName)) { // Constructor
return "?" + name + "QAE@" +
mangleArgs(f.getSignature(true), dict, nameRaw + "()") +
mangleArgs(f.getSignature(true), false, dict, nameRaw + "()") +
"Z";
} else if (unqualified.equals("~" + clsName)) { // Destructor
return "?" + name + (isVirtual(f) ? "UAE" : "QAE") + "@XZ";
@ -218,6 +218,7 @@ public class MSVC7Mangle extends GhidraScript {
private static String mangleIdentifier(
final String ident,
final boolean isMethod,
final boolean isMethodPtr,
final DataType retType, // Function return type, nullable
final List<String> dict
) {
@ -231,7 +232,8 @@ public class MSVC7Mangle extends GhidraScript {
names.
*/
// Break up names into their mangled order
final List<String> parts = Arrays.asList(ident.split("::"));
List<String> parts = Arrays.asList(ident.split("::"));
parts = parts.subList(isMethodPtr ? 1 : 0, parts.size());
Collections.reverse(parts);
// Non-method special names
@ -386,8 +388,8 @@ public class MSVC7Mangle extends GhidraScript {
) throws Exception {
/* Mangle everything in f but its name and visibility/linkage */
return mangleCallC(f) +
mangleType(f.getReturnType(), dict, loc) +
mangleArgs(f, dict, loc) + "Z";
mangleType(f.getReturnType(), Optional.empty(), dict, loc) +
mangleArgs(f, false, dict, loc) + "Z";
}
private static String mangleCallC(final FunctionSignature f) throws Exception {
@ -402,9 +404,10 @@ public class MSVC7Mangle extends GhidraScript {
}
private static String mangleType(
final DataType t,
final List<String> dict,
final String loc
final DataType t,
final Optional<Namespace> classNamespace,
final List<String> dict,
final String loc
) throws Exception {
/* Mangle a data type in a function name
All types are assumed to have no CV qualifiers.
@ -429,19 +432,23 @@ public class MSVC7Mangle extends GhidraScript {
case DoubleDataType x -> "N";
case LongDoubleDataType x -> "O";
case Pointer p -> "P" +
(p.getDataType() instanceof FunctionSignature ? "6" : "A") +
mangleType(p.getDataType(), dict, loc);
case Union u -> "T" + mangleIdentifier(u.getName(), false, null, dict);
case Structure s -> "U" + mangleIdentifier(s.getName(), false, null, dict);
case Enum e -> "W4" + mangleIdentifier(e.getName(), false, null, dict);
(p.getDataType() instanceof FunctionSignature f ? (
classNamespace.isPresent() ? "8" : "6"
) : "A"
) + mangleType(p.getDataType(), classNamespace, dict, loc);
case Union u -> "T" + mangleIdentifier(u.getName(), false, false, null, dict);
case Structure s -> "U" + mangleIdentifier(s.getName(), false, false, null, dict);
case Enum e -> "W4" + mangleIdentifier(e.getName(), false, false, null, dict);
case VoidDataType x -> "X";
case LongLongDataType x -> "_J";
case UnsignedLongLongDataType x -> "_K";
case BooleanDataType x -> "_N";
case WideCharDataType x -> "_W";
case Array a -> "PA" + mangleArrDims(a) + mangleType(arrType(a), dict, loc);
case FunctionSignature f -> mangleFnType(f, dict, "function typedef \"" + f.getName() + "\"");
case TypeDef d -> mangleType(d.getBaseDataType(), dict, "typedef \"" + d.getName() + "\"");
case Array a -> "PA" + mangleArrDims(a) + mangleType(arrType(a), classNamespace, dict, loc);
case FunctionSignature f -> classNamespace.isPresent() ?
mangleMethodPtr(f, classNamespace.get(), dict, loc) :
mangleFnType(f, dict, "function typedef \"" + f.getName() + "\"");
case TypeDef d -> mangleType(d.getBaseDataType(), classNamespace, dict, "typedef \"" + d.getName() + "\"");
case DefaultDataType x -> throw new Exception ("Encountered data marked \"undefined\" at " + loc + ". Ensure that all data types in the code/data to mangle have been defined.");
case Undefined x -> throw new Exception ("Encountered data marked \"undefined\" at " + loc + ". Ensure that all data types in the code/data to mangle have been defined.");
default -> throw new Exception ("Unknown type \"" + t.getClass().getName() + "\" at " + loc);
@ -490,19 +497,33 @@ public class MSVC7Mangle extends GhidraScript {
return t instanceof Array a_ ? arrType(a_) : t;
}
private static String mangleMethodPtr(
final FunctionSignature f,
final Namespace classNamespace,
final List<String> dict,
final String loc
) throws Exception {
/* Mangle pointed-to part of a method pointer (i.e. after "3PAP8") */
return classNamespace.getName() + "@@AE" +
mangleType(f.getReturnType(), Optional.empty(), dict, loc) +
mangleArgs(f, true, dict, loc) + "Z";
}
private static String mangleArgs(
final FunctionSignature f,
final boolean isMethodPtr,
final List<String> dict,
final String loc
) throws Exception {
/* Mangle the arguments for a function */
final DataType[] args = Arrays.stream(f.getArguments())
.map(ParameterDefinition::getDataType)
.toArray(DataType[]::new);
List<DataType> args = Arrays.stream(f.getArguments())
.map(ParameterDefinition::getDataType)
.toList();
args = args.subList(isMethodPtr ? 1 : 0, args.size());
final ArrayList<DataType> argDict = new ArrayList<>();
if (args.length == 0) return "X";
if (args.size() == 0) return "X";
else {
// I try to be more expression-oriented, but not being
// able to throw in lambdas, not having an error sum
@ -518,7 +539,7 @@ public class MSVC7Mangle extends GhidraScript {
// helped us out here)
String mangledArgs = "";
for (final DataType arg : args) {
final String mangledArg = mangleType(arg, dict, loc);
final String mangledArg = mangleType(arg, Optional.empty(), dict, loc);
mangledArgs += mangledArg.length() == 1 ?
mangledArg :
@ -528,9 +549,17 @@ public class MSVC7Mangle extends GhidraScript {
}
}
private Optional<Namespace> getClassNamespace(final Symbol s) {
/* Return the namespace of the class containing s, if there is one */
final Namespace ns = s.getParentNamespace();
return ns.getType() == Namespace.Type.CLASS ? Optional.of(ns)
: Optional.empty();
}
private String mangleData(
final Data d,
final String name
final Data d,
final String name,
final Optional<Namespace> classNamespace
) throws Exception {
/* Set the data symbol's name to its mangled version */
// String constants
@ -547,13 +576,19 @@ public class MSVC7Mangle extends GhidraScript {
}
final ArrayList<String> dict = new ArrayList<>();
final String ident = mangleIdentifier(name, false, null, dict);
final String ident = mangleIdentifier(
name,
false,
classNamespace.isPresent() && isFnPtr(d.getDataType(), "0x" + d.getAddress().toString()),
null,
dict
);
// vtable
if (ident.startsWith("?_7")) return "?" + ident + "6B@";
return "?" + ident + "3" +
mangleType(d.getDataType(), dict, "0x" + d.getAddress().toString()) +
mangleType(d.getDataType(), classNamespace, dict, "0x" + d.getAddress().toString()) +
"A";
}
@ -625,6 +660,26 @@ public class MSVC7Mangle extends GhidraScript {
return (int)crc.getValue() ^ 0xFFFFFFFF;
}
private static boolean isFnPtr(
final DataType t,
final String loc
) throws Exception {
/* Determine whether the type is a function pointer (or wraps one) */
if (t == null) throw new Exception (
"A data type at " + loc + " was reported as null. " +
"Ensure that all data types in the code/data to " +
"mangle have been defined."
);
return switch(t) {
case Pointer p -> isFnPtr(p.getDataType() , loc);
case Array a -> isFnPtr(arrType(a) , loc);
case TypeDef d -> isFnPtr(d.getBaseDataType(), loc);
case FunctionSignature f -> true;
default -> false;
};
}
private void mangleRefs(
final Function f,
final Set seenSymbols

View file

@ -34,7 +34,7 @@
0x00011ee0 func void __thiscall notinline nofixup CActBase::recursiveExec1FreezeCam
0x00011f80 func void __thiscall notinline nofixup CActBase::recursiveExec1UncoveredPause
0x00012020 func void __thiscall notinline nofixup CActBase::insertActionTree CActBase * lpParent
0x00012100 func CActBase * __thiscall notinline nofixup CActBase::CActBase CActBase * parent eACTID index eACTFLAG flags
0x00012100 func CActBase * __thiscall notinline nofixup CActBase::CActBase CActBase * lpParent eACTID ActID eACTFLAG Flags
0x00012170 func CDrawBase * __thiscall notinline nofixup CDrawBase::CDrawBase CActBase * parent eACTID index eACTFLAG flags
0x000121b0 func void * __thiscall notinline nofixup CDrawBase::`scalar_deleting_destructor' uint param_1
0x000121d0 func void __thiscall notinline nofixup CDrawBase::~CDrawBase
@ -341,7 +341,7 @@
0x00046920 func undefined __stdcall notinline nofixup getUnknownStatic13PartDefault UnknownStatic13Part * out
0x00046b60 func undefined __stdcall notinline nofixup getBlocksNeeded uint param_1
0x00046e20 func BOOL __thiscall notinline nofixup SaveGame char cDriveLetter void * lpvSaveData
0x00047110 func BOOL __cdecl notinline nofixup SaveDataExists_MAYBE undefined * param_1
0x00047110 func BOOL __cdecl notinline nofixup SaveDataExists_MAYBE void * unused
0x00047550 func undefined4 __thiscall notinline nofixup Progress::~Progress
0x00048100 func void __thiscall notinline nofixup Progress::Progress
0x0004a6c0 func Progress * __thiscall notinline nofixup Progress::`scalar_deleting_destructor' byte param_1
@ -440,10 +440,10 @@
0x00067310 func void __thiscall notinline nofixup CSaveMenu::DrawTreeDefault
0x000678b0 func BOOL __thiscall notinline nofixup CSaveMenu::Save
0x00067980 func void __thiscall notinline nofixup CSaveMenu::~CSaveMenu
0x00067b60 func CSaveMenu * __thiscall notinline nofixup CSaveMenu::CSaveMenu CActBase * parent eACTID index eACTFLAG flags undefined4 param_4 undefined4 * param_5
0x00067b60 func CSaveMenu * __thiscall notinline nofixup CSaveMenu::CSaveMenu CActBase * parent eACTID index eACTFLAG flags undefined4 param_4 uint * param_5
0x00067e70 func void * __thiscall notinline nofixup CSaveMenu::`scalar_deleting_destructor' uint param_1
0x00067e90 func void __thiscall notinline nofixup CSaveMenu::Exec0Default
0x000681c0 func void __cdecl notinline nofixup CreateSaveMenu undefined4 param_1 undefined4 * param_2
0x000681c0 func void __cdecl notinline nofixup CreateSaveMenu int param_1 void * param_2
0x000694a0 func float __thiscall notinline nofixup GraphicsSettings::getGraphicsSetting eGRAPHICSSETTINGTYPE setting
0x000694c0 func SGraphicsSettings * __thiscall notinline nofixup GraphicsSettings::GraphicsSettings
0x0006af00 func StageBin * __thiscall notinline nofixup StageBin::StageBin void * stgBinBuf
@ -484,11 +484,12 @@
0x0006f6a0 func void __cdecl notinline nofixup showReconnectControllerMessage
0x0006f6d0 func void default notinline nofixup showCharacterJoinMessage ePLAYERCHARID character undefined4 controller
0x0006f730 func undefined unknown notinline nofixup ShowProblemWithDisc
0x0006f760 func undefined unknown notinline nofixup showSaveLoadErr
0x0006f760 func void __cdecl notinline nofixup ShowSaveLoadErr
0x0006f8c0 func void __thiscall notinline nofixup UnknownStatic27::calledDuringExec0Default
0x0006f9c0 func undefined4 * __thiscall notinline nofixup UnknownStatic27::`scalar_deleting_destructor' byte param_1
0x0006f9e0 func void __cdecl notinline nofixup main
0x00075a30 func undefined4 * __thiscall notinline nofixup CRoboyMenuFull::CRoboyMenuFull CActBase * param_1 eACTID param_2 uint param_3
0x00075de0 func void __cdecl notinline nofixup CreateFullRoboyMenu void * unused DWORD param_2
0x00077400 func void __cdecl notinline nofixup resetHighScores
0x000780f0 func void __thiscall notinline nofixup UnknownObj_0x1167_2::~UnknownAllocated_0x1167_2
0x00078520 func UnknownAllocated_0x1167_2 * __thiscall notinline nofixup UnknownObj_0x1167_2::UnknownAllocated_0x1167_2 CActBase * parent eACTID index uint bitfieldValue undefined4 param_4 uint mssnId
@ -500,9 +501,11 @@
0x0007af70 func void __thiscall notinline nofixup DemoInitializer::~DemoInitializer
0x0007b830 func DemoInitializer * __thiscall notinline nofixup DemoInitializer::`scalar_deleting_destructor' byte param_1
0x0007b8d0 func void __thiscall notinline nofixup DemoInitializer::execDefault
0x0007bc10 func CActSequence * __thiscall notinline nofixup CActSequence::CActSequence CActBase * parent eACTID index uint bitfieldValue
0x0007bc10 func CActSequence * __thiscall notinline nofixup CActSequence::CActSequence CActBase * lpParent eACTID ActID eACTFLAG ActFlag
0x0007bc90 func void __thiscall notinline nofixup CActSequence::~CActSequence
0x0007bdd0 func void __thiscall notinline nofixup CActSequence::Exec0Default
0x0007be08 data void *[2] CActSequence::Exec0Default__jumptargets
0x0007be10 data uchar CActSequence::Exec0Default__jumptable
0x0007be30 func void __thiscall notinline nofixup CActSequence::Init
0x0007bfd0 func void __thiscall notinline nofixup CActSequence::LoadSprNorm
0x0007c020 func void __thiscall notinline nofixup CActSequence::WaitLoadSprNorm
@ -562,7 +565,7 @@
0x0007d460 func void __thiscall notinline nofixup CActSequence::PrepareStinger
0x0007d520 func void __thiscall notinline nofixup CActSequence::WaitEndStinger
0x0007d540 func void __thiscall notinline nofixup CActSequence::ReturnFromStinger
0x0007d5b0 func CActSequence * __thiscall notinline nofixup CActSequence::`scalar_deleting_constructor' byte param_1
0x0007d5b0 func void * __thiscall notinline nofixup CActSequence::`scalar_deleting_destructor' uint param_1
0x0007e100 func BOOL default notinline nofixup 0x1DF3NotAllocated
0x0007e260 func void __stdcall notinline nofixup allocate0x1DF3 CActBase * parent
0x0007e2f0 func void __thiscall notinline nofixup Opening::~Opening
@ -1003,6 +1006,14 @@
0x00186e38 func undefined unknown notinline nofixup ExceptionHandler
0x00187710 func void __cdecl notinline nofixup main_handler_unwind1
0x0018771b func void __cdecl notinline nofixup main_handler ExceptionRecord * param_1 EHRegistrationNode * param_2 void * param_3 EHDispatcherContext * param_4
0x00187c70 func void __cdecl notinline nofixup CActSequence__CActSequence_handler_unwind1
0x00187c78 func void __cdecl notinline nofixup CActSequence__CActSequence_handler ExceptionRecord * param_1 EHRegistrationNode * param_2 void * param_3 EHDispatcherContext * param_4
0x00187c90 func void __cdecl notinline nofixup CActSequence__Init_handler_unwind1
0x00187c9b func void __cdecl notinline nofixup CActSequence__Init_handler_unwind2
0x00187ca6 func void __cdecl notinline nofixup CActSequence__Init_handler_unwind3
0x00187cb1 func void __cdecl notinline nofixup CActSequence__Init_handler ExceptionRecord * param_1 EHRegistrationNode * param_2 void * param_3 EHDispatcherContext * param_4
0x00187cc0 func void __cdecl notinline nofixup CActSequence__Prepare_common_unwind1
0x00187ccb func void __cdecl notinline nofixup CActSequence__common_handler ExceptionRecord * param_1 EHRegistrationNode * param_2 void * param_3 EHDispatcherContext * param_4
0x0018acc0 func undefined unknown notinline nofixup initUnknownStatic01
0x0018acf0 func undefined unknown notinline nofixup initUnknownStatic02
0x0018ad00 func void __stdcall notinline nofixup initCollisionManager
@ -1124,7 +1135,7 @@
0x001c4d58 data CActBaseVtbl CCacheBuilder::`vftable'
0x001c4f68 data CActBaseVtbl FileManager::`vftable'
0x001ca168 data pointer UnknownStatic07::vtable
0x001ca3d8 data pointer CSaveData::`vftable'
0x001ca3d8 data CSaveDataVtbl CSaveData::`vftable'
0x001ca440 data CActBaseVtbl TextRenderer_MAYBE::`vftable'
0x001ca4c8 data undefined * MissionManagerChild_0xE7::vtable
0x001ca508 data undefined * UnknownStatic09::vtable
@ -1208,6 +1219,32 @@
0x001e5048 data EHFuncInfo initCache_funcinfo
0x001e620c data EHUnwindMapEntry[1] main_unwindmap
0x001e6214 data EHFuncInfo main_funcinfo
0x001e67ec data EHUnwindMapEntry[1] CActSequence__CActSequence_unwindmap
0x001e67f4 data EHFuncInfo CActSequence__CActSequence_funcinfo
0x001e6810 data EHUnwindMapEntry[1] CActSequence__~CActSequence_unwindmap_UNUSED
0x001e6818 data EHFuncInfo CActSequence__~CActSequence_funcinfo_UNUSED
0x001e6834 data EHUnwindMapEntry[3] CActSequence__Init_unwindmap
0x001e684c data EHFuncInfo CActSequence__Init_funcinfo
0x001e6868 data EHUnwindMapEntry[1] CActSequence__PrepareTitle_unwindmap_UNUSED
0x001e6870 data EHFuncInfo CActSequence__PrepareTitle_funcinfo_UNUSED
0x001e688c data EHUnwindMapEntry[1] CActSequence__StartFullRoboyMenu_unwindmap_UNUSED
0x001e6894 data EHFuncInfo CActSequence__StartFullRoboyMenu_funcinfo_UNUSED
0x001e68b0 data EHUnwindMapEntry[1] CActSequence__PrepareStoryOrVsMission_unwindmap_UNUSED
0x001e68b8 data EHFuncInfo CActSequence__PrepareStoryOrVsMission_funcinfo_UNUSD
0x001e68d4 data EHUnwindMapEntry[1] CActSequence__PrepareTutorial_unwindmap_UNUSED
0x001e68dc data EHFuncInfo CActSequence__PrepareTutorial_funcinfo_UNUSED
0x001e68f8 data EHUnwindMapEntry[1] CActSequence__PrepareTestRun__unwindmap_UNUSED
0x001e6900 data EHFuncInfo CActSequence__PrepareTestRun_funcinfo_UNUSED
0x001e691c data EHUnwindMapEntry[1] CActSequence__PrepareUnused_unwindmap_UNUSED
0x001e6924 data EHFuncInfo CActSequence__PrepareUnused_funcinfo_UNUSED
0x001e6940 data EHUnwindMapEntry[1] CActSequence__PrepareGraffitiMenu_unwindmap_UNUSED
0x001e6948 data EHFuncInfo CActSequence__PrepareGraffitiMenu_funcinfo_UNUSED
0x001e6964 data EHUnwindMapEntry[1] CActSequence__WaitLoadVsMenu_unwindmap_UNUSED
0x001e696c data EHFuncInfo CActSequence__WaitLoadVsMenu_funcinfo_UNUSED
0x001e6988 data EHUnwindMapEntry[1] CActSequence__WaitLoadEnding_unwindmap_UNUSED
0x001e6990 data EHFuncInfo CActSequence__WaitLoadEnding_funcinfo_UNUSED
0x001e69ac data EHUnwindMapEntry CActSequence__PrepareStinger_unwindmap
0x001e69b4 data EHFuncInfo CActSequence__PrepareStinger_funcinfo
0x001eb760 data voidFunc * __xri_a
0x001eb76c data voidFunc * __xri_z
0x001eb770 data voidFunc * __xc_a
@ -1241,7 +1278,7 @@
0x001efdc8 data uint[40] g_defaultSpawnedSouls
0x001efe68 data uint[4][5] g_defaultSelectedTags
0x001efeb8 data char *[61] g_unusedDebugStrings
0x001effb0 data CSaveData g_gameData
0x001effb0 data CSaveData g_SaveData
0x001f8b30 data float someJaTextParameter
0x001f8b58 data float someSpaceTextParameter
0x001f8c38 data int[286] g_soullIdToStageIndex
@ -1274,7 +1311,7 @@
0x0020cc48 data Timer g_timer
0x0020cc58 data UnknownStatic27 g_unknownStatic27
0x0020cf40 data char *[27] keyStrings
0x0020d2b8 data pointer *[64] CActSequence::switcherFuncs
0x0020d2b8 data ActSequenceFunc *[64] CActSequence::fSequenceMethods
0x0020d498 data D3DCOLOR[6] g_logoBgColours
0x0020d4b0 data undefined segaLogoBuf_ja
0x0020d510 data undefined segaLogoBuf_en

Can't render this file because it has a wrong number of fields in line 3.