mirror of
https://github.com/EricPlayZ/EGameTools.git
synced 2025-07-18 17:37:53 +08:00
backup of scripts before json parser changes
This commit is contained in:
@ -1,48 +1,86 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
from ExportClassH import Utils
|
||||
from ExportClassH import Utils, HeaderGen
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ClassName:
|
||||
"""Split a potentially namespaced class name into namespace parts and class name."""
|
||||
namespaces: tuple[str] = field(default_factory=tuple[str])
|
||||
namespaces: tuple[str] = field(default_factory=tuple)
|
||||
classes: tuple[str] = field(default_factory=tuple)
|
||||
name: str = ""
|
||||
namespacedName: str = ""
|
||||
namespacedClassedName: str = ""
|
||||
templateParams: tuple["ClassName"] = field(default_factory=tuple)
|
||||
templatedName: str = ""
|
||||
templatedNamespacedClassedName: str = ""
|
||||
fullName: str = ""
|
||||
type: str = ""
|
||||
|
||||
def __init__(self, fullName: str):
|
||||
object.__setattr__(self, "namespaces", ())
|
||||
object.__setattr__(self, "classes", ())
|
||||
object.__setattr__(self, "name", "")
|
||||
object.__setattr__(self, "namespacedName", "")
|
||||
object.__setattr__(self, "namespacedClassedName", "")
|
||||
object.__setattr__(self, "templateParams", ())
|
||||
object.__setattr__(self, "templatedName", "")
|
||||
object.__setattr__(self, "templatedNamespacedClassedName", "")
|
||||
object.__setattr__(self, "fullName", "")
|
||||
object.__setattr__(self, "type", "")
|
||||
|
||||
fullName = (fullName or "").strip()
|
||||
fullName = fullName.strip()
|
||||
if not fullName:
|
||||
return
|
||||
|
||||
object.__setattr__(self, "fullName", fullName)
|
||||
types = Utils.ExtractTypesFromString(fullName)
|
||||
types = Utils.ExtractTypeTokensFromString(fullName)
|
||||
if len(types) > 1:
|
||||
if (types[0] in Utils.CLASS_TYPES):
|
||||
object.__setattr__(self, "type", types[0])
|
||||
fullName = types[1]
|
||||
elif (len(types) > 2 and types[0] in Utils.FUNC_QUALIFIERS and types[1] in Utils.CLASS_TYPES):
|
||||
object.__setattr__(self, "type", types[1])
|
||||
fullName = types[2]
|
||||
if (types[0].strip() in Utils.CLASS_TYPES):
|
||||
object.__setattr__(self, "type", types[0].strip())
|
||||
fullName = types[1].strip()
|
||||
elif (len(types) > 2 and types[0].strip() in Utils.FUNC_QUALIFIERS and types[1].strip() in Utils.CLASS_TYPES):
|
||||
object.__setattr__(self, "type", types[1].strip())
|
||||
fullName = types[2].strip()
|
||||
else:
|
||||
return
|
||||
|
||||
lastClassSeparatorIndex: int = Utils.FindLastClassSeparatorOutsideTemplates(fullName)
|
||||
namespacesStr: str = ""
|
||||
className: str = fullName
|
||||
templatedClassName: str = fullName
|
||||
if lastClassSeparatorIndex != -1:
|
||||
namespacesStr = fullName[:lastClassSeparatorIndex].strip()
|
||||
templatedClassName = fullName[lastClassSeparatorIndex+2:].strip()
|
||||
className = templatedClassName
|
||||
namespaces: list[str] = list(filter(None, namespacesStr.split("::")))
|
||||
classes: list[str] = []
|
||||
|
||||
parts = fullName.split("::")
|
||||
if len(parts) == 1:
|
||||
object.__setattr__(self, "name", parts[0])
|
||||
object.__setattr__(self, "namespacedName", self.name)
|
||||
else:
|
||||
object.__setattr__(self, "namespaces", tuple(parts[:-1]))
|
||||
object.__setattr__(self, "name", parts[-1])
|
||||
object.__setattr__(self, "namespacedName", f"{'::'.join(self.namespaces)}::{self.name}")
|
||||
parsedNamespaces: list[str] = []
|
||||
for namespace in namespaces:
|
||||
fullNamespace: str = "::".join(filter(None, ["::".join(parsedNamespaces), namespace]))
|
||||
if HeaderGen.IsClassGenerable(ClassName(fullNamespace)):
|
||||
classes = namespaces
|
||||
break
|
||||
parsedNamespaces.append(namespace)
|
||||
namespaces.pop(0)
|
||||
namespacesAndClasses: str = "::".join(filter(None, ["::".join(namespaces).strip(), "::".join(classes).strip()]))
|
||||
|
||||
templateParamsOpenIndex: int = templatedClassName.find('<')
|
||||
templateParamsCloseIndex: int = templatedClassName.rfind('>')
|
||||
|
||||
templateParamsList: list[ClassName] = []
|
||||
if templateParamsOpenIndex != -1 and templateParamsCloseIndex != -1:
|
||||
templateParams: str = templatedClassName[templateParamsOpenIndex + 1:templateParamsCloseIndex].strip()
|
||||
templateParamsStrList: list[str] = Utils.SplitByCommaOutsideTemplates(templateParams)
|
||||
templateParamsList = [ClassName(templateParamStr) for templateParamStr in templateParamsStrList if templateParamStr]
|
||||
className = templatedClassName[:templateParamsOpenIndex].strip()
|
||||
|
||||
object.__setattr__(self, "namespaces", tuple(namespaces))
|
||||
object.__setattr__(self, "classes", tuple(classes))
|
||||
object.__setattr__(self, "name", className)
|
||||
object.__setattr__(self, "namespacedClassedName", "::".join(filter(None, [namespacesAndClasses, className])).strip())
|
||||
object.__setattr__(self, "templateParams", tuple(templateParamsList))
|
||||
object.__setattr__(self, "templatedName", templatedClassName)
|
||||
object.__setattr__(self, "templatedNamespacedClassedName", fullName)
|
||||
|
||||
virtualFuncPlaceholderCounter: int = 0 # Counter for placeholder virtual functions
|
||||
virtualFuncDuplicateCounter: dict[str, int] = {} # Counter for duplicate virtual functions
|
||||
|
@ -4,18 +4,25 @@ import idc
|
||||
from typing import Optional
|
||||
|
||||
from ExportClassH import Utils, Config, RTTIAnalyzer
|
||||
from ExportClassH.ClassDefs import ClassName, ParsedFunction, ParsedClassVar
|
||||
|
||||
# Global caches
|
||||
parsedClassVarsByClass: dict[str, list[ParsedClassVar]] = {} # Cache of parsed class vars by class name
|
||||
parsedVTableFuncsByClass: dict[str, list[ParsedFunction]] = {} # Cache of parsed functions by class name
|
||||
parsedFuncsByClass: dict[str, list[ParsedFunction]] = {} # Cache of parsed functions by class name
|
||||
allParsedFuncs: list[ParsedFunction] = []
|
||||
parsedClassVarsByClass: dict[str, list[dict]] = {} # Cache of parsed class vars by class name
|
||||
parsedVTableFuncsByClass: dict[str, list[dict]] = {} # Cache of parsed functions by class name
|
||||
parsedFuncsByClass: dict[str, list[dict]] = {} # Cache of parsed functions by class name
|
||||
allParsedFuncs: list[dict] = []
|
||||
unparsedExportedSigs: list[str] = []
|
||||
allClassVarsAreParsed = False # Flag to indicate if all class vars have been parsed
|
||||
allFuncsAreParsed = False # Flag to indicate if all functions have been parsed
|
||||
|
||||
def CreateParamNamesForVTFunc(parsedFunc: ParsedFunction, skipFirstParam: bool) -> str:
|
||||
def IsClassGenerable(cls: dict) -> bool:
|
||||
"""
|
||||
Check if a class has any parsable elements (class vars, vtable functions, regular functions).
|
||||
Returns True if the class is generable, False if it should be treated as a namespace.
|
||||
"""
|
||||
(parsedVars, parsedVTFuncs, parsedFuncs) = GetAllParsedClassVarsAndFuncs(cls)
|
||||
return len(parsedVars) > 0 or len(parsedVTFuncs) > 0 or len(parsedFuncs) > 0
|
||||
|
||||
def CreateParamNamesForVTFunc(parsedFunc: dict, skipFirstParam: bool) -> str:
|
||||
paramsList: list[str] = [param.fullName for param in parsedFunc.params if param.fullName]
|
||||
if len(paramsList) == 1 and paramsList[0] == "void":
|
||||
return "void"
|
||||
@ -45,19 +52,19 @@ def GetClassTypeFromParsedSigs(targetClass: ClassName, allParsedElements: tuple[
|
||||
# Check class vars first
|
||||
for parsedClassVar in parsedClassVars:
|
||||
if (parsedClassVar.varType and
|
||||
parsedClassVar.varType.namespacedName == targetClass.namespacedName and
|
||||
parsedClassVar.varType.namespacedClassedName == targetClass.namespacedClassedName and
|
||||
parsedClassVar.varType.type):
|
||||
return parsedClassVar.varType.type
|
||||
# Check vtable functions next
|
||||
for parsedVTFunc in parsedVtFuncs:
|
||||
if (parsedVTFunc.returnType and
|
||||
parsedVTFunc.returnType.namespacedName == targetClass.namespacedName and
|
||||
parsedVTFunc.returnType.namespacedClassedName == targetClass.namespacedClassedName and
|
||||
parsedVTFunc.returnType.type):
|
||||
return parsedVTFunc.returnType.type
|
||||
# Check all parsed functions last
|
||||
for parsedFunc in allParsedFuncs:
|
||||
if (parsedFunc.returnType and
|
||||
parsedFunc.returnType.namespacedName == targetClass.namespacedName and
|
||||
parsedFunc.returnType.namespacedClassedName == targetClass.namespacedClassedName and
|
||||
parsedFunc.returnType.type):
|
||||
return parsedFunc.returnType.type
|
||||
|
||||
@ -86,7 +93,7 @@ def GetDemangledExportedSigs() -> list[str]:
|
||||
sigs_set.add(demangledExportedSig)
|
||||
return list(sigs_set)
|
||||
|
||||
def GetParsedClassVars(targetClass: Optional[ClassName] = None) -> list[ParsedClassVar]:
|
||||
def GetParsedClassVars(targetClass: dict = {}) -> list[ParsedClassVar]:
|
||||
"""
|
||||
Collect and parse all class var signatures from the IDA database.
|
||||
If target_class is provided, only return class vars for that class.
|
||||
@ -126,7 +133,7 @@ def GetParsedClassVars(targetClass: Optional[ClassName] = None) -> list[ParsedCl
|
||||
print(f"Failed parsing class var sig: \"{sig}\"")
|
||||
continue
|
||||
|
||||
parsedClassVarsByClass.setdefault(parsedVar.className.namespacedName, []).append(parsedVar)
|
||||
parsedClassVarsByClass.setdefault(parsedVar.className.namespacedClassedName, []).append(parsedVar)
|
||||
|
||||
allClassVarsAreParsed = True
|
||||
|
||||
@ -146,7 +153,7 @@ def GetParsedClassVars(targetClass: Optional[ClassName] = None) -> list[ParsedCl
|
||||
if targetClass is None:
|
||||
return [var for vars_list in parsedClassVarsByClass.values() for var in vars_list]
|
||||
else:
|
||||
return parsedClassVarsByClass.get(targetClass.namespacedName, [])
|
||||
return parsedClassVarsByClass.get(targetClass.namespacedClassedName, [])
|
||||
|
||||
def GetParsedVTableFuncs(targetClass: ClassName) -> list[ParsedFunction]:
|
||||
"""
|
||||
@ -157,27 +164,27 @@ def GetParsedVTableFuncs(targetClass: ClassName) -> list[ParsedFunction]:
|
||||
global parsedVTableFuncsByClass
|
||||
|
||||
if targetClass not in parsedVTableFuncsByClass:
|
||||
parsedVTableFuncsByClass[targetClass.namespacedName] = []
|
||||
parsedVTableFuncsByClass[targetClass.namespacedClassedName] = []
|
||||
|
||||
for (demangledFuncSig, rawType) in RTTIAnalyzer.GetDemangledVTableFuncSigs(targetClass):
|
||||
if rawType:
|
||||
parsedFunc: ParsedFunction = ParsedFunction(rawType, True)
|
||||
if parsedFunc.returnType:
|
||||
newParamTypes: str = CreateParamNamesForVTFunc(parsedFunc, True) if parsedFunc.params else ""
|
||||
demangledFuncSig = f"{'DUPLICATE_FUNC ' if demangledFuncSig.startswith('DUPLICATE_FUNC') else ''}IDA_GEN_PARSED virtual {parsedFunc.returnType.namespacedName} {demangledFuncSig.removeprefix('DUPLICATE_FUNC').strip()}({newParamTypes})"
|
||||
demangledFuncSig = f"{'DUPLICATE_FUNC ' if demangledFuncSig.startswith('DUPLICATE_FUNC') else ''}IDA_GEN_PARSED virtual {parsedFunc.returnType.namespacedClassedName} {demangledFuncSig.removeprefix('DUPLICATE_FUNC').strip()}({newParamTypes})"
|
||||
elif demangledFuncSig.startswith("DUPLICATE_FUNC"):
|
||||
parsedFunc: ParsedFunction = ParsedFunction(demangledFuncSig.removeprefix("DUPLICATE_FUNC").strip(), True)
|
||||
if parsedFunc.returnType:
|
||||
newParamTypes: str = CreateParamNamesForVTFunc(parsedFunc, False) if parsedFunc.params else ""
|
||||
demangledFuncSig = f"DUPLICATE_FUNC {parsedFunc.returnType.namespacedName} {parsedFunc.funcName}({newParamTypes})"
|
||||
demangledFuncSig = f"DUPLICATE_FUNC {parsedFunc.returnType.namespacedClassedName} {parsedFunc.funcName}({newParamTypes})"
|
||||
|
||||
parsedFunc: ParsedFunction = ParsedFunction(demangledFuncSig, True)
|
||||
if not parsedFunc.className:
|
||||
object.__setattr__(parsedFunc, "className", targetClass)
|
||||
|
||||
parsedVTableFuncsByClass[targetClass.namespacedName].append(parsedFunc)
|
||||
parsedVTableFuncsByClass[targetClass.namespacedClassedName].append(parsedFunc)
|
||||
|
||||
return parsedVTableFuncsByClass.get(targetClass.namespacedName, [])
|
||||
return parsedVTableFuncsByClass.get(targetClass.namespacedClassedName, [])
|
||||
|
||||
def GetParsedFuncs(targetClass: Optional[ClassName] = None) -> list[ParsedFunction]:
|
||||
"""
|
||||
@ -209,7 +216,7 @@ def GetParsedFuncs(targetClass: Optional[ClassName] = None) -> list[ParsedFuncti
|
||||
if not parsedFunc.type or not parsedFunc.className:
|
||||
print(f"Failed parsing func sig: \"{demangledFuncSig}\"")
|
||||
continue
|
||||
parsedFuncsByClass.setdefault(parsedFunc.className.namespacedName, []).append(parsedFunc)
|
||||
parsedFuncsByClass.setdefault(parsedFunc.className.namespacedClassedName, []).append(parsedFunc)
|
||||
allFuncsAreParsed = True
|
||||
try:
|
||||
os.makedirs(Config.CACHE_OUTPUT_PATH, exist_ok=True)
|
||||
@ -225,23 +232,23 @@ def GetParsedFuncs(targetClass: Optional[ClassName] = None) -> list[ParsedFuncti
|
||||
if targetClass is None:
|
||||
return [pf for funcList in parsedFuncsByClass.values() for pf in funcList]
|
||||
else:
|
||||
return parsedFuncsByClass.get(targetClass.namespacedName, [])
|
||||
return parsedFuncsByClass.get(targetClass.namespacedClassedName, [])
|
||||
|
||||
def GetAllParsedClassVarsAndFuncs(targetClass: ClassName) -> tuple[list[ParsedClassVar], list[ParsedFunction], list[ParsedFunction]]:
|
||||
def GetAllParsedClassVarsAndFuncs(cls: dict) -> tuple[list[dict], list[dict], list[dict]]:
|
||||
global allParsedFuncs
|
||||
|
||||
parsedVTableClassFuncs: list[ParsedFunction] = GetParsedVTableFuncs(targetClass)
|
||||
parsedVTableClassFuncs: list[dict] = GetParsedVTableFuncs(cls)
|
||||
if not parsedVTableClassFuncs:
|
||||
print(f"No matching VTable function signatures were found for {targetClass.namespacedName}.")
|
||||
print(f"No matching VTable function signatures were found for {cls.namespacedClassedName}.")
|
||||
|
||||
parsedClassFuncs: list[ParsedFunction] = GetParsedFuncs(targetClass)
|
||||
parsedClassFuncs: list[dict] = GetParsedFuncs(cls)
|
||||
if not parsedClassFuncs:
|
||||
print(f"No matching function signatures were found for {targetClass.namespacedName}.")
|
||||
print(f"No matching function signatures were found for {cls.namespacedClassedName}.")
|
||||
allParsedFuncs = GetParsedFuncs()
|
||||
|
||||
parsedClassVars: list[ParsedClassVar] = GetParsedClassVars(targetClass)
|
||||
parsedClassVars: list[dict] = GetParsedClassVars(cls)
|
||||
if not parsedClassVars:
|
||||
print(f"No matching class var signatures were found for {targetClass.namespacedName}.")
|
||||
print(f"No matching class var signatures were found for {cls.namespacedClassedName}.")
|
||||
|
||||
# Get non-vtable methods
|
||||
vTableFuncsSet: set[str] = {pf.fullFuncSig for pf in parsedVTableClassFuncs}
|
||||
@ -250,11 +257,9 @@ def GetAllParsedClassVarsAndFuncs(targetClass: ClassName) -> tuple[list[ParsedCl
|
||||
if parsedFunc.fullFuncSig not in vTableFuncsSet
|
||||
]
|
||||
|
||||
allParsedElements = (parsedClassVars, parsedVTableClassFuncs, finalParsedClassFuncs)
|
||||
allParsedElements = parsedClassVars, parsedVTableClassFuncs, finalParsedClassFuncs
|
||||
|
||||
# Get and set class type if available
|
||||
classType: str = GetClassTypeFromParsedSigs(targetClass, allParsedElements)
|
||||
if classType:
|
||||
object.__setattr__(targetClass, "type", classType)
|
||||
# Set class type if available
|
||||
cls["type"] = GetClassTypeFromParsedSigs(cls, allParsedElements)
|
||||
|
||||
return allParsedElements
|
@ -3,49 +3,14 @@ import os
|
||||
from ExportClassH import Utils, Config, ClassParser
|
||||
from ExportClassH.ClassDefs import ClassName, ParsedFunction, ParsedClassVar
|
||||
|
||||
def IsClassGenerable(className: ClassName) -> bool:
|
||||
"""
|
||||
Check if a class has any parsable elements (class vars, vtable functions, regular functions).
|
||||
Returns True if the class is generable, False if it should be treated as a namespace.
|
||||
"""
|
||||
parsedVars = ClassParser.GetParsedClassVars(className)
|
||||
parsedVtFuncs = ClassParser.GetParsedVTableFuncs(className) if className else []
|
||||
parsedFuncs = ClassParser.GetParsedFuncs(className)
|
||||
|
||||
return len(parsedVars) > 0 or len(parsedVtFuncs) > 0 or len(parsedFuncs) > 0
|
||||
|
||||
def IdentifyClassHierarchy(targetClass: ClassName) -> list[tuple[ClassName, bool]]:
|
||||
"""
|
||||
Given a class name with namespace/nested parts, identify which parts are classes
|
||||
and which are namespaces.
|
||||
|
||||
Returns a list of tuples (ClassName, isClass) for each part of the hierarchy.
|
||||
"""
|
||||
if not targetClass.namespaces:
|
||||
return [(targetClass, IsClassGenerable(targetClass))]
|
||||
|
||||
hierarchy = []
|
||||
|
||||
# Check each part of the namespace to see if it's a class
|
||||
currentNamespace = []
|
||||
for part in targetClass.namespaces:
|
||||
currentNamespace.append(part)
|
||||
partClass = ClassName("::".join(currentNamespace))
|
||||
|
||||
isClass = IsClassGenerable(partClass)
|
||||
hierarchy.append((partClass, isClass))
|
||||
|
||||
# Add the target class at the end
|
||||
hierarchy.append((targetClass, IsClassGenerable(targetClass)))
|
||||
|
||||
return hierarchy
|
||||
processedClasses: set[str] = set()
|
||||
|
||||
currentAccess: str = "public"
|
||||
def GenerateClassVarCode(classVar: ParsedClassVar) -> str:
|
||||
def GenerateClassVarCode(classVar: ParsedClassVar) -> list[str]:
|
||||
"""Generate code for a single class variable."""
|
||||
global currentAccess
|
||||
|
||||
access: str = f"{classVar.access}:\n\t" if classVar.access else "\t"
|
||||
access: str = f"{classVar.access}:" if classVar.access else ""
|
||||
if currentAccess == classVar.access:
|
||||
access = "\t"
|
||||
else:
|
||||
@ -61,15 +26,21 @@ def GenerateClassVarCode(classVar: ParsedClassVar) -> str:
|
||||
varType: str = ""
|
||||
|
||||
classVarSig: str = f"{varType}{classVar.varName}"
|
||||
return f"{access}{classVarSig};"
|
||||
|
||||
classVarLines: list[str] = []
|
||||
if access:
|
||||
classVarLines.append(access)
|
||||
if classVarSig:
|
||||
classVarLines.append(f"\t{classVarSig};")
|
||||
return classVarLines
|
||||
|
||||
def GenerateClassFuncCode(func: ParsedFunction, vtFuncIndex: int = 0) -> str:
|
||||
def GenerateClassFuncCode(func: ParsedFunction, vtFuncIndex: int = 0) -> list[str]:
|
||||
"""Generate code for a single class method."""
|
||||
global currentAccess
|
||||
|
||||
access: str = f"{func.access}:\n\t" if func.access else "\t"
|
||||
access: str = f"{func.access}:" if func.access else ""
|
||||
if currentAccess == func.access:
|
||||
access = "\t"
|
||||
access = ""
|
||||
else:
|
||||
currentAccess = func.access
|
||||
|
||||
@ -106,7 +77,13 @@ def GenerateClassFuncCode(func: ParsedFunction, vtFuncIndex: int = 0) -> str:
|
||||
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})"
|
||||
return f"{access}{funcSig};"
|
||||
|
||||
classFuncLines: list[str] = []
|
||||
if access:
|
||||
classFuncLines.append(access)
|
||||
if funcSig:
|
||||
classFuncLines.append(f"\t{funcSig};")
|
||||
return classFuncLines
|
||||
|
||||
def GenerateClassContent(allParsedElements: tuple[list[ParsedClassVar], list[ParsedFunction], list[ParsedFunction]]) -> list[str]:
|
||||
"""
|
||||
@ -129,7 +106,7 @@ def GenerateClassContent(allParsedElements: tuple[list[ParsedClassVar], list[Par
|
||||
for classVar in parsedVars:
|
||||
if not firstVarOrFuncAccess:
|
||||
firstVarOrFuncAccess = classVar.access
|
||||
contentLines.append(GenerateClassVarCode(classVar))
|
||||
contentLines.extend(GenerateClassVarCode(classVar))
|
||||
|
||||
# Add newline between sections if both exist
|
||||
if parsedVars and (parsedVtFuncs or parsedFuncs):
|
||||
@ -139,7 +116,7 @@ def GenerateClassContent(allParsedElements: tuple[list[ParsedClassVar], list[Par
|
||||
for index, vTableFunc in enumerate(parsedVtFuncs):
|
||||
if not firstVarOrFuncAccess:
|
||||
firstVarOrFuncAccess = vTableFunc.access
|
||||
contentLines.append(GenerateClassFuncCode(vTableFunc, index))
|
||||
contentLines.extend(GenerateClassFuncCode(vTableFunc, index))
|
||||
|
||||
# Add newline between sections if both exist
|
||||
if parsedVtFuncs and parsedFuncs:
|
||||
@ -149,7 +126,7 @@ def GenerateClassContent(allParsedElements: tuple[list[ParsedClassVar], list[Par
|
||||
for func in parsedFuncs:
|
||||
if not firstVarOrFuncAccess:
|
||||
firstVarOrFuncAccess = func.access
|
||||
contentLines.append(GenerateClassFuncCode(func))
|
||||
contentLines.extend(GenerateClassFuncCode(func))
|
||||
|
||||
contentLines.append("#pragma endregion")
|
||||
|
||||
@ -159,36 +136,37 @@ def GenerateClassContent(allParsedElements: tuple[list[ParsedClassVar], list[Par
|
||||
|
||||
return contentLines
|
||||
|
||||
def GenerateClassDefinition(targetClass: ClassName, allParsedElements: tuple[list[ParsedClassVar], list[ParsedFunction], list[ParsedFunction]]) -> list[str]:
|
||||
def GenerateClassDefinition(targetClass: ClassName, forwardDeclare: bool = False) -> list[str]:
|
||||
"""Generate a class definition from a list of methods."""
|
||||
parsedVars, parsedVtFuncs, parsedFuncs = allParsedElements
|
||||
if not parsedVars and not parsedVtFuncs and not parsedFuncs:
|
||||
return []
|
||||
|
||||
classContent: list[str] = GenerateClassContent(allParsedElements)
|
||||
|
||||
classLines: list[str] = []
|
||||
if classContent:
|
||||
classLines.extend(classContent)
|
||||
classDefLines: list[str] = [f"{targetClass.type} {targetClass.name}{' {' if not forwardDeclare else ';'}"]
|
||||
if not forwardDeclare:
|
||||
if targetClass.type == "class":
|
||||
classDefLines.append("public:")
|
||||
classDefLines.append("};")
|
||||
return classDefLines
|
||||
|
||||
classLines.insert(0, f"{targetClass.type if targetClass.type else 'class'} {targetClass.name}")
|
||||
if classContent:
|
||||
classLines[0] = f"{classLines[0]} {{"
|
||||
if targetClass.type == "struct":
|
||||
classLines[1] = "public:"
|
||||
classLines.append("};")
|
||||
else:
|
||||
classLines[0] = f"{classLines[0]};"
|
||||
|
||||
return classLines
|
||||
|
||||
def GenerateHeaderCode(targetClass: ClassName, allParsedElements: tuple[list[ParsedClassVar], list[ParsedFunction], list[ParsedFunction]]) -> list[str]:
|
||||
def GenerateHeaderCode(targetClass: ClassName) -> list[str]:
|
||||
"""Generate header code for a standard class (not nested)."""
|
||||
classDefinition = GenerateClassDefinition(targetClass, allParsedElements)
|
||||
if not classDefinition:
|
||||
return []
|
||||
# Reset processed classes for this generation
|
||||
global processedClasses, forwardDeclarations
|
||||
processedClasses = set()
|
||||
forwardDeclarations = set()
|
||||
|
||||
# Wrap in namespace blocks if needed
|
||||
# Mark target class as processed to avoid self-dependency issues
|
||||
processedClasses.add(targetClass.namespacedClassedName)
|
||||
|
||||
# Get all parsed elements for the target class
|
||||
allParsedElements = ClassParser.GetAllParsedClassVarsAndFuncs(targetClass)
|
||||
|
||||
# Generate the target class definition
|
||||
classContent = GenerateClassContent(allParsedElements)
|
||||
classDefinition = GenerateClassDefinition(targetClass)
|
||||
|
||||
fullClassDefinition = classDefinition
|
||||
if (classContent):
|
||||
fullClassDefinition = fullClassDefinition[:len(classDefinition) - 1] + classContent + fullClassDefinition[len(classDefinition) - 1:]
|
||||
|
||||
# Wrap target class in namespace blocks if needed
|
||||
if targetClass.namespaces:
|
||||
namespaceCode = []
|
||||
indentLevel = ""
|
||||
@ -197,19 +175,30 @@ def GenerateHeaderCode(targetClass: ClassName, allParsedElements: tuple[list[Par
|
||||
for namespace in targetClass.namespaces:
|
||||
namespaceCode.append(f"{indentLevel}namespace {namespace} {{")
|
||||
indentLevel += "\t"
|
||||
|
||||
for cls in targetClass.classes:
|
||||
clsType: str = ClassParser.GetClassTypeFromParsedSigs(ClassName(targetClass.namespacedClassedName), allParsedElements)
|
||||
namespaceCode.append(f"{indentLevel}{clsType if clsType else 'class'} {cls} {{")
|
||||
indentLevel += "\t"
|
||||
|
||||
indentedClassDefinition = [f"{indentLevel}{line}" for line in classDefinition]
|
||||
indentedClassDefinition = [f"{indentLevel if '#pragma' not in line else ''}{line}" for line in fullClassDefinition]
|
||||
namespaceCode.extend(indentedClassDefinition)
|
||||
|
||||
for cls in reversed(targetClass.classes):
|
||||
indentLevel = indentLevel[:-1] # Remove one level of indentation
|
||||
namespaceCode.append(f"{indentLevel}}}")
|
||||
|
||||
for namespace in reversed(targetClass.namespaces):
|
||||
indentLevel = indentLevel[:-1] # Remove one level of indentation
|
||||
namespaceCode.append(f"{indentLevel}}}")
|
||||
|
||||
classDefinition = namespaceCode
|
||||
fullClassDefinition = namespaceCode
|
||||
|
||||
# Combine all parts of the header
|
||||
headerParts = ["#pragma once", r"#include <EGSDK\Imports.h>", ""]
|
||||
headerParts.extend(classDefinition)
|
||||
|
||||
# Add the target class definition
|
||||
headerParts.extend(fullClassDefinition)
|
||||
|
||||
return headerParts
|
||||
|
||||
@ -249,13 +238,20 @@ def ExportClassHeader(targetClass: ClassName):
|
||||
Generate and save a C++ header file for the target class.
|
||||
Handles multiple levels of nested classes and also generates dependencies.
|
||||
"""
|
||||
# Get the parsed elements for the target class
|
||||
allParsedElements = ClassParser.GetAllParsedClassVarsAndFuncs(targetClass)
|
||||
global processedClasses
|
||||
|
||||
# Skip if we've already processed this class
|
||||
if targetClass.namespacedClassedName in processedClasses:
|
||||
print(f"Already processed class {targetClass.namespacedClassedName}, skipping.")
|
||||
return
|
||||
|
||||
# Add to processed set to prevent infinite recursion
|
||||
processedClasses.add(targetClass.namespacedClassedName)
|
||||
|
||||
# Generate the header code
|
||||
headerCodeLines = GenerateHeaderCode(targetClass, allParsedElements)
|
||||
headerCodeLines = GenerateHeaderCode(targetClass)
|
||||
if not headerCodeLines:
|
||||
print(f"No functions were found for class {targetClass.namespacedName}, therefore will not generate.")
|
||||
print(f"No functions were found for class {targetClass.namespacedClassedName}, therefore will not generate.")
|
||||
return
|
||||
headerCode: str = "\n".join(headerCodeLines)
|
||||
|
||||
|
84
_IDAScripts/ExportClassH/JSONGen.py
Normal file
84
_IDAScripts/ExportClassH/JSONGen.py
Normal file
@ -0,0 +1,84 @@
|
||||
from ExportClassH import Utils, ClassParser
|
||||
|
||||
def SplitTypeFromName(fullName: str) -> tuple[str, str]:
|
||||
CLASS_TYPES = ["class", "struct", "enum", "union"]
|
||||
FUNC_QUALIFIERS = ["virtual", "static", "inline", "explicit", "friend"]
|
||||
|
||||
parts = Utils.ExtractTypeTokensFromString(fullName)
|
||||
if not parts:
|
||||
return "", ""
|
||||
|
||||
if len(parts) > 1:
|
||||
if parts[0] in CLASS_TYPES:
|
||||
return parts[0], parts[1]
|
||||
elif len(parts) > 2 and parts[0] in FUNC_QUALIFIERS and parts[1] in CLASS_TYPES:
|
||||
return parts[1], parts[2]
|
||||
|
||||
return "", fullName
|
||||
|
||||
def ExtractClassNameAndTemplateParams(templatedClassName: str) -> tuple[str, list[str]]:
|
||||
templateParams = []
|
||||
className = templatedClassName
|
||||
|
||||
templateOpen = templatedClassName.find('<')
|
||||
templateClose = templatedClassName.rfind('>')
|
||||
|
||||
if templateOpen != -1 and templateClose != -1:
|
||||
className = templatedClassName[:templateOpen].strip()
|
||||
paramsStr = templatedClassName[templateOpen + 1:templateClose].strip()
|
||||
|
||||
# Split by commas, but only those outside of nested templates
|
||||
templateParams = Utils.SplitByCommaOutsideTemplates(paramsStr)
|
||||
|
||||
return className, templateParams
|
||||
|
||||
def ParseClassStr(fullName: str):
|
||||
classInfo = {
|
||||
"type": "",
|
||||
"name": "",
|
||||
"templateParams": [],
|
||||
"parentNamespace": [],
|
||||
"parentClass": []
|
||||
}
|
||||
|
||||
# Strip whitespace
|
||||
fullName = fullName.strip()
|
||||
if not fullName:
|
||||
return classInfo
|
||||
|
||||
# Extract type (struct, class, etc.)
|
||||
typeAndName = SplitTypeFromName(fullName)
|
||||
if not typeAndName[0]:
|
||||
return classInfo
|
||||
|
||||
classInfo["type"] = typeAndName[0]
|
||||
templatedClassNameWithNS = typeAndName[1]
|
||||
|
||||
# Split into namespaced parts and the final class name with templates
|
||||
lastClassSeparatorIndex = Utils.FindLastClassSeparatorOutsideTemplates(templatedClassNameWithNS)
|
||||
namespacesAndClasses = ""
|
||||
templatedClassName = ""
|
||||
|
||||
if lastClassSeparatorIndex != -1:
|
||||
namespacesAndClasses = templatedClassNameWithNS[:lastClassSeparatorIndex].strip()
|
||||
templatedClassName = templatedClassNameWithNS[lastClassSeparatorIndex+2:].strip()
|
||||
|
||||
# Extract template parameters
|
||||
className, templateParams = ExtractClassNameAndTemplateParams(templatedClassName)
|
||||
classInfo["name"] = className
|
||||
classInfo["templateParams"] = templateParams
|
||||
|
||||
# Split namespaces and classes - for this example we'll use a simple approach
|
||||
# where we assume the first part(s) are namespaces and later parts are parent classes
|
||||
if namespacesAndClasses:
|
||||
allParts = namespacesAndClasses.split("::")
|
||||
continueOnlyWithClasses: bool = False
|
||||
for part in allParts:
|
||||
if not ClassParser.IsClassGenerable(classInfo) and not continueOnlyWithClasses:
|
||||
classInfo["parentNamespace"].append(part)
|
||||
else:
|
||||
if not continueOnlyWithClasses:
|
||||
continueOnlyWithClasses = True
|
||||
classInfo["parentClass"].append(part)
|
||||
|
||||
return classInfo
|
@ -43,7 +43,7 @@ def GetVTablePtr(targetClass: ClassName, 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.namespacedName}.")
|
||||
print(f"Type descriptor pattern '{typeDescriptorName}' not found for {targetClass.namespacedClassedName}.")
|
||||
return 0
|
||||
|
||||
# Adjust to get RTTI type descriptor
|
||||
@ -89,7 +89,7 @@ def GetVTablePtr(targetClass: ClassName, targetClassRTTIName: str = "") -> int:
|
||||
|
||||
return vtableAddr
|
||||
|
||||
print(f"Failed to locate vtable pointer for {targetClass.namespacedName}.")
|
||||
print(f"Failed to locate vtable pointer for {targetClass.namespacedClassedName}.")
|
||||
return 0
|
||||
|
||||
def GetDemangledVTableFuncSigs(targetClass: ClassName, targetClassRTTIName: str = "") -> list[tuple[str, str]]:
|
||||
@ -99,7 +99,7 @@ def GetDemangledVTableFuncSigs(targetClass: ClassName, targetClassRTTIName: str
|
||||
"""
|
||||
vtablePtr: int = GetVTablePtr(targetClass, targetClassRTTIName)
|
||||
if not vtablePtr:
|
||||
print(f"Vtable pointer not found for {targetClass.namespacedName}.")
|
||||
print(f"Vtable pointer not found for {targetClass.namespacedClassedName}.")
|
||||
return []
|
||||
|
||||
demangledVTableFuncSigsList: list[tuple[str, str]] = []
|
||||
|
@ -7,8 +7,6 @@ import idautils
|
||||
import idc
|
||||
|
||||
IDA_NALT_ENCODING = ida_nalt.get_default_encoding_idx(ida_nalt.BPU_1B)
|
||||
CLASS_TYPES = ("class", "struct", "enum", "union")
|
||||
FUNC_QUALIFIERS = ("virtual", "static")
|
||||
|
||||
def FixTypeSpacing(type: str) -> str:
|
||||
"""Fix spacing for pointers/references, commas, and angle brackets."""
|
||||
@ -30,13 +28,13 @@ def ReplaceIDATypes(type: str) -> str:
|
||||
"""Replace IDA types with normal ones"""
|
||||
return type.replace("unsigned __int64", "uint64_t").replace("_QWORD", "uint64_t").replace("__int64", "int64_t").replace("unsigned int", "uint32_t")
|
||||
|
||||
def ExtractTypesFromString(types: str) -> list[str]:
|
||||
def ExtractTypeTokensFromString(types: str) -> list[str]:
|
||||
"""Extract potential type names from a string, properly handling template types."""
|
||||
if not types:
|
||||
return []
|
||||
|
||||
types = FixTypeSpacing(types)
|
||||
result = []
|
||||
result: list[str] = []
|
||||
currentWord = ""
|
||||
templateDepth = 0
|
||||
|
||||
@ -60,7 +58,7 @@ def ExtractTypesFromString(types: str) -> list[str]:
|
||||
result.append(currentWord)
|
||||
|
||||
# Filter out empty strings
|
||||
return [word for word in result if word]
|
||||
return [word.strip() for word in result if word]
|
||||
|
||||
def SplitByCommaOutsideTemplates(params: str) -> list[str]:
|
||||
parts = []
|
||||
|
@ -2,47 +2,48 @@
|
||||
#include <EGSDK\Imports.h>
|
||||
|
||||
class cbs {
|
||||
public:
|
||||
#pragma region GENERATED by ExportClassToCPPH.py
|
||||
public:
|
||||
GAME_IMPORT class cbs::PrefabManager* g_PrefabManager;
|
||||
|
||||
GAME_IMPORT bool IsInDynamicRoot(class cbs::CPointer<class cbs::CEntity>, bool);
|
||||
GAME_IMPORT void GetPCIDHierarchyPath(class ttl::span<class cbs::PrefabEntityComponent const* const, 4294967295>, uint32_t, class ttl::string_base<char>&);
|
||||
GAME_IMPORT enum cbs::EntitySpawnOrigin GetEntitySpawnOrigin(class cbs::PrefabEntityComponent const*);
|
||||
GAME_IMPORT bool CreateEntityFromPrefabDeferred(class cbs::CPointer<class cbs::CEntity>&, class ttl::string_const<char>, class ILevel*, class mtx34 const&, class ttl::string_const<char>, class Replication::CreateObjectOptions, bool, bool);
|
||||
GAME_IMPORT bool GetPrefabEntityComponents(class cbs::CEntity const*, class ttl::vector<class cbs::PrefabEntityComponent const*, class ttl::vector_allocators::heap_allocator<class cbs::PrefabEntityComponent const*>, 16>&);
|
||||
GAME_IMPORT void GetPECHierarchyPath(class ttl::span<class cbs::PrefabEntityComponent const* const, 4294967295>, uint32_t, class ttl::string_base<char>&);
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(class cbs::CPcidPath const&, enum cbs::pcid_t);
|
||||
GAME_IMPORT class CLevel* GetCLevel(struct cbs::WorldIndex);
|
||||
GAME_IMPORT void GetEntityDebugHierarchyPath(class cbs::CEntity const*, class ttl::string_base<char>&);
|
||||
GAME_IMPORT bool operator==(class cbs::CPointer<class cbs::CEntity>, class cbs::CEntityPointer);
|
||||
GAME_IMPORT class IPhysicsManager* GetPhysicsManager(struct cbs::WorldIndex);
|
||||
GAME_IMPORT class cbs::EnumPropertyManager& GetEnumPropertyManager();
|
||||
GAME_IMPORT bool operator!=(class cbs::CEntity const*, class cbs::CEntityPointer);
|
||||
GAME_IMPORT bool operator!=(class cbs::CPointer<class cbs::CEntity>, class cbs::CEntityPointer);
|
||||
GAME_IMPORT float GetTime(struct cbs::WorldIndex);
|
||||
GAME_IMPORT float GetTimeDelta(struct cbs::WorldIndex);
|
||||
GAME_IMPORT bool operator==(class cbs::CPcidPath const&, class cbs::CPcidPath const&);
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(enum cbs::pcid_t, class cbs::CPcidPath const&);
|
||||
GAME_IMPORT bool operator==(class cbs::CEntity const*, class cbs::CEntityPointer);
|
||||
GAME_IMPORT class cbs::PrefabManager& GetPrefabManager();
|
||||
GAME_IMPORT class cbs::ComponentsPool& GetComponentsPool();
|
||||
GAME_IMPORT class ILevel* GetILevel(struct cbs::WorldIndex);
|
||||
GAME_IMPORT class cbs::CPointer<class cbs::CEntity> CreateEntityFromPrefab(class ttl::string_const<char>, class ILevel*, class mtx34 const&, class ttl::string_const<char>, class Replication::CreateObjectOptions, bool, bool);
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(class cbs::CPcidPath&&, class cbs::CPcidPath const&);
|
||||
GAME_IMPORT void GatherSubtypeEntityInterfaces(class ttl::set<class CRTTI const*, struct ttl::less<class CRTTI const*>, class ttl::allocator>&, class CRTTIField const*);
|
||||
GAME_IMPORT class ttl::string_base<char> GetSpawnContextDebugInfoString(class SpawnContext const&);
|
||||
GAME_IMPORT bool CreateEntityFromPrefabDeferred(class cbs::CPointer<class cbs::CEntity>&, class ttl::string_const<char>, class SpawnContext&);
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(class cbs::CPcidPathView, class cbs::CPcidPath const&);
|
||||
GAME_IMPORT class cbs::CPointer<class cbs::CEntity> CreateEntityFromPrefab(class ttl::string_const<char>, class ILevel*, class mtx34 const&, struct PresetId, class Replication::CreateObjectOptions, bool);
|
||||
GAME_IMPORT enum cbs::EntitySpawnOrigin GetEntitySpawnOrigin(class cbs::CEntity const*);
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(class cbs::CPcidPath&&, class cbs::CPcidPath const&);
|
||||
GAME_IMPORT bool operator!=(class cbs::CEntity const*, class cbs::CEntityPointer);
|
||||
GAME_IMPORT bool IsAnyAncestorAlwaysSpawned(class cbs::CPointer<class cbs::CEntity>);
|
||||
GAME_IMPORT class ttl::string_base<char> GetEntityDebugInfoString(class cbs::CEntity const*);
|
||||
GAME_IMPORT bool operator==(class cbs::CPointer<class cbs::CEntity>, class cbs::CEntityPointer);
|
||||
GAME_IMPORT float GetTime(struct cbs::WorldIndex);
|
||||
GAME_IMPORT bool CreateEntityFromPrefabDeferred(class cbs::CPointer<class cbs::CEntity>&, class ttl::string_const<char>, class ILevel*, class mtx34 const&, class ttl::string_const<char>, class Replication::CreateObjectOptions, bool, bool);
|
||||
GAME_IMPORT class cbs::PrefabManager& GetPrefabManager();
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(class cbs::CPcidPathView, class cbs::CPcidPathView);
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(class cbs::CPcidPath const&, class cbs::CPcidPath const&);
|
||||
GAME_IMPORT class cbs::CPointer<class cbs::CEntity> CreateEntityFromPrefab(class ttl::string_const<char>, class SpawnContext&);
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(class cbs::CPcidPath const&, class cbs::CPcidPathView);
|
||||
GAME_IMPORT bool operator<(class cbs::CPcidPath const&, class cbs::CPcidPath const&);
|
||||
GAME_IMPORT bool GetPrefabEntityComponents(class cbs::CEntity const*, class ttl::vector<class cbs::PrefabEntityComponent const*, class ttl::vector_allocators::heap_allocator<class cbs::PrefabEntityComponent const*>, 16>&);
|
||||
GAME_IMPORT bool IsInDynamicRoot(class cbs::CPointer<class cbs::CEntity>, bool);
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(class cbs::CPcidPathView, class cbs::CPcidPath const&);
|
||||
GAME_IMPORT void GetEntityDebugHierarchyPath(class cbs::CEntity const*, class ttl::string_base<char>&);
|
||||
GAME_IMPORT class IPhysicsManager* GetPhysicsManager(struct cbs::WorldIndex);
|
||||
GAME_IMPORT class ttl::string_base<char> GetEntityDebugInfoString(class cbs::CEntity const*);
|
||||
GAME_IMPORT void GatherSubtypeEntityInterfaces(class ttl::set<class CRTTI const*, struct ttl::less<class CRTTI const*>, class ttl::allocator>&, class CRTTIField const*);
|
||||
GAME_IMPORT class cbs::CPointer<class cbs::CEntity> CreateEntityFromPrefab(class ttl::string_const<char>, class ILevel*, class mtx34 const&, struct PresetId, class Replication::CreateObjectOptions, bool);
|
||||
GAME_IMPORT bool CreateEntityFromPrefabDeferred(class cbs::CPointer<class cbs::CEntity>&, class ttl::string_const<char>, class SpawnContext&);
|
||||
GAME_IMPORT class cbs::ComponentsPool& GetComponentsPool();
|
||||
GAME_IMPORT bool CreateEntityFromPrefabDeferred(class cbs::CPointer<class cbs::CEntity>&, class ttl::string_const<char>, class ILevel*, class mtx34 const&, struct PresetId, class Replication::CreateObjectOptions, bool);
|
||||
GAME_IMPORT class cbs::CPointer<class cbs::CEntity> CreateEntityFromPrefab(class ttl::string_const<char>, class ILevel*, class mtx34 const&, class ttl::string_const<char>, class Replication::CreateObjectOptions, bool, bool);
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(class cbs::CPcidPath const&, class cbs::CPcidPathView);
|
||||
GAME_IMPORT void GetPCIDHierarchyPath(class ttl::span<class cbs::PrefabEntityComponent const* const, 4294967295>, uint32_t, class ttl::string_base<char>&);
|
||||
GAME_IMPORT bool operator==(class cbs::CPcidPath const&, class cbs::CPcidPath const&);
|
||||
GAME_IMPORT bool operator==(class cbs::CEntity const*, class cbs::CEntityPointer);
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(class cbs::CPcidPath const&, class cbs::CPcidPath const&);
|
||||
GAME_IMPORT class ILevel* GetILevel(struct cbs::WorldIndex);
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(enum cbs::pcid_t, class cbs::CPcidPath const&);
|
||||
GAME_IMPORT bool operator<(class cbs::CPcidPath const&, class cbs::CPcidPath const&);
|
||||
GAME_IMPORT void GetPECHierarchyPath(class ttl::span<class cbs::PrefabEntityComponent const* const, 4294967295>, uint32_t, class ttl::string_base<char>&);
|
||||
GAME_IMPORT class cbs::CPointer<class cbs::CEntity> CreateEntityFromPrefab(class ttl::string_const<char>, class SpawnContext&);
|
||||
GAME_IMPORT class cbs::CPcidPath operator+(class cbs::CPcidPath const&, enum cbs::pcid_t);
|
||||
GAME_IMPORT enum cbs::EntitySpawnOrigin GetEntitySpawnOrigin(class cbs::PrefabEntityComponent const*);
|
||||
GAME_IMPORT float GetTimeDelta(struct cbs::WorldIndex);
|
||||
GAME_IMPORT class CLevel* GetCLevel(struct cbs::WorldIndex);
|
||||
GAME_IMPORT enum cbs::EntitySpawnOrigin GetEntitySpawnOrigin(class cbs::CEntity const*);
|
||||
GAME_IMPORT class cbs::EnumPropertyManager& GetEnumPropertyManager();
|
||||
GAME_IMPORT bool operator!=(class cbs::CPointer<class cbs::CEntity>, class cbs::CEntityPointer);
|
||||
#pragma endregion
|
||||
};
|
Reference in New Issue
Block a user