backup script changes

This commit is contained in:
EricPlayZ
2025-03-17 02:09:24 +02:00
parent d40310fd80
commit 0c5d0c76f9
8 changed files with 154 additions and 103 deletions

View File

@ -39,7 +39,7 @@ class ParsedFunction(Prodict):
parentNamespaces: List[str]
parentClasses: List[str]
funcName: str
params: List
params: List[ParsedParam]
const: bool
fullFuncSig: str

View File

@ -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"

View File

@ -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))

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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@@"