diff --git a/.gitignore b/.gitignore index 7a79ff8389..7627ea0c6a 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ tools/asmsplitter/asm/* tools/asmsplitter/c/* ctx.c tools/*dSYM/ +graphs/ # Assets *.png diff --git a/extract_assets.py b/extract_assets.py index e162ae64b4..70ce95523c 100755 --- a/extract_assets.py +++ b/extract_assets.py @@ -1,55 +1,100 @@ #!/usr/bin/env python3 import argparse +from multiprocessing import Pool, cpu_count, Event import os -from shutil import copyfile -from multiprocessing import Pool -from multiprocessing import cpu_count + +# Returns True if outFile doesn't exists +# or if inFile has been modified after the last modification of outFile +def checkTouchedFile(inFile: str, outFile: str) -> bool: + if not os.path.exists(outFile): + return True + return os.path.getmtime(inFile) > os.path.getmtime(outFile) def Extract(xmlPath, outputPath, outputSourcePath): - ExtractFile(xmlPath, outputPath, outputSourcePath, 1, 0) + ExtractFile(xmlPath, outputPath, outputSourcePath, 1, 0) def ExtractScene(xmlPath, outputPath, outputSourcePath): - ExtractFile(xmlPath, outputPath, outputSourcePath, 1, 1) + ExtractFile(xmlPath, outputPath, outputSourcePath, 1, 1) def ExtractFile(xmlPath, outputPath, outputSourcePath, genSrcFile, incFilePrefix): - execStr = "tools/ZAPD/ZAPD.out e -eh -i %s -b baserom/ -o %s -osf %s -gsf %i -ifp %i -rconf tools/ZAPDConfigs/MqDbg/Config.xml" % (xmlPath, outputPath, outputSourcePath, genSrcFile, incFilePrefix) + if globalAbort.is_set(): + # Don't extract if another file wasn't extracted properly. + return - print(execStr) - os.system(execStr) + execStr = "tools/ZAPD/ZAPD.out e -eh -i %s -b baserom/ -o %s -osf %s -gsf %i -ifp %i -rconf tools/ZAPDConfigs/MqDbg/Config.xml" % (xmlPath, outputPath, outputSourcePath, genSrcFile, incFilePrefix) + if globalUnaccounted: + execStr += " -wu" + + print(execStr) + exitValue = os.system(execStr) + if exitValue != 0: + globalAbort.set() + print("\n") + print("Error when extracting from file " + xmlPath, file=os.sys.stderr) + print("Aborting...", file=os.sys.stderr) + print("\n") def ExtractFunc(fullPath): - outPath = ("assets/" + fullPath.split("assets/xml/")[1]).split(".xml")[0] - outSourcePath = ("assets/" + fullPath.split("assets/xml/")[1]).split(".xml")[0] + *pathList, xmlName = fullPath.split(os.sep) + objectName = os.path.splitext(xmlName)[0] - if (fullPath.startswith("assets/xml/scenes/")): - ExtractScene(fullPath, outPath, outSourcePath) - else: - Extract(fullPath, outPath, outSourcePath) + outPath = os.path.join("assets", *pathList[2:], objectName) + outSourcePath = outPath + + isScene = fullPath.startswith("assets/xml/scenes/") + if isScene: + objectName += "_scene" + + if not globalForce: + cFile = os.path.join(outPath, objectName + ".c") + hFile = os.path.join(outPath, objectName + ".h") + if not checkTouchedFile(fullPath, cFile) and not checkTouchedFile(fullPath, hFile): + return + + if isScene: + ExtractScene(fullPath, outPath, outSourcePath) + else: + Extract(fullPath, outPath, outSourcePath) + +def initializeWorker(force: bool, abort, unaccounted: bool): + global globalForce + global globalAbort + global globalUnaccounted + globalForce = force + globalAbort = abort + globalUnaccounted = unaccounted def main(): parser = argparse.ArgumentParser(description="baserom asset extractor") parser.add_argument("-s", "--single", help="asset path relative to assets/, e.g. objects/gameplay_keep") + parser.add_argument("-f", "--force", help="Force the extraction of every xml instead of checking the touched ones.", action="store_true") + parser.add_argument("-u", "--unaccounted", help="Enables ZAPD unaccounted detector warning system.", action="store_true") args = parser.parse_args() + abort = Event() + asset_path = args.single if asset_path is not None: - if asset_path.endswith("/"): - asset_path = asset_path[0:-1] - Extract(f"assets/xml/{asset_path}.xml", f"assets/{asset_path}/", f"assets/{asset_path}/") + # Always force if -s is used. + initializeWorker(True, abort, args.unaccounted) + fullPath = os.path.join("assets", "xml", asset_path + ".xml") + ExtractFunc(fullPath) else: xmlFiles = [] - for currentPath, folders, files in os.walk("assets"): - for file in files: - fullPath = os.path.join(currentPath, file) - if file.endswith(".xml") and currentPath.startswith("assets/xml/"): - outPath = ("assets/" + fullPath.split("assets/xml/")[1]).split(".xml")[0] - xmlFiles.append(fullPath) + for currentPath, folders, files in os.walk(os.path.join("assets", "xml")): + for file in files: + fullPath = os.path.join(currentPath, file) + if file.endswith(".xml"): + xmlFiles.append(fullPath) numCores = cpu_count() print("Extracting assets with " + str(numCores) + " CPU cores.") - p = Pool(numCores) - p.map(ExtractFunc, xmlFiles) + with Pool(numCores, initializer=initializeWorker, initargs=(args.force, abort, args.unaccounted)) as p: + p.map(ExtractFunc, xmlFiles) + + if abort.is_set(): + exit(1) if __name__ == "__main__": main() diff --git a/tools/.gitignore b/tools/.gitignore index 2da9fd53e6..3900e427a4 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -5,3 +5,5 @@ makeromfs elf2rom mkldscript vtxdis + +graphovl/ diff --git a/tools/actorfixer.py b/tools/actorfixer.py new file mode 100755 index 0000000000..0c884c0f17 --- /dev/null +++ b/tools/actorfixer.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 + +import os +import argparse + +animdict ={ + "Actor_SetHeight":"Actor_SetFocus", + "func_8002E4B4":"Actor_UpdateBgCheckInfo", + "func_8002BDB0":"Actor_SetFeetPos", + "func_8002DA78":"Actor_WorldYawTowardActor", + "func_8002DAC0":"Actor_WorldYawTowardPoint", + "func_8002DB48":"Actor_WorldDistXYZToActor", + "func_8002DB6C":"Actor_WorldDistXYZToPoint", + "func_8002DAE0":"Actor_WorldPitchTowardActor", + "func_8002DB28":"Actor_WorldPitchTowardPoint", + "func_8002DB8C":"Actor_WorldDistXZToActor", + "func_8002DBB0":"Actor_WorldDistXZToPoint", + "func_8002EEE4":"Actor_GetFocus", + "func_8002EF14":"Actor_GetWorld", + "func_8002EF44":"Actor_GetWorldPosShapeRot", + "actor.unk_1F":"actor.targetMode", + "ICHAIN_U8(unk_1F":"ICHAIN_U8(targetMode", + "actor.initPosRot":"actor.home", + "actor.posRot.":"actor.world.", + "actor.posRot2":"actor.focus", + "actor.unk_4C":"actor.targetArrowOffset", + "ICHAIN_F32(unk_4C":"ICHAIN_F32(targetArrowOffset", + "actor.groundY":"actor.floorHeight", + "actor.wallPolySource":"actor.wallBgId", + "actor.floorPolySource":"actor.floorBgId", + "actor.wallPolyRot":"actor.wallYaw", + "DistToLink":"DistToPlayer", + "yawTowardsLink":"yawTowardsPlayer", + "colChkInfo.unk_10":"colChkInfo.cylRadius", + "colChkInfo.unk_12":"colChkInfo.cylHeight", + "colChkInfo.unk_14":"colChkInfo.cylYShift", + "shape.unk_08":"shape.yOffset", + "shape.shadowDrawFunc":"shape.shadowDraw", + "shape.unk_10":"shape.shadowScale", + "shape.unk_14":"shape.shadowAlpha", + "actor.unk_CC":"actor.shape.feetPos", + "actor.pos4":"actor.prevPos", + "actor.unk_10C":"actor.isTargeted", + "actor.unk_10D":"actor.targetPriority", + "actor.dmgEffectTimer":"actor.colorFilterTimer", + "actor.dmgEffectParams":"actor.colorFilterParams", + "actor.unk_116":"actor.dropFlag", + "actorCtx.actorList[":"actorCtx.actorLists[", + "Actor_ChangeType":"Actor_ChangeCategory", + "ActorShadow_DrawFunc_Squiggly":"ActorShadow_DrawHorse", + "ActorShadow_DrawFunc_":"ActorShadow_Draw", + "ACTORTYPE":"ACTORCAT", + "actor.type":"actor.category", + ".body.":".info.", + "HitItem":"HitInfo", + "bumper.unk_06":"bumper.hitPos", + "base.list":"base.elements", + "toucher.flags":"toucher.dmgFlags", + "bumper.flags":"bumper.dmgFlags", + "maskA ":"ocFlags1 ", + "maskB ":"ocFlags2 ", + "base.type":"base.colType", + "COLTYPE_UNK11":"COLTYPE_HARD", + "COLTYPE_UNK12":"COLTYPE_WOOD", + "COLTYPE_UNK13":"COLTYPE_TREE", + "COLTYPE_METAL_SHIELD":"COLTYPE_METAL", + "COLTYPE_UNK10":"COLTYPE_NONE", + "COLTYPE_UNK":"COLTYPE_HIT", + "info.flags":"info.elemtype", + "ColliderBody":"ColliderInfo", + "ColliderJntSphItem":"ColliderJntSphElement", + "ColliderTrisItem":"ColliderTrisElement", + "Collider_CylinderUpdate":"Collider_UpdateCylinder", + "func_800628A4":"Collider_UpdateSpheres", + "func_800627A0":"Collider_SetTrisVertices", + "func_80062734":"Collider_SetQuadVertices", + "func_80061ED4":"CollisionCheck_SetInfo", + "func_80061EFC":"CollisionCheck_SetInfo2", + "func_80061EB0":"CollisionCheck_SetInfoNoDamageTable", + # "func_8002E084": "Actor_YawInRangeWithPlayer", + # "func_8002E0D0": "Actor_YawInRangeWithActor", + # "func_8002E12C": "Actor_YawAndDistInRangeWithPlayer", + # "func_8002E1A8": "Actor_YawAndDistInRangeWithActor", + # "func_80033A84": "Actor_IsTargeted", + # "func_80033AB8": "Actor_OtherIsTargeted", + # "func_80035650": "Actor_SetDropFlag", + # "func_8003573C": "Actor_SetDropFlagJntSph", + # "func_80033780": "Actor_GetProjectileActor", + # "func_80033260": "Actor_SpawnFloorDust", + # "func_80032C7C": "Actor_PlayDeathFx", + # "actorCtx.unk_00": "actorCtx.freezeFlashTimer", +} + +def replace_anim(file): + with open(file,'r',encoding = 'utf-8') as infile: + srcdata = infile.read() + + funcs = list(animdict.keys()) + fixes = 0 + for func in funcs: + newfunc = animdict.get(func) + if(newfunc is None): + print("How did this happen?") + return -1 + if(func in srcdata): + fixes += 1 + print(func) + srcdata = srcdata.replace(func, newfunc) + + if(fixes > 0): + print('Changed', fixes,'entr' + ('y' if fixes == 1 else 'ies') + ' in',file) + with open(file, 'w', encoding = 'utf-8', newline = '\n') as outfile: + outfile.write(srcdata) + return 1 + +def replace_anim_all(repo): + for subdir, dirs, files in os.walk(repo + os.sep + 'src'): + for filename in files: + if(filename.endswith('.c')): + file = subdir + os.sep + filename + replace_anim(file) + for subdir, dirs, files in os.walk(repo + os.sep + 'asm' + os.sep + 'non_matchings'): + for filename in files: + if(filename.endswith('.s')): + file = subdir + os.sep + filename + replace_anim(file) + return 1 + +parser = argparse.ArgumentParser(description='Update to the new animation names') +parser.add_argument('file', help="source file to be processed. use . to process the whole repo", default = None) + +if __name__ == "__main__": + args = parser.parse_args() + if(args.file == '.'): + replace_anim_all(os.curdir) + else: + replace_anim(args.file)