UnityPy TypeTree 적용 (작성중)
아직 제대로 테스트해보지 않았지만, 일단 기록용으로 작성함.
필요 프로그램: TypeTreeGenerator ( https://github.com/K0lb3/TypeTreeGenerator )
1. 타입트리 생성
TypeTreeGenerator를 다운받아서 {게임명}_data\TypeTreeGenerator 폴더 안에 압축해제한다.
import UnityPy
import subprocess
import os
def gen_typetree(game_data_folder, unity_version=None):
os.makedirs(f"{game_data_folder}/typetree", exist_ok=True)
dll_folder = f"{game_data_folder}/Managed"
dll_lst = [i for i in os.listdir(dll_folder) if i.endswith(".dll")]
ttg_folder = f"{game_data_folder}/TypeTreeGenerator"
ttg_exe = f"{ttg_folder}/TypeTreeGeneratorCLI.exe"
for dll in dll_lst:
cmd = [ttg_exe, "-p", dll_folder, "-a", dll, "-v", unity_version, "-d", "json", "-o", f"{ttg_folder}/typetree_{dll.rstrip('.dll')}.json"]
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
print(e)
return
def main():
game_folder = os.path.dirname(os.path.abspath(__file__))
env = UnityPy.load(f"{game_folder}/resources.assets")
gen_typetree(game_folder, env.file.unity_version)
이후, 위 코드를 gen_typetree.py로 저장하고 나서 {게임명}_data 폴더에서 실행하면
{게임명}_data/typetree 폴더 내 dump된 typetree들이 json파일에 담기게 된다.
2. 타입트리 사용
우선, {게임명}_data 폴더 내 Resource 폴더를 복사한 후 폴더명을 Library로 바꾸자.
import json
import UnityPy
import os
def load_typetree(fp: str):
json_lst = [i for i in os.listdir(fp) if i.startswith("typetree_") and i.endswith(".json")]
load_tree = {}
for json_file in json_lst:
clean_fn = os.path.splitext(json_file)[0]
clean_fn = clean_fn.replace("typetree_", "")
# clean_fn = clean_fn + ".dll"
if tree.get(clean_fn) is None:
load_tree[clean_fn] = {}
with open(f"{fp}/{json_file}", "r", encoding="utf-8") as f:
load_tree[clean_fn] = json.load(f)
return load_tree
def export(fn):
data_folder = os.path.dirname(os.path.abspath(__file__))
env = UnityPy.load(fn)
load_tree = load_typetree(f"{data_folder}/typetree")
for obj in env.objects:
if obj.type.name == "MonoBehaviour":
monobehaviour = obj.read()
script = monobehaviour.m_Script.read()
if obj.serialized_type.nodes:
tree = obj.read_typetree()
else:
if script.m_Namespace == "":
typetree = load_tree[script.m_AssemblyName][script.m_ClassName]
else:
typetree = load_tree[script.m_AssemblyName][f"{script.m_Namespace}.{script.m_ClassName}"]
tree = obj.read_typetree(typetree)
# Tree 작업
...
# 수정한 monobehaviour 저장
obj.save_typetree(tree)
# 수정한 assets 파일 저장
with open(f"{fn}_edit", "wb") as f:
f.write(env.file.save())
if __name__ == "__main__":
asset_list = ["sharedassets0.assets"] # 임의로 수정
for i in asset_list:
export(i)
위 경우 특정 .assets만 작업하기에 그냥 리스트를 만들어서 해당 리스트를 순회하도록 하였다.
UnityPy.load()는 file path, folder path, stream, bytes object를 받아올 수 있으니 임의대로 수정해서 사용하자.
또한, script의 m_Namespace가 없는 경우
typetree dump json에서 script.m_ClassName을 Key로 Value를 가져오면 정상적으로 적용이 됐었고,
ex) LabelUI
만약 m_Namespace가 있는 경우 f"{scipt.m_Namespace}.{script.m_ClassName}"을 Key로 Value를 가져오면 정상적으로 적용이 됐었다.
ex) TMPro.TMP_FontAsset
3. 기타등등...
https://github.com/K0lb3/UnityPy/issues/201#issuecomment-1719335452
누군가 Typetree dump 적용에 관해 질문한 글이다.
답변을 보면 특정 클래스로 typetree dump를 읽어오는 것이 보이는데,
아마 답변의 첫번째 방법은 script.m_Namespace가 있는 경우 KeyError를 내면서 적용이 안 될 것이다.
두번째 방법은 잘 모르겠다. 나중에 직접 사용해봐야 알 듯.