From ad42edb0340ba5e0e2f6e318b58fcb0af61312d1 Mon Sep 17 00:00:00 2001 From: EllipticEllipsis Date: Sun, 17 Apr 2022 20:35:35 +0100 Subject: [PATCH] Add OoT's `namefixer.py` (#775) * Add OoT's namefixer.py * Absorb actorfixer into namefixer * Merge remote-tracking branch 'upstream/master' into namefixer * Remove problematic functions * Add sanity check * Review --- tools/{actorfixer.py => namefixer.py} | 247 +++++++++++++++++--------- 1 file changed, 159 insertions(+), 88 deletions(-) rename tools/{actorfixer.py => namefixer.py} (80%) diff --git a/tools/actorfixer.py b/tools/namefixer.py similarity index 80% rename from tools/actorfixer.py rename to tools/namefixer.py index 907b83a8b..08621f709 100755 --- a/tools/actorfixer.py +++ b/tools/namefixer.py @@ -1,12 +1,24 @@ #!/usr/bin/env python3 -import argparse, os +import os +import argparse -# There are a few commented out entries that would produce unexpected renames. -# They are left as a comment so people can just grab them. +# all occurrences of keys will be replaced by associated value +simpleReplace = { + # "Math_Rand_":"Rand_", + # "ACTORTYPE":"ACTORCAT", + # "DistToLink":"DistToPlayer", + # "HitItem":"HitInfo", +} -# "old": "new" -animdict = { +# all occurrences of keys will be replaced by associated value, +# if the occurence is the whole word +# for example, if there is a space before and an open parenthesis after, +# like for a function call: ` func_8002E4B4(` +# +# Custom behaviour can be enabled by using a tuple as the value (see +# explanation in replace_single below) +wordReplace = { "Actor_GetSwitchFlag": "Flags_GetSwitch", "atan_flip": "Math_Acot2F", "atans": "Math_Atan2S", @@ -88,25 +100,25 @@ animdict = { "func_801A89A8": "Audio_QueueSeqCmd", "func_8019F1C0": "Audio_PlaySfxAtPos", "func_801A72CC": "Audio_StopSfxByPos", - "SkelAnime_LodDrawLimb(": "SkelAnime_DrawLimbLod(", - "SkelAnime_LodDraw(": "SkelAnime_DrawLod(", - "SkelAnime_LodDrawLimbSV(": "SkelAnime_DrawFlexLimbLod(", - "SkelAnime_LodDrawSV(": "SkelAnime_DrawFlexLod(", - #"SkelAnime_DrawLimb(": "SkelAnime_DrawLimbOpa(", - #"SkelAnime_Draw(": "SkelAnime_DrawOpa(", - "SkelAnime_DrawLimbSV(": "SkelAnime_DrawFlexLimbOpa(", - "SkelAnime_DrawSV(": "SkelAnime_DrawFlexOpa(", - #"SkelAnime_AnimateFrame(": "SkelAnime_GetFrameData(", - "SkelAnime_GetTotalFrames(": "Animation_GetLength(", - "SkelAnime_GetFrameCount(": "Animation_GetLastFrame(", - "SkelAnime_Draw2Limb(": "SkelAnime_DrawLimb(", - "SkelAnime_Draw2(": "SkelAnime_Draw(", - "SkelAnime_DrawLimbSV2(": "SkelAnime_DrawFlexLimb(", - "SkelAnime_DrawSV2(": "SkelAnime_DrawFlex(", + "SkelAnime_LodDrawLimb": "SkelAnime_DrawLimbLod", + "SkelAnime_LodDraw": "SkelAnime_DrawLod", + "SkelAnime_LodDrawLimbSV": "SkelAnime_DrawFlexLimbLod", + "SkelAnime_LodDrawSV": "SkelAnime_DrawFlexLod", + # "SkelAnime_DrawLimb": "SkelAnime_DrawLimbOpa", # A different function is called this now + # "SkelAnime_Draw": "SkelAnime_DrawOpa", # A different function is called this now + "SkelAnime_DrawLimbSV": "SkelAnime_DrawFlexLimbOpa", + "SkelAnime_DrawSV": "SkelAnime_DrawFlexOpa", + # "SkelAnime_AnimateFrame": "SkelAnime_GetFrameData", # A different function is called this now + "SkelAnime_GetTotalFrames": "Animation_GetLength", + "SkelAnime_GetFrameCount": "Animation_GetLastFrame", + "SkelAnime_Draw2Limb": "SkelAnime_DrawLimb", + "SkelAnime_Draw2": "SkelAnime_Draw", + "SkelAnime_DrawLimbSV2": "SkelAnime_DrawFlexLimb", + "SkelAnime_DrawSV2": "SkelAnime_DrawFlex", "func_80134FFC": "SkelAnime_GetFrameData2", "func_801353D4": "Animation_GetLimbCount2", - "SkelAnime_GetTotalFrames2(": "Animation_GetLength2(", - "SkelAnime_GetFrameCount2(": "Animation_GetLastFrame2(", + "SkelAnime_GetTotalFrames2": "Animation_GetLength2", + "SkelAnime_GetFrameCount2": "Animation_GetLastFrame2", "SkelAnime_InterpolateVec3s": "SkelAnime_InterpFrameTable", "SkelAnime_AnimationCtxReset": "AnimationContext_Reset", "func_801358D4": "AnimationContext_SetNextQueue", @@ -125,7 +137,7 @@ animdict = { "SkelAnime_AnimationType4Loaded": "AnimationContext_CopyFalse", "SkelAnime_AnimationType5Loaded": "AnimationContext_MoveActor", "func_80135EE8": "AnimationContext_Update", - "SkelAnime_InitLinkAnimetion(": "SkelAnime_InitLink(", + "SkelAnime_InitLinkAnimetion": "SkelAnime_InitLink", "func_801360A8": "LinkAnimation_SetUpdateFunction", "func_801360E0": "LinkAnimation_Update", "func_80136104": "LinkAnimation_Morph", @@ -158,7 +170,7 @@ animdict = { "func_801370B0": "SkelAnime_LoopPartial", "func_8013713C": "SkelAnime_Once", "SkelAnime_ChangeAnimImpl": "Animation_ChangeImpl", - "SkelAnime_ChangeAnim(": "Animation_Change(", + "SkelAnime_ChangeAnim": "Animation_Change", "SkelAnime_ChangeAnimDefaultStop": "Animation_PlayOnce", "SkelAnime_ChangeAnimTransitionStop": "Animation_MorphToPlayOnce", "SkelAnime_ChangeAnimPlaybackStop": "Animation_PlayOnceSetSpeed", @@ -188,12 +200,12 @@ animdict = { "Actor_SetChestFlag": "Flags_SetTreasure", "Actor_SetAllChestFlag": "Flags_SetAllTreasure", "Actor_GetAllChestFlag": "Flags_GetAllTreasure", - "Actor_GetRoomCleared(": "Flags_GetClear(", - "Actor_SetRoomCleared(": "Flags_SetClear(", - "Actor_UnsetRoomCleared(": "Flags_UnsetClear(", - "Actor_GetRoomClearedTemp(": "Flags_GetClearTemp(", - "Actor_SetRoomClearedTemp(": "Flags_SetClearTemp(", - "Actor_UnsetRoomClearedTemp(": "Flags_UnsetTempClear(", + "Actor_GetRoomCleared": "Flags_GetClear", + "Actor_SetRoomCleared": "Flags_SetClear", + "Actor_UnsetRoomCleared": "Flags_UnsetClear", + "Actor_GetRoomClearedTemp": "Flags_GetClearTemp", + "Actor_SetRoomClearedTemp": "Flags_SetClearTemp", + "Actor_UnsetRoomClearedTemp": "Flags_UnsetTempClear", "Actor_GetCollectibleFlag": "Flags_GetCollectible", "Actor_SetCollectibleFlag": "Flags_SetCollectible", "func_800B8A1C": "Actor_PickUp", @@ -214,7 +226,7 @@ animdict = { "Actor_TitleCardCreate": "TitleCard_InitBossName", "func_800B867C": "Actor_TextboxIsClosing", "func_800BDC5C": "Actor_ChangeAnimationByInfo", - "Actor_ChangeAnimation(": "Actor_ChangeAnimationByInfo(", + "Actor_ChangeAnimation": "Actor_ChangeAnimationByInfo", "func_80152498": "Message_GetState", "func_800B8898": "Actor_GetScreenPos", "Audio_PlayActorSound2": "Actor_PlaySfxAtPos", @@ -222,8 +234,8 @@ animdict = { "Actor_IsFacingPlayerAndWithinRange": "Actor_IsFacingAndNearPlayer", "func_800BC8B8": "Actor_DrawDoorLock", "func_800B86C8": "Actor_ChangeFocus", - "zelda_malloc(": "ZeldaArena_Malloc(", - "zelda_mallocR(": "ZeldaArena_MallocR(", + "zelda_malloc": "ZeldaArena_Malloc", + "zelda_mallocR": "ZeldaArena_MallocR", "zelda_realloc": "ZeldaArena_Realloc", "zelda_free": "ZeldaArena_Free", "zelda_calloc": "ZeldaArena_Calloc", @@ -234,9 +246,9 @@ animdict = { "MainHeap_IsInitialized": "ZeldaArena_IsInitialized", "func_80138300": "Skin_GetLimbPos", "func_8013835C": "Skin_GetVertexPos", - # "BgCheck_RelocateMeshHeader": "CollisionHeader_GetVirtual", - # "BgCheck_AddActorMesh": "DynaPoly_SetBgActor", - # "BgCheck_RemoveActorMesh": "DynaPoly_DeleteBgActor", + "BgCheck_RelocateMeshHeader": "CollisionHeader_GetVirtual", + "BgCheck_AddActorMesh": "DynaPoly_SetBgActor", + "BgCheck_RemoveActorMesh": "DynaPoly_DeleteBgActor", "BgCheck_PolygonLinkedListNodeInit": "SSNode_SetValue", "BgCheck_PolygonLinkedListResetHead": "SSList_SetNull", "BgCheck_ScenePolygonListsNodeInsert": "SSNodeList_SetSSListHead", @@ -265,7 +277,7 @@ animdict = { "BgCheck_GetIsDefaultSpecialScene": "BgCheck_IsSmallMemScene", "BgCheck_GetSpecialSceneMaxMemory": "BgCheck_TryGetCustomMemsize", "BgCheck_CalcSubdivisionSize": "BgCheck_SetSubdivisionDimension", - "BgCheck_Init(": "BgCheck_Allocate(", + "BgCheck_Init": "BgCheck_Allocate", "func_800C3C00": "BgCheck_SetContextFlags", "func_800C3C14": "BgCheck_UnsetContextFlags", "BgCheck_GetActorMeshHeader": "BgCheck_GetCollisionHeader", @@ -379,7 +391,7 @@ animdict = { "func_8017CEF0": "Math3D_TriChkPointParaZIntersect", "func_8017D020": "Math3D_TriChkLineSegParaZIntersect", "Math3D_ColSphereLineSeg": "Math3D_LineVsSph", - "Math3D_ColSphereSphere(": "Math3D_SphVsSph(", + "Math3D_ColSphereSphere": "Math3D_SphVsSph", "func_8017F9C0": "Math3D_XZInSphere", "func_8017FA34": "Math3D_XYInSphere", "func_8017FAA8": "Math3D_YZInSphere", @@ -484,7 +496,7 @@ animdict = { "func_8015E750": "Message_FindCreditsMessage", - # Structs members + # Struct members "skelAnime.unk03": "skelAnime.taper", "skelAnime.animCurrentSeg": "skelAnime.animation", "skelAnime.initialFrame": "skelAnime.startFrame", @@ -505,6 +517,7 @@ animdict = { "actor.minVelocityY": "actor.terminalVelocity", "actor.yDistToWater": "actor.depthInWater", "actor.yDistToPlayer": "actor.playerHeightRel", + "gSaveContext.weekEventReg": "gSaveContext.save.weekEventReg", "gSaveContext.playerForm": "gSaveContext.save.playerForm", "gSaveContext.day": "gSaveContext.save.day", @@ -529,6 +542,11 @@ animdict = { "gSaveContext.permanentSceneFlags": "gSaveContext.save.permanentSceneFlags", "gSaveContext.bomberCode": "gSaveContext.save.bomberCode", "gSaveContext.skullTokenCount": "gSaveContext.save.skullTokenCount", + "gSaveContext.cutscene": "gSaveContext.save.cutscene", + "gSaveContext.health": "gSaveContext.save.playerData.health", + "gSaveContext.unk_1016": "gSaveContext.jinxTimer", + "gSaveContext.unk_3F58": "gSaveContext.sunsSongState", + "player->unk_A87": "player->exchangeItemId", "player->leftHandActor": "player->heldActor", "player->unk_384": "player->getItemId", @@ -547,8 +565,7 @@ animdict = { "globalCtx->envCtx.unk_E6": "globalCtx->envCtx.screenFillColor", "globalCtx->envCtx.unk_C3": "globalCtx->envCtx.lightSettingOverride", "globalCtx->envCtx.unk_DC": "globalCtx->envCtx.lightBlend", - "gSaveContext.unk_1016": "gSaveContext.jinxTimer", - "gSaveContext.unk_3F58": "gSaveContext.sunsSongState", + "globalCtx->msgCtx.unk1202A": "globalCtx->msgCtx.ocarinaMode", "globalCtx->msgCtx.unk1202C": "globalCtx->msgCtx.ocarinaAction", "globalCtx->msgCtx.unk11F22": "globalCtx->msgCtx.msgMode", @@ -572,65 +589,107 @@ animdict = { "CUR_UPG_VALUE_VOID": "GET_CUR_UPG_VALUE", "ICHAIN_F32_DIV1000(minVelocityY,": "ICHAIN_F32_DIV1000(terminalVelocity,", "ICHAIN_F32(minVelocityY,": "ICHAIN_F32(terminalVelocity,", + + # Example of custom behaviour: + # "PLAYER": ("GET_PLAYER(globalCtx)", {"ignore": (-1, '"PLAYER"')}), # ignore "PLAYER" in sSoundBankNames } -def replace_anim(file): - with open(file, 'r', encoding='utf-8') as infile: +# [a-zA-Z0-9_] +def is_word_char(c): + return (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z') or (c >= '0' and c <= '9') or c == '_' + +def replace_single(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) + changesCount = 0 - if(fixes > 0): - print('Changed', fixes,'entr' + ('y' if fixes == 1 else 'ies') + ' in',file) + for old, new in simpleReplace.items(): + # replace `old` with `new` + if old in srcdata: + changesCount += 1 + print(old, "->", new) + srcdata = srcdata.replace(old, new) + + for old, new in wordReplace.items(): + # `new` can be a tuple where the first element is what to replace `old` with, + # and the second element is a dict containing "custom behavior" properties. + if isinstance(new, tuple): + custom_behavior = True + new, custom_behavior_data = new + # The "ignore" data is a tuple where the first element is an offset relative to + # where `old` was found, and the string from that index must differ from the + # tuple's second element for the replacement to be done. + custom_behavior_ignore_data = custom_behavior_data.get("ignore") + custom_behavior_ignore = custom_behavior_ignore_data is not None + if custom_behavior_ignore: + custom_behavior_ignore_offset, custom_behavior_ignore_match = custom_behavior_ignore_data + else: + custom_behavior = False + # replace `old` with `new` if the occurence of `old` is the whole word + oldStartIdx = srcdata.find(old) + if oldStartIdx >= 0: + old_start_as_word = is_word_char(old[0]) + old_end_as_word = is_word_char(old[-1]) + replaceCount = 0 + while oldStartIdx >= 0: + replace = True + if old_start_as_word: + if oldStartIdx == 0: + pass + elif is_word_char(srcdata[oldStartIdx-1]): + replace = False + if old_end_as_word: + oldEndIdx = oldStartIdx + len(old) + if oldEndIdx >= len(srcdata): + pass + elif is_word_char(srcdata[oldEndIdx]): + replace = False + if replace and custom_behavior and custom_behavior_ignore: + if srcdata[oldStartIdx + custom_behavior_ignore_offset:].startswith(custom_behavior_ignore_match): + replace = False + if replace: + srcdata = srcdata[:oldStartIdx] + new + srcdata[oldEndIdx:] + replaceCount += 1 + oldStartIdx = srcdata.find(old, oldStartIdx + len(new)) + if replaceCount > 0: + changesCount += 1 + print(old, "->", new) + + if changesCount > 0: + print('Changed', changesCount, 'entry' if changesCount == 1 else 'entries', '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'): +def replace_all(repo): + for subdir, dirs, files in os.walk(os.path.join(repo,'src')): for filename in files: - if(filename.endswith('.c')): - file = subdir + os.sep + filename - replace_anim(file) + if filename.endswith('.c') or filename.endswith('.h'): + file = os.path.join(subdir,filename) + replace_single(file) - for subdir, dirs, files in os.walk(repo + os.sep + 'asm'): + for subdir, dirs, files in os.walk(os.path.join(repo,'asm')): for filename in files: - if(filename.endswith('.s')): - file = subdir + os.sep + filename - replace_anim(file) + if filename.endswith('.s'): + file = os.path.join(subdir,filename) + replace_single(file) - for subdir, dirs, files in os.walk(repo + os.sep + 'data'): + for subdir, dirs, files in os.walk(os.path.join(repo,'data')): for filename in files: - if(filename.endswith('.s')): - file = subdir + os.sep + filename - replace_anim(file) + if filename.endswith('.s'): + file = os.path.join(subdir,filename) + replace_single(file) - for subdir, dirs, files in os.walk(repo + os.sep + 'docs'): + for subdir, dirs, files in os.walk(os.path.join(repo,'docs')): for filename in files: - if(filename.endswith('.md')): - file = subdir + os.sep + filename - replace_anim(file) + if filename.endswith('.md'): + file = os.path.join(subdir,filename) + replace_single(file) - for subdir, dirs, files in os.walk(repo + os.sep + 'tools' + os.sep + 'sizes'): - for filename in files: - if(filename.endswith('.csv')): - file = subdir + os.sep + filename - replace_anim(file) - return 1 def dictSanityCheck(): - keys = animdict.keys() - values = animdict.values() + keys = wordReplace.keys() + values = wordReplace.values() for k in keys: if k in values: print(f"Key '{k}' found in values") @@ -638,15 +697,27 @@ def dictSanityCheck(): print(f"Fix this by removing said key from the dictionary") exit(-1) -if __name__ == "__main__": - 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) + keys = simpleReplace.keys() + values = {*wordReplace.values(), *simpleReplace.values()} + for k in keys: + for value in values: + if k in value: + print(f"Key '{k}' found in values") + print(f"This would produce unintended renames") + print(f"Fix this by removing said key from the dictionary") + exit(-1) +def main(): + parser = argparse.ArgumentParser(description='Apply function renames to a file') + parser.add_argument('file', help="source file to be processed. use . to process the whole repo") args = parser.parse_args() dictSanityCheck() - if(args.file == '.'): - replace_anim_all(os.curdir) + if args.file == '.': + replace_all(os.curdir) else: - replace_anim(args.file) + replace_single(args.file) + +if __name__ == "__main__": + main()