mirror of
https://github.com/EricPlayZ/EGameTools.git
synced 2025-07-18 17:37:53 +08:00
backup script changes
This commit is contained in:
@ -39,7 +39,7 @@ class ParsedFunction(Prodict):
|
||||
parentNamespaces: List[str]
|
||||
parentClasses: List[str]
|
||||
funcName: str
|
||||
params: List
|
||||
params: List[ParsedParam]
|
||||
const: bool
|
||||
fullFuncSig: str
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import os
|
||||
|
||||
from ExportClassH import Utils, Config, ClassParser
|
||||
from ExportClassH import ClassGen, Utils, Config
|
||||
from ExportClassH.ClassDefs import ClassName, ParsedFunction, ParsedClassVar
|
||||
|
||||
processedClasses: set[str] = set()
|
||||
@ -73,7 +73,7 @@ def GenerateClassFuncCode(func: ParsedFunction, vtFuncIndex: int = 0) -> list[st
|
||||
|
||||
targetParams: str = ""
|
||||
if func.type == "basic_vfunc":
|
||||
targetParams = ClassParser.ExtractParamNames(params)
|
||||
targetParams = ClassGen.ExtractParamNames(params)
|
||||
targetParams = ", " + targetParams if targetParams else ""
|
||||
|
||||
funcSig: str = f"{returnType}{func.funcName}({params}){const}{stripped_vfunc}" if func.type != "basic_vfunc" else f"VIRTUAL_CALL({vtFuncIndex}, {returnType}, {func.funcName}, ({params}){targetParams})"
|
||||
@ -156,7 +156,7 @@ def GenerateHeaderCode(targetClass: ClassName) -> list[str]:
|
||||
processedClasses.add(targetClass.namespacedClassedName)
|
||||
|
||||
# Get all parsed elements for the target class
|
||||
allParsedElements = ClassParser.GetAllParsedClassVarsAndFuncs(targetClass)
|
||||
allParsedElements = ClassGen.GetAllParsedClassVarsAndFuncs(targetClass)
|
||||
|
||||
# Generate the target class definition
|
||||
classContent = GenerateClassContent(allParsedElements)
|
||||
@ -177,7 +177,7 @@ def GenerateHeaderCode(targetClass: ClassName) -> list[str]:
|
||||
indentLevel += "\t"
|
||||
|
||||
for cls in targetClass.classes:
|
||||
clsType: str = ClassParser.GetClassTypeFromParsedSigs(ClassName(targetClass.namespacedClassedName), allParsedElements)
|
||||
clsType: str = ClassGen.GetClassTypeFromParsedSigs(ClassName(targetClass.namespacedClassedName), allParsedElements)
|
||||
namespaceCode.append(f"{indentLevel}{clsType if clsType else 'class'} {cls} {{")
|
||||
indentLevel += "\t"
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
from ExportClassH import Utils, IDAUtils
|
||||
from ExportClassH import Utils, IDAUtils, RTTIAnalyzer
|
||||
from ExportClassH.ClassDefs import ParsedClass, ParsedFunction, ParsedParam
|
||||
|
||||
CLASS_TYPES = ["namespace", "class", "struct", "enum", "union"]
|
||||
@ -11,7 +11,7 @@ STD_CLASSES = ["std", "rapidjson"]
|
||||
|
||||
parsedClassesDict: dict[str, ParsedClass] = {}
|
||||
|
||||
def GetTypeAndNameStr(fullName: str) -> str:
|
||||
def GetTypeAndNameStr(fullName: str, returnFullName: bool = False) -> str:
|
||||
parts = Utils.ExtractTypeTokensFromString(fullName)
|
||||
if not parts:
|
||||
return ""
|
||||
@ -20,11 +20,11 @@ def GetTypeAndNameStr(fullName: str) -> str:
|
||||
|
||||
for i in range(len(parts) - 1):
|
||||
if parts[i] in CLASS_TYPES:
|
||||
return f"{parts[i]} {parts[i + 1]}"
|
||||
return f"{parts[i]} {parts[i + 1] if not returnFullName else ' '.join(parts[i + 1:])}"
|
||||
return ""
|
||||
|
||||
def SplitTypeFromName(fullName: str) -> tuple[str, str]:
|
||||
typeAndNameStr = GetTypeAndNameStr(fullName)
|
||||
def SplitTypeFromName(fullName: str, returnFullName: bool = False) -> tuple[str, str]:
|
||||
typeAndNameStr = GetTypeAndNameStr(fullName, returnFullName)
|
||||
if not typeAndNameStr:
|
||||
return "", fullName
|
||||
|
||||
@ -35,7 +35,7 @@ def GetParsedParamsFromList(paramsList: list[str], type: str) -> list[ParsedPara
|
||||
params: list[ParsedParam] = []
|
||||
for i in range(len(paramsList)):
|
||||
typeOfParam: str = type
|
||||
classType, className = SplitTypeFromName(paramsList[i])
|
||||
classType, className = SplitTypeFromName(paramsList[i], True)
|
||||
nameOfParam: str = className
|
||||
parsedClassOfParam: Optional[ParsedClass] = None
|
||||
|
||||
@ -62,10 +62,29 @@ def ExtractClassNameAndTemplateParams(templatedClassName: str) -> tuple[str, lis
|
||||
|
||||
return className, templateParams
|
||||
|
||||
def ExtractParentNamespacesAndClasses(namespacesAndClasses: list[str]) -> tuple[list[str], list[str]]:
|
||||
global parsedClassesDict
|
||||
|
||||
parentNamespaces: list[str] = []
|
||||
parentClasses: list[str] = []
|
||||
|
||||
continueOnlyWithClasses: bool = False
|
||||
for part in namespacesAndClasses:
|
||||
namespacesAndClass = "::".join(parentNamespaces + [part])
|
||||
if (namespacesAndClass not in parsedClassesDict or parsedClassesDict[namespacesAndClass].type == "namespace") and not continueOnlyWithClasses:
|
||||
parentNamespaces.append(part)
|
||||
else:
|
||||
if not continueOnlyWithClasses:
|
||||
continueOnlyWithClasses = True
|
||||
parentClasses.append(part)
|
||||
|
||||
return parentNamespaces, parentClasses
|
||||
|
||||
def ParseClassStr(clsStr: str) -> Optional[ParsedClass]:
|
||||
clsStr = clsStr.strip()
|
||||
if not clsStr:
|
||||
return None
|
||||
clsStr = Utils.CleanEndOfClassStr(clsStr)
|
||||
|
||||
parsedClass = ParsedClass()
|
||||
|
||||
@ -93,16 +112,16 @@ def ParseClassStr(clsStr: str) -> Optional[ParsedClass]:
|
||||
parsedClass.name = className
|
||||
parsedClass.templateParams = templateParams
|
||||
parentNamespaces = Utils.SplitByClassSeparatorOutsideTemplates(namespacesAndClasses)
|
||||
if any(STD_CLASS in parentNamespaces for STD_CLASS in STD_CLASSES):
|
||||
if any(STD_CLASS in parentNamespaces for STD_CLASS in STD_CLASSES) or any(STD_CLASS in className for STD_CLASS in STD_CLASSES):
|
||||
return None
|
||||
parsedClass.parentNamespaces.extend(parentNamespaces)
|
||||
|
||||
parsedClass.fullClassName = f"{'::'.join(parsedClass.parentNamespaces + parsedClass.parentClasses + [parsedClass.name])}"
|
||||
return parsedClass
|
||||
|
||||
virtualFuncDuplicateCounter: dict[str, int] = {}
|
||||
virtualFuncPlaceholderCounter: dict[str, int] = {}
|
||||
def ParseFuncStr(funcStr: str, onlyVirtualFuncs: bool = False) -> Optional[ParsedFunction]:
|
||||
virtualFuncDuplicateCounter: dict[tuple[str, str], int] = {}
|
||||
virtualFuncPlaceholderCounter: dict[tuple[str, str], int] = {}
|
||||
def ParseFuncStr(parsedClass: ParsedClass, funcStr: str, onlyVirtualFuncs: bool = False) -> Optional[ParsedFunction]:
|
||||
global virtualFuncDuplicateCounter
|
||||
global virtualFuncPlaceholderCounter
|
||||
|
||||
@ -142,18 +161,8 @@ def ParseFuncStr(funcStr: str, onlyVirtualFuncs: bool = False) -> Optional[Parse
|
||||
if paramsOpenParenIndex != -1 and paramsCloseParenIndex != -1:
|
||||
# Extract parameters
|
||||
paramsStr = funcStr[paramsOpenParenIndex + 1:paramsCloseParenIndex]
|
||||
paramsStrList = Utils.SplitByCommaOutsideTemplates(paramsStr)
|
||||
finalParamsStrList = []
|
||||
######################
|
||||
################### TO SEE HOW TO PROPERLY IMPLEMENT
|
||||
######################
|
||||
for paramStr in paramsStrList:
|
||||
parsedParamAsClass = ParseClassStr(paramStr)
|
||||
if parsedParamAsClass:
|
||||
finalParamsStrList.append(parsedParamAsClass)
|
||||
else:
|
||||
finalParamsStrList.append(paramStr)
|
||||
parsedFunc.params = finalParamsStrList
|
||||
params = GetParsedParamsFromList(Utils.SplitByCommaOutsideTemplates(paramsStr), "param")
|
||||
parsedFunc.params = params
|
||||
|
||||
# Check for const qualifier
|
||||
remainingInputAfterParamsParen = funcStr[paramsCloseParenIndex + 1:].strip()
|
||||
@ -163,6 +172,8 @@ def ParseFuncStr(funcStr: str, onlyVirtualFuncs: bool = False) -> Optional[Parse
|
||||
remainingInputBeforeParamsParen = funcStr[:paramsOpenParenIndex].strip()
|
||||
|
||||
returnType = ""
|
||||
namespacesAndClasses = ""
|
||||
funcName = ""
|
||||
if not isIDAGeneratedType:
|
||||
# Find the last space outside of angle brackets
|
||||
lastSpaceIndex = Utils.FindLastSpaceOutsideTemplates(remainingInputBeforeParamsParen)
|
||||
@ -179,7 +190,7 @@ def ParseFuncStr(funcStr: str, onlyVirtualFuncs: bool = False) -> Optional[Parse
|
||||
namespacesAndClasses = classAndFuncName[:lastClassSeparatorIndex]
|
||||
funcName = classAndFuncName[lastClassSeparatorIndex+2:]
|
||||
else:
|
||||
classParts = classAndFuncName.split("::")
|
||||
classParts = Utils.SplitByClassSeparatorOutsideTemplates(classAndFuncName)
|
||||
namespacesAndClasses = "::".join(classParts[:-1]) if len(classParts) > 1 else ""
|
||||
funcName = classParts[-1]
|
||||
else:
|
||||
@ -191,24 +202,22 @@ def ParseFuncStr(funcStr: str, onlyVirtualFuncs: bool = False) -> Optional[Parse
|
||||
namespacesAndClasses = classAndFuncName[:lastClassSeparatorIndex]
|
||||
funcName = classAndFuncName[lastClassSeparatorIndex+2:]
|
||||
else:
|
||||
returnType = ""
|
||||
funcName = remainingInputBeforeParamsParen
|
||||
namespacesAndClasses = ""
|
||||
else:
|
||||
returnType = remainingInputBeforeParamsParen
|
||||
namespacesAndClasses = ""
|
||||
funcName = ""
|
||||
|
||||
# parentNamespaces, parentClasses = ExtractParentNamespacesAndClasses(namespacesAndClasses)
|
||||
# funcInfo.parentNamespaces.extend(parentNamespaces)
|
||||
# funcInfo.parentNamespaces.extend(parentClasses)
|
||||
parentNamespacesAndClasses = Utils.SplitByClassSeparatorOutsideTemplates(namespacesAndClasses)
|
||||
parentNamespaces, parentClasses = ExtractParentNamespacesAndClasses(parentNamespacesAndClasses)
|
||||
parsedFunc.parentNamespaces = parentNamespaces
|
||||
parsedFunc.parentNamespaces = parentClasses
|
||||
|
||||
# Handle duplicate function naming
|
||||
if isDuplicateFunc:
|
||||
if funcStr not in virtualFuncDuplicateCounter:
|
||||
virtualFuncDuplicateCounter[funcStr] = 0
|
||||
virtualFuncDuplicateCounter[funcStr] += 1
|
||||
funcName = f"_{funcName}{virtualFuncDuplicateCounter[funcStr]}"
|
||||
key = (parsedClass.fullClassName, funcStr)
|
||||
if key not in virtualFuncDuplicateCounter:
|
||||
virtualFuncDuplicateCounter[key] = 0
|
||||
virtualFuncDuplicateCounter[key] += 1
|
||||
funcName = f"_{funcName}{virtualFuncDuplicateCounter[key]}"
|
||||
|
||||
# Determine function type
|
||||
if onlyVirtualFuncs:
|
||||
@ -225,54 +234,22 @@ def ParseFuncStr(funcStr: str, onlyVirtualFuncs: bool = False) -> Optional[Parse
|
||||
else:
|
||||
parsedFunc.funcType = "virtual"
|
||||
|
||||
######################
|
||||
################### TO SEE HOW TO PROPERLY IMPLEMENT
|
||||
######################
|
||||
returnType = Utils.ReplaceIDATypes(returnType)
|
||||
returnType = Utils.CleanType(returnType)
|
||||
|
||||
returnTypeTokens = Utils.ExtractTypeTokensFromString(returnType)
|
||||
for i in range(len(returnTypeTokens)):
|
||||
typeOfReturnType: str = "returnType"
|
||||
nameOfReturnType: str = returnTypeTokens[i]
|
||||
parsedClassOfReturnType: Optional[ParsedClass] = None
|
||||
|
||||
if len(returnTypeTokens) > 1:
|
||||
if returnTypeTokens[i] in CLASS_TYPES:
|
||||
typeOfReturnType = "classReturnType"
|
||||
parsedClassOfReturnType = ParseClassStr(f"{returnTypeTokens[i]} {returnTypeTokens[i + 1]}")
|
||||
parsedFunc.returnTypes.append(ParsedParam(type=typeOfReturnType, name=nameOfReturnType, parsedClassParam=parsedClassOfReturnType))
|
||||
|
||||
#funcInfo.class = ParseClassNameString(className) if className else {}
|
||||
returnTypes = GetParsedParamsFromList(Utils.ExtractTypeTokensFromString(returnType), "param")
|
||||
parsedFunc.returnTypes = returnTypes
|
||||
parsedFunc.funcName = funcName
|
||||
|
||||
# Handle special case for _purecall
|
||||
elif onlyVirtualFuncs and funcStr == "_purecall":
|
||||
virtualFuncPlaceholderCounter[funcStr] += 1
|
||||
key = (parsedClass.fullClassName, funcStr)
|
||||
if key not in virtualFuncPlaceholderCounter:
|
||||
virtualFuncPlaceholderCounter[key] = 0
|
||||
virtualFuncPlaceholderCounter[key] += 1
|
||||
parsedFunc.funcType = "strippedVirtual"
|
||||
parsedFunc.returnTypes = [ParsedParam(type="returnType", name="virtual"), ParsedParam(type="returnType", name="void")]
|
||||
parsedFunc.funcName = f"_StrippedVFunc{virtualFuncPlaceholderCounter[funcStr]}"
|
||||
parsedFunc.funcName = f"_StrippedVFunc{virtualFuncPlaceholderCounter[key]}"
|
||||
|
||||
return parsedFunc
|
||||
|
||||
def ExtractParentNamespacesAndClasses(namespacesAndClasses: list[str]) -> tuple[list[str], list[str]]:
|
||||
global parsedClassesDict
|
||||
|
||||
parentNamespaces: list[str] = []
|
||||
parentClasses: list[str] = []
|
||||
|
||||
continueOnlyWithClasses: bool = False
|
||||
for part in namespacesAndClasses:
|
||||
namespacesAndClass = "::".join(parentNamespaces + [part])
|
||||
if (namespacesAndClass not in parsedClassesDict or parsedClassesDict[namespacesAndClass].type == "namespace") and not continueOnlyWithClasses:
|
||||
parentNamespaces.append(part)
|
||||
else:
|
||||
if not continueOnlyWithClasses:
|
||||
continueOnlyWithClasses = True
|
||||
parentClasses.append(part)
|
||||
|
||||
return parentNamespaces, parentClasses
|
||||
|
||||
def ExtractAllClassSigsFromFuncSig(funcSig: str) -> list[str]:
|
||||
parts = Utils.ExtractTypeTokensFromString(funcSig)
|
||||
if not len(parts) > 1:
|
||||
@ -282,7 +259,6 @@ def ExtractAllClassSigsFromFuncSig(funcSig: str) -> list[str]:
|
||||
for i in range(len(parts) - 1):
|
||||
(classType, className) = (parts[i], parts[i + 1])
|
||||
if classType in CLASS_TYPES and className:
|
||||
className = Utils.CleanEndOfClassStr(className)
|
||||
listOfClassSigs.append(f"{classType} {className}")
|
||||
return listOfClassSigs
|
||||
|
||||
@ -370,6 +346,32 @@ def ParseAllClasses():
|
||||
elif parsedClass.type == "class" and parsedClassesDict[parsedClass.fullClassName].type == "namespace":
|
||||
parsedClassesDict[parsedClass.fullClassName].type = "class"
|
||||
|
||||
# Generate missing namespaces or classes so we can properly move child classes into these generated parent classes later on
|
||||
parsedClassesCopy = list(parsedClassesDict.values())
|
||||
for parsedClass in parsedClassesCopy:
|
||||
parentNamespaces: list[str] = []
|
||||
parentClasses: list[str] = []
|
||||
|
||||
for parentNamespace in parsedClass.parentNamespaces:
|
||||
namespaces = "::".join(parentNamespaces + [parentNamespace])
|
||||
parentNamespaces.append(parentNamespace)
|
||||
if namespaces in parsedClassesDict:
|
||||
continue
|
||||
|
||||
parsedMissingClass = ParseClassStr(f"namespace {namespaces}")
|
||||
if parsedMissingClass:
|
||||
parsedClassesDict[parsedMissingClass.fullClassName] = parsedMissingClass
|
||||
|
||||
for parentClass in parsedClass.parentClasses:
|
||||
classes = "::".join(parentNamespaces + parentClasses + [parentClass])
|
||||
parentClasses.append(parentClass)
|
||||
if classes in parsedClassesDict:
|
||||
continue
|
||||
|
||||
parsedMissingClass = ParseClassStr(f"class {classes}")
|
||||
if parsedMissingClass:
|
||||
parsedClassesDict[parsedMissingClass.fullClassName] = parsedMissingClass
|
||||
|
||||
# Fix parsed classes by setting the right parent namespaces and classes (because cbs might be a parent class and not a parent namespace, which will later change how the header generates for the class)
|
||||
for parsedClass in parsedClassesDict.values():
|
||||
parentNamespaces, parentClasses = ExtractParentNamespacesAndClasses(parsedClass.parentNamespaces)
|
||||
@ -379,29 +381,75 @@ def ParseAllClasses():
|
||||
parsedClass.type = "class"
|
||||
|
||||
# Find and move child classes to parent classes
|
||||
for parsedClass in list(parsedClassesDict.values()):
|
||||
parsedClassesCopy = list(parsedClassesDict.values())
|
||||
for parsedClass in parsedClassesCopy:
|
||||
if not parsedClass.parentNamespaces and not parsedClass.parentClasses:
|
||||
continue
|
||||
|
||||
parentName = ""
|
||||
if parsedClass.parentNamespaces:
|
||||
parentName = parsedClass.parentNamespaces[-1]
|
||||
elif parsedClass.parentClasses:
|
||||
if parsedClass.parentClasses:
|
||||
parentName = parsedClass.parentClasses[-1]
|
||||
elif parsedClass.parentNamespaces:
|
||||
parentName = parsedClass.parentNamespaces[-1]
|
||||
if not parentName:
|
||||
continue
|
||||
|
||||
parentClass = None
|
||||
if parsedClass.parentNamespaces:
|
||||
parentClass = next((parentClass for parentClass in parsedClassesDict.values() if parentClass.name == parentName and parentClass.parentNamespaces == parsedClass.parentNamespaces[:-1]), None)
|
||||
elif parsedClass.parentClasses:
|
||||
parentClass = next((parentClass for parentClass in parsedClassesDict.values() if parentClass.name == parentName and parentClass.parentClasses == parsedClass.parentClasses[:-1]), None)
|
||||
if parsedClass.parentClasses:
|
||||
parentClass = next((parentClass for parentClass in parsedClassesCopy if parentClass.name == parentName and parentClass.parentClasses == parsedClass.parentClasses[:-1]), None)
|
||||
elif parsedClass.parentNamespaces:
|
||||
parentClass = next((parentClass for parentClass in parsedClassesCopy if parentClass.name == parentName and parentClass.parentNamespaces == parsedClass.parentNamespaces[:-1]), None)
|
||||
if not parentClass:
|
||||
continue
|
||||
|
||||
parentClass.childClasses.append(parsedClass)
|
||||
del parsedClassesDict[parsedClass.fullClassName]
|
||||
|
||||
def CreateParamNamesForVTFunc(parsedFunc: ParsedFunction, skipFirstParam: bool) -> str:
|
||||
paramsList: list[str] = [param.name for param in parsedFunc.params if param.name]
|
||||
if len(paramsList) == 1 and paramsList[0] == "void":
|
||||
return "void"
|
||||
# Skip the first parameter (typically the "this" pointer)
|
||||
if skipFirstParam:
|
||||
paramsList = paramsList[1:]
|
||||
paramsList = [Utils.FixTypeSpacing(param.strip()) for param in paramsList]
|
||||
|
||||
paramNames: list[str] = [f"a{i+1}" for i in range(len(paramsList))]
|
||||
newParams: str = ", ".join(f"{paramType} {paramName}" for paramType, paramName in zip(paramsList, paramNames))
|
||||
return newParams
|
||||
|
||||
def ParseAllClassVTFuncs():
|
||||
global parsedClassesDict
|
||||
for parsedClass in parsedClassesDict.values():
|
||||
for (demangledFuncSig, rawType) in RTTIAnalyzer.GetDemangledVTableFuncSigs(parsedClass):
|
||||
if rawType:
|
||||
parsedFunc = ParseFuncStr(parsedClass, rawType, True)
|
||||
if not parsedFunc:
|
||||
continue
|
||||
|
||||
if parsedFunc.returnTypes:
|
||||
newParamTypes = CreateParamNamesForVTFunc(parsedFunc, True) if parsedFunc.params else ""
|
||||
returnTypes = [returnType.name for returnType in parsedFunc.returnTypes if returnType.name]
|
||||
returnTypesStr = ' '.join(returnTypes)
|
||||
demangledFuncSig = f"{'DUPLICATE_FUNC ' if demangledFuncSig.startswith('DUPLICATE_FUNC') else ''}IDA_GEN_PARSED virtual {returnTypesStr} {demangledFuncSig.removeprefix('DUPLICATE_FUNC').strip()}({newParamTypes})"
|
||||
elif demangledFuncSig.startswith("DUPLICATE_FUNC"):
|
||||
parsedFunc = ParseFuncStr(parsedClass, demangledFuncSig.removeprefix("DUPLICATE_FUNC").strip(), True)
|
||||
if not parsedFunc:
|
||||
continue
|
||||
|
||||
if parsedFunc.returnTypes:
|
||||
newParamTypes: str = CreateParamNamesForVTFunc(parsedFunc, False) if parsedFunc.params else ""
|
||||
returnTypes = [returnType.name for returnType in parsedFunc.returnTypes if returnType.name]
|
||||
returnTypesStr = ' '.join(returnTypes)
|
||||
demangledFuncSig = f"DUPLICATE_FUNC {returnTypesStr} {parsedFunc.funcName}({newParamTypes})"
|
||||
|
||||
parsedFunc = ParseFuncStr(parsedClass, demangledFuncSig, True)
|
||||
if not parsedFunc:
|
||||
continue
|
||||
|
||||
parsedClass.functions.append(parsedFunc)
|
||||
|
||||
def GetAllParsedClasses():
|
||||
ParseAllClasses()
|
||||
ParseAllClassVTFuncs()
|
||||
print(json.dumps(parsedClassesDict, indent=4))
|
@ -7,13 +7,13 @@ def Main():
|
||||
UI.OpenMainDlg()
|
||||
|
||||
# Reload modules to apply any changes
|
||||
from ExportClassH import Config, Utils, IDAUtils, ClassDefs, JSONGen#, RTTIAnalyzer, ClassParser, HeaderGen, ProjectManager
|
||||
from ExportClassH import Config, Utils, IDAUtils, ClassDefs, JSONGen, RTTIAnalyzer#, ClassParser, HeaderGen, ProjectManager
|
||||
importlib.reload(Config)
|
||||
importlib.reload(Utils)
|
||||
importlib.reload(IDAUtils)
|
||||
importlib.reload(ClassDefs)
|
||||
importlib.reload(JSONGen)
|
||||
# importlib.reload(RTTIAnalyzer)
|
||||
importlib.reload(RTTIAnalyzer)
|
||||
# importlib.reload(ClassParser)
|
||||
# importlib.reload(HeaderGen)
|
||||
# importlib.reload(ProjectManager)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
from ExportClassH import Config, HeaderGen, ClassParser
|
||||
from ExportClassH import ClassGen, Config, HeaderGen
|
||||
from ExportClassH.ClassDefs import ClassName
|
||||
|
||||
def FindClassDefInFile(className: str, filePath: str) -> tuple[bool, str, int, int, str]:
|
||||
@ -186,7 +186,7 @@ def ProcessExistingHeaders():
|
||||
targetClass = ClassName(className)
|
||||
|
||||
# Check if this class has any functions or variables to export
|
||||
allParsedClassVarsAndFuncs = ClassParser.GetAllParsedClassVarsAndFuncs(targetClass)
|
||||
allParsedClassVarsAndFuncs = ClassGen.GetAllParsedClassVarsAndFuncs(targetClass)
|
||||
hasContent = (
|
||||
len(allParsedClassVarsAndFuncs[0]) > 0 or
|
||||
len(allParsedClassVarsAndFuncs[1]) > 0 or
|
||||
|
@ -21,7 +21,7 @@ def GetVTablePtr(targetClass: ParsedClass, targetClassRTTIName: str = "") -> int
|
||||
# Use provided RTTI name if available (for templates), otherwise generate it
|
||||
if not targetClassRTTIName:
|
||||
# Check if this is a templated class
|
||||
typeDescriptorName: str = Utils.GetMangledTypePrefix(targetClass.parentNamespaces + targetClass.parentClasses, targetClass.name)
|
||||
typeDescriptorName: str = Utils.GetMangledTypePrefix(tuple(targetClass.parentNamespaces + targetClass.parentClasses), targetClass.name)
|
||||
else:
|
||||
# Use the provided RTTI name directly
|
||||
typeDescriptorName: str = targetClassRTTIName
|
||||
@ -43,7 +43,6 @@ def GetVTablePtr(targetClass: ParsedClass, targetClassRTTIName: str = "") -> int
|
||||
|
||||
typeDescriptorPatternAddr: int = ida_bytes.bin_search(rdataStartAddr, ida_ida.cvar.inf.max_ea, compiledIDAPattern, ida_bytes.BIN_SEARCH_FORWARD)
|
||||
if typeDescriptorPatternAddr == idc.BADADDR:
|
||||
print(f"Type descriptor pattern '{typeDescriptorName}' not found for {targetClass.fullClassStr}.")
|
||||
return 0
|
||||
|
||||
# Adjust to get RTTI type descriptor
|
||||
@ -89,7 +88,6 @@ def GetVTablePtr(targetClass: ParsedClass, targetClassRTTIName: str = "") -> int
|
||||
|
||||
return vtableAddr
|
||||
|
||||
print(f"Failed to locate vtable pointer for {targetClass.fullClassStr}.")
|
||||
return 0
|
||||
|
||||
def GetDemangledVTableFuncSigs(targetClass: ParsedClass, targetClassRTTIName: str = "") -> list[tuple[str, str]]:
|
||||
@ -99,7 +97,6 @@ def GetDemangledVTableFuncSigs(targetClass: ParsedClass, targetClassRTTIName: st
|
||||
"""
|
||||
vtablePtr: int = GetVTablePtr(targetClass, targetClassRTTIName)
|
||||
if not vtablePtr:
|
||||
print(f"Vtable pointer not found for {targetClass.fullClassStr}.")
|
||||
return []
|
||||
|
||||
demangledVTableFuncSigsList: list[tuple[str, str]] = []
|
||||
@ -114,7 +111,6 @@ def GetDemangledVTableFuncSigs(targetClass: ParsedClass, targetClassRTTIName: st
|
||||
if seg is None or seg.type != idaapi.SEG_CODE:
|
||||
break
|
||||
|
||||
# Force function decompilation to generate the full function type signature
|
||||
funcSig: str = idc.get_func_name(ptr)
|
||||
demangledFuncSig: str = Utils.DemangleSig(funcSig)
|
||||
demangledFuncSig = demangledFuncSig if demangledFuncSig else funcSig
|
||||
@ -126,8 +122,12 @@ def GetDemangledVTableFuncSigs(targetClass: ParsedClass, targetClassRTTIName: st
|
||||
|
||||
if demangledFuncSig != "_purecall":
|
||||
if " " not in demangledFuncSig:
|
||||
ida_hexrays.decompile(ptr)
|
||||
rawType = "IDA_GEN_TYPE " + idc.get_type(ptr)
|
||||
cfunc = ida_hexrays.decompile(ptr)
|
||||
tinfo = idaapi.tinfo_t()
|
||||
cfunc.get_func_type(tinfo)
|
||||
|
||||
funcType: str = idaapi.print_tinfo('', 0, 0, idaapi.PRTYPE_NOARGS, tinfo, '', '')
|
||||
rawType = "IDA_GEN_TYPE " + funcType
|
||||
if (demangledFuncSig, rawType) in demangledVTableFuncSigsList:
|
||||
demangledFuncSig = "DUPLICATE_FUNC " + demangledFuncSig
|
||||
|
||||
|
@ -55,9 +55,10 @@ def ExtractTypeTokensFromString(types: str) -> list[str]:
|
||||
templateDepth += 1
|
||||
currentWord += char
|
||||
elif char == '>':
|
||||
templateDepth -= 1
|
||||
if templateDepth > 0:
|
||||
templateDepth -= 1
|
||||
currentWord += char
|
||||
elif char.isspace() and templateDepth == 0:
|
||||
elif char == ' ' and templateDepth == 0:
|
||||
# Only split on spaces outside of templates
|
||||
if currentWord:
|
||||
result.append(currentWord)
|
||||
@ -139,7 +140,8 @@ def FindLastSpaceOutsideTemplates(s: str) -> int:
|
||||
if ch == '>':
|
||||
depth += 1
|
||||
elif ch == '<':
|
||||
depth -= 1
|
||||
if depth > 0:
|
||||
depth -= 1
|
||||
elif depth == 0 and ch == ' ':
|
||||
return i
|
||||
return -1
|
||||
@ -153,7 +155,8 @@ def FindLastClassSeparatorOutsideTemplates(s: str) -> int:
|
||||
if s[i] == '>':
|
||||
depth += 1
|
||||
elif s[i] == '<':
|
||||
depth -= 1
|
||||
if depth > 0:
|
||||
depth -= 1
|
||||
# Only if we're not inside a template.
|
||||
if depth == 0 and i > 0 and s[i-1:i+1] == "::":
|
||||
return i - 1 # return the index of the first colon
|
||||
@ -166,7 +169,7 @@ def FindLastClassSeparatorOutsideTemplates(s: str) -> int:
|
||||
def DemangleSig(sig: str) -> str:
|
||||
return idaapi.demangle_name(sig, idaapi.MNG_LONG_FORM)
|
||||
|
||||
def GetMangledTypePrefix(namespaces: tuple[str], className: str) -> str:
|
||||
def GetMangledTypePrefix(namespaces: tuple[str, ...], className: str) -> str:
|
||||
"""
|
||||
Get the appropriate mangled type prefix for a class name.
|
||||
For class "X" this would be ".?AVX@@"
|
||||
|
Reference in New Issue
Block a user