Support Piklib 6.1/7.1: CMC_Scene::resolve factory + tag-based types
Earlier text-script engines (Piklib 6.1/7.1, added text scripts in 6.1) keep the type factory on CMC_Scene::resolve, not CMC_ObjectsContainer::resolve — so the extractor bailed with "resolve not found". find_factory() now tries both anchors. 6.1's factory is also tag-based: each branch is operator==(NAME) -> new(0x74) -> store tag -> jmp, with the ctor in a separate tag switch (no inline ctor). extract_types gains a pre-emit: when the next operator== arrives still armed, it records the pending type by name (size known, ctor/cpp_class not). The 8.x inline-ctor factory clears `armed` first, so it's untouched (golden pair unchanged). Per-version reality: 6.1 = 23 types / 0 methods (no prepareMthHashSet yet) / 103 events / 80 fields; 7.1 = 26 / 322 / 102 / 86 / 288 dispatch (full); type names line up across 6.1->7.1->8.x so version diffs work. - snapshots/PIKLib61 + PIKLIB71 added as golden fixtures (evolution chain) - tests/test_versions.py: 6.1 partial surface, 7.1 full, 61->71 diff -> 38/38 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -206,6 +206,23 @@ def extract_types(program, factory):
|
||||
f = call_target(program, instr)
|
||||
tname = f.getName() if f is not None else None
|
||||
if tname == OP_EQ:
|
||||
# Tag-based factory (Piklib 6.1/7.1's CMC_Scene::resolve): a branch is
|
||||
# `operator==(NAME) -> new(SIZE) -> store tag -> jmp`, with the ctor in a
|
||||
# separate tag switch, so no inline ctor ever fires `elif armed`. If we reach
|
||||
# the *next* operator== still armed, record the pending type by name (size known,
|
||||
# ctor not). The inline-ctor factory (8.x) clears `armed` first, so it's untouched.
|
||||
if armed and pending_name is not None:
|
||||
types.append({
|
||||
"script_name": pending_name,
|
||||
"cpp_class": None,
|
||||
"ctor_addr": None,
|
||||
"object_size": pending_size,
|
||||
"dispatch_addr": None,
|
||||
"via_module_iface": _branch_uses_field_load(branch),
|
||||
})
|
||||
pending_name = None
|
||||
pending_size = None
|
||||
armed = False
|
||||
s = lookback_string(program, recent)
|
||||
if s is not None:
|
||||
pending_name = s
|
||||
@@ -839,11 +856,26 @@ def sha256_of(program):
|
||||
|
||||
# --------------------------------------------------------------------------- main
|
||||
|
||||
def find_factory(program):
|
||||
"""The type-dispatch factory (the operator==("NAME") -> new -> ctor ladder). Its home class
|
||||
moved across versions: the script factory lived on CMC_Scene in early text-script Piklib
|
||||
(6.1 / 7.1), then was hoisted into CMC_ObjectsContainer from Piklib 8.x onward (and BlooMoo)."""
|
||||
for class_name, method_name in (
|
||||
("CMC_ObjectsContainer", "resolve"), # Piklib 8.x / BlooMoo
|
||||
("CMC_Scene", "resolve"), # Piklib 6.1 / 7.1
|
||||
):
|
||||
f = find_function_by_qualified(program, class_name, method_name)
|
||||
if f is not None:
|
||||
return f
|
||||
return None
|
||||
|
||||
|
||||
def run():
|
||||
program = currentProgram # GhidraScript/pyghidra inject this global, not `program`
|
||||
factory = find_function_by_qualified(program, "CMC_ObjectsContainer", "resolve")
|
||||
factory = find_factory(program)
|
||||
if factory is None:
|
||||
print("[!] CMC_ObjectsContainer::resolve not found - is this a Piklib/BlooMoo DLL?")
|
||||
print("[!] factory not found (CMC_ObjectsContainer::resolve / CMC_Scene::resolve)"
|
||||
" - is this a Piklib/BlooMoo DLL with text-script support? (added in Piklib 6.1)")
|
||||
return
|
||||
|
||||
engine, compiler = detect_engine(factory) # namespace lives on the symbol/stub
|
||||
|
||||
Reference in New Issue
Block a user