mirror of
https://github.com/HackerN64/HackerOoT.git
synced 2026-01-21 10:37:37 -08:00
Merge pull request #17 from Yanis42/feature_daf
[Feature] Introduce Assets Fixer
This commit is contained in:
3
Makefile
3
Makefile
@@ -194,12 +194,13 @@ setup:
|
||||
python3 fixbaserom.py
|
||||
python3 extract_baserom.py
|
||||
python3 extract_assets.py -j$(N_THREADS)
|
||||
python3 tools/daf/daf.py -a -p ./
|
||||
|
||||
test: $(ROM)
|
||||
$(EMULATOR) $(EMU_FLAGS) $<
|
||||
|
||||
|
||||
.PHONY: all clean setup test distclean assetclean
|
||||
.PHONY: all clean setup test distclean assetclean daf
|
||||
|
||||
#### Various Recipes ####
|
||||
|
||||
|
||||
@@ -55,10 +55,6 @@ typedef struct {
|
||||
/* 0x4 */ Vec3s* bgCamFuncData; // s16 data grouped in threes (ex. Vec3s), is usually of type `BgCamFuncData`, but can be a list of points of type `Vec3s` for crawlspaces
|
||||
} BgCamInfo; // size = 0x8
|
||||
|
||||
// ZAPD compatibility typedefs
|
||||
// TODO: Remove when ZAPD adds support for them
|
||||
typedef BgCamInfo CamData;
|
||||
|
||||
// The structure used for all instances of s16 data from `BgCamInfo` with the exception of crawlspaces.
|
||||
// See `Camera_Subj4` for Vec3s data usage in crawlspaces
|
||||
typedef struct {
|
||||
|
||||
@@ -59,10 +59,4 @@ s32 SkelCurve_Update(struct PlayState* play, SkelCurve* skelCurve);
|
||||
void SkelCurve_Draw(Actor* actor, struct PlayState* play, SkelCurve* skelCurve, OverrideCurveLimbDraw overrideLimbDraw,
|
||||
PostCurveLimbDraw postLimbDraw, s32 lod, void* data);
|
||||
|
||||
// ZAPD compatibility typedefs
|
||||
// TODO: Remove when ZAPD adds support for them
|
||||
typedef CurveInterpKnot TransformData;
|
||||
typedef CurveAnimationHeader TransformUpdateIndex;
|
||||
typedef CurveSkeletonHeader SkelCurveLimbList;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -43,7 +43,7 @@ typedef struct {
|
||||
} Spawn;
|
||||
|
||||
// TODO: ZAPD Compatibility
|
||||
typedef Spawn EntranceEntry;
|
||||
typedef Spawn EntranceEntry;
|
||||
|
||||
typedef struct {
|
||||
/* 0x00 */ u8 count; // number of points in the path
|
||||
@@ -146,17 +146,6 @@ typedef union {
|
||||
RoomShapeCullable cullable;
|
||||
} RoomShape; // "Ground Shape"
|
||||
|
||||
// ZAPD compatibility typedefs
|
||||
// TODO: Remove when ZAPD adds support for them
|
||||
typedef RoomShapeDListsEntry PolygonDlist;
|
||||
typedef RoomShapeNormal PolygonType0;
|
||||
typedef RoomShapeImageSingle MeshHeader1Single;
|
||||
typedef RoomShapeImageMultiBgEntry BgImage;
|
||||
typedef RoomShapeImageMulti MeshHeader1Multi;
|
||||
typedef RoomShapeCullableEntry PolygonDlist2;
|
||||
typedef RoomShapeCullable PolygonType2;
|
||||
#define SCENE_CMD_MESH SCENE_CMD_ROOM_SHAPE
|
||||
|
||||
#define ROOM_DRAW_OPA (1 << 0)
|
||||
#define ROOM_DRAW_XLU (1 << 1)
|
||||
|
||||
|
||||
@@ -43,13 +43,6 @@ typedef struct {
|
||||
/* 0x08 */ Gfx* dlist;
|
||||
} SkinAnimatedLimbData; // size = 0xC
|
||||
|
||||
// ZAPD compatibility typedefs
|
||||
// TODO: Remove when ZAPD adds support for them
|
||||
typedef SkinVertex Struct_800A57C0;
|
||||
typedef SkinTransformation Struct_800A598C_2;
|
||||
typedef SkinAnimatedLimbData Struct_800A5E28;
|
||||
typedef SkinLimbModif Struct_800A598C;
|
||||
|
||||
#define SKIN_LIMB_TYPE_ANIMATED 4
|
||||
#define SKIN_LIMB_TYPE_NORMAL 11
|
||||
|
||||
|
||||
28
tools/daf/README.md
Normal file
28
tools/daf/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Decompilation Assets Fixer (DAF)
|
||||
The tool used for extract the assets ([ZAPD](https://github.com/zeldaret/ZAPD)) needs to be updated because it's using old names for structs and macros, this project is a workaround attempt until the main tool is updated.
|
||||
|
||||
## Usage
|
||||
- Set the decomp path with the ``-p (--path)`` argument (example: ``daf.py -p ./ (-a || -m MODE)``)
|
||||
- Run ``daf.py`` (tested under Python 3.10, should work with 3.7+)
|
||||
- If you have any issues when compiling the decomp, try ``make clean && make -j``, if it's not working feel free to open an issue on this repo
|
||||
|
||||
### Operating modes:
|
||||
- ``daf.py -m (--mode) fix_types``, this will update types and macros, intended to be used after using ZAPD
|
||||
- ``daf.py -m (--mode) name_entrances``, this will remove hex numbers from exit lists
|
||||
- ``daf.py -m (--mode) fix_segments``, this will add casts to segment symbols inside room lists
|
||||
- ``daf.py -a (--all)``, this will run all modes
|
||||
- ``daf.py -v (--verbose)``, this will display extra informations
|
||||
|
||||
### ROM should build OK on an unmodifed codebase
|
||||
|
||||
```
|
||||
f0b7f35375f9cc8ca1b2d59d78e35405 zelda_ocarina_mq_dbg.z64
|
||||
zelda_ocarina_mq_dbg.z64: OK
|
||||
```
|
||||
|
||||
## Add More Data
|
||||
If you need to add more data to change, add a dictionnary with the following format: ``"OLD": "NEW"``, then add your dictionnary to ``dataToFix``.
|
||||
|
||||
Next, run ``daf.py`` and it should make the changes.
|
||||
|
||||
## Contributions are welcome!
|
||||
61
tools/daf/daf.py
Normal file
61
tools/daf/daf.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from time import time
|
||||
|
||||
try:
|
||||
from data import fileTypes
|
||||
from functions import replaceOldData, replaceEntranceHex, fixSegments, getArguments
|
||||
except:
|
||||
print("[DAF:Error]: Files are missing. Make sure everything is in the same folder.")
|
||||
quit()
|
||||
|
||||
# verbose settings
|
||||
fixTypeTime = entranceTime = segmentsTime = None
|
||||
dashAmount = 60
|
||||
infoPrefix = "[DAF:Info]"
|
||||
|
||||
# general things
|
||||
args, parser = getArguments()
|
||||
|
||||
hasArgs = False
|
||||
runTime = time()
|
||||
|
||||
if args.mode == "fix_types" or args.run_all:
|
||||
startingTime = time()
|
||||
|
||||
if args.verbose:
|
||||
print(f"{infoPrefix}: Fixing types and macros...")
|
||||
|
||||
hasArgs = True
|
||||
for type in fileTypes:
|
||||
replaceOldData(f"{args.decompPath}/assets/", type)
|
||||
|
||||
if args.verbose:
|
||||
print(f"{infoPrefix}: Done in {(time() - startingTime):.2f}s!\n{'-' * dashAmount}")
|
||||
|
||||
if args.mode == "name_entrances" or args.run_all:
|
||||
startingTime = time()
|
||||
|
||||
if args.verbose:
|
||||
print(f"{infoPrefix}: Removing hexadecimal from exit lists...")
|
||||
hasArgs = True
|
||||
replaceEntranceHex(args.decompPath)
|
||||
|
||||
if args.verbose:
|
||||
print(f"{infoPrefix}: Done in {(time() - startingTime):.2f}s!\n{'-' * dashAmount}")
|
||||
|
||||
if args.mode == "fix_segments" or args.run_all:
|
||||
startingTime = time()
|
||||
|
||||
if args.verbose:
|
||||
print(f"{infoPrefix}: Adding missing casts to rooms symbols...")
|
||||
hasArgs = True
|
||||
fixSegments(args.decompPath)
|
||||
|
||||
if args.verbose:
|
||||
print(f"{infoPrefix}: Done in {(time() - startingTime):.2f}s!\n{'-' * dashAmount}")
|
||||
|
||||
if hasArgs and args.verbose:
|
||||
print(f"{infoPrefix}: All Done in {(time() - runTime):.2f}s!")
|
||||
|
||||
if not hasArgs:
|
||||
parser.print_help()
|
||||
quit()
|
||||
61
tools/daf/data.py
Normal file
61
tools/daf/data.py
Normal file
@@ -0,0 +1,61 @@
|
||||
### [GENERAL DATA] ###
|
||||
|
||||
# Add types here
|
||||
fileTypes = [".h", ".c"]
|
||||
|
||||
# -------------------------------------------------------
|
||||
|
||||
### [FIX TYPES MODE] ###
|
||||
|
||||
# Format: ``"OLD": "NEW"``
|
||||
|
||||
camData = {
|
||||
"CamData": "BgCamInfo",
|
||||
}
|
||||
|
||||
curveData = {
|
||||
"TransformData": "CurveInterpKnot",
|
||||
"TransformUpdateIndex": "CurveAnimationHeader",
|
||||
"SkelCurveLimbList": "CurveSkeletonHeader",
|
||||
}
|
||||
|
||||
roomData = {
|
||||
"PolygonDlist": "RoomShapeDListsEntry",
|
||||
"PolygonType0": "RoomShapeNormal",
|
||||
"MeshHeader1Single": "RoomShapeImageSingle",
|
||||
"BgImage": "RoomShapeImageMultiBgEntry",
|
||||
"MeshHeader1Multi": "RoomShapeImageMulti",
|
||||
"PolygonDlist2": "RoomShapeCullableEntry",
|
||||
"PolygonType2": "RoomShapeCullable",
|
||||
"SCENE_CMD_MESH": "SCENE_CMD_ROOM_SHAPE",
|
||||
}
|
||||
|
||||
skinData = {
|
||||
"Struct_800A57C0": "SkinVertex",
|
||||
"Struct_800A598C_2": "SkinTransformation",
|
||||
"Struct_800A5E28": "SkinAnimatedLimbData",
|
||||
"Struct_800A598C": "SkinLimbModif",
|
||||
}
|
||||
|
||||
# Add or remove dictionnaries to this list to replace types
|
||||
dataToFix = [
|
||||
camData,
|
||||
curveData,
|
||||
roomData,
|
||||
skinData,
|
||||
]
|
||||
|
||||
# -------------------------------------------------------
|
||||
|
||||
### [NAME ENTRANCES] ###
|
||||
|
||||
# Dictionnary containing special entrance values
|
||||
entrDictSpecial = {
|
||||
"0x7FF9": "ENTR_RETURN_YOUSEI_IZUMI_YOKO", # Great Fairy Fountain (spells)
|
||||
"0x7FFA": "ENTR_RETURN_SYATEKIJYOU", # Shooting gallery
|
||||
"0x7FFB": "ENTR_RETURN_2", # Unused
|
||||
"0x7FFC": "ENTR_RETURN_SHOP1", # Bazaar
|
||||
"0x7FFD": "ENTR_RETURN_4", # Unused
|
||||
"0x7FFE": "ENTR_RETURN_DAIYOUSEI_IZUMI", # Great Fairy Fountain (magic, double magic, double defense)
|
||||
"0x7FFF": "ENTR_RETURN_GROTTO", # Grottos and normal Fairy Fountain
|
||||
}
|
||||
190
tools/daf/functions.py
Normal file
190
tools/daf/functions.py
Normal file
@@ -0,0 +1,190 @@
|
||||
from os import walk
|
||||
from re import sub
|
||||
from argparse import ArgumentParser as Parser
|
||||
|
||||
try:
|
||||
from data import dataToFix, entrDictSpecial
|
||||
except:
|
||||
print("[DAF:Error]: ``data.py`` not found! Make sure everything is in the same folder.")
|
||||
quit()
|
||||
|
||||
# -------------------------------------------------------
|
||||
|
||||
### [GENERAL FUNCTIONS] ###
|
||||
|
||||
|
||||
def getArguments():
|
||||
"""Initialisation of the argument parser"""
|
||||
|
||||
parser = Parser(description="Fix various things related to assets for the OoT Decomp")
|
||||
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--mode",
|
||||
dest="mode",
|
||||
type=str,
|
||||
default="",
|
||||
help="available modes: `fix_types`, `name_entrances`, `fix_segments`",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--all",
|
||||
dest="run_all",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="run every mode",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
dest="verbose",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="show extra informations",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--path",
|
||||
dest="decompPath",
|
||||
default="",
|
||||
required=True,
|
||||
help="set decomp root",
|
||||
)
|
||||
|
||||
return parser.parse_args(), parser
|
||||
|
||||
|
||||
def getPaths(path: str, fileType: str):
|
||||
"""Returns a list of data paths with the specified extension"""
|
||||
paths = []
|
||||
for path, dirs, files in walk(path):
|
||||
for file in files:
|
||||
if file.endswith(fileType):
|
||||
paths.append(f"{path}/{file}")
|
||||
paths.sort()
|
||||
return paths
|
||||
|
||||
|
||||
def getArrayInfos(data: list, arrayName: str):
|
||||
"""Returns arrays containing line numbers for the start and the end of relevant C data"""
|
||||
arrayStartIndices = []
|
||||
arrayEndIndices = []
|
||||
hasListStarted = False
|
||||
|
||||
for lineNb, line in enumerate(data):
|
||||
if arrayName in line and line.endswith("] = {\n"):
|
||||
arrayStartIndices.append(lineNb + 1)
|
||||
hasListStarted = True
|
||||
|
||||
if hasListStarted and line.startswith("};\n"):
|
||||
arrayEndIndices.append(lineNb)
|
||||
hasListStarted = False
|
||||
try:
|
||||
if len(arrayStartIndices) != len(arrayEndIndices):
|
||||
raise IndexError
|
||||
except IndexError:
|
||||
print("[DAF:Error]: Start Length != End Length")
|
||||
|
||||
return arrayStartIndices, arrayEndIndices
|
||||
|
||||
|
||||
# -------------------------------------------------------
|
||||
|
||||
### [FIX TYPES MODE] ###
|
||||
|
||||
|
||||
def replaceOldData(path: str, extension: str):
|
||||
"""Replaces older names by newer ones"""
|
||||
paths = getPaths(path, extension)
|
||||
for path in paths:
|
||||
with open(path, "r") as curFile:
|
||||
fileData = curFile.read()
|
||||
for data in dataToFix:
|
||||
for key in data.keys():
|
||||
fileData = sub(rf"{key}\b", data[key], fileData)
|
||||
fileData = sub(rf"{key}_\B", f"{data[key]}_", fileData)
|
||||
with open(path, "w") as curFile:
|
||||
curFile.write(fileData)
|
||||
|
||||
|
||||
# -------------------------------------------------------
|
||||
|
||||
### [NAME ENTRANCES] ###
|
||||
|
||||
|
||||
def getEntranceDict(path: str):
|
||||
"""Returns a list containing every entrances"""
|
||||
entranceList = []
|
||||
|
||||
# read the entrance table
|
||||
try:
|
||||
with open(f"{path}/include/tables/entrance_table.h", "r") as fileData:
|
||||
# keep the relevant data
|
||||
for line in fileData.readlines():
|
||||
if line.startswith("/* 0x"):
|
||||
startIndex = line.find("ENTR_")
|
||||
entranceList.append(line[startIndex : line.find(",", startIndex)])
|
||||
except FileNotFoundError:
|
||||
raise print("[DAF:Error]: Can't find entrance_table.h!")
|
||||
|
||||
# return a dictionnary from the entrance list
|
||||
entranceDict = {f"0x{i:04X}": entrance for i, entrance in enumerate(entranceList)}
|
||||
return dict(entranceDict, **entrDictSpecial)
|
||||
|
||||
|
||||
def getNewFileData(data: list, dataDict: dict, arrayName: str):
|
||||
"""Returns the current data with the updated values"""
|
||||
startList, endList = getArrayInfos(data, arrayName)
|
||||
if len(startList) == len(endList):
|
||||
for curStart, curEnd in zip(startList, endList):
|
||||
for i in range(curEnd - curStart):
|
||||
curLine = curStart + i
|
||||
try:
|
||||
data[curLine] = (" " * 4) + f"{dataDict[data[curLine][:-2].lstrip()]},\n"
|
||||
except KeyError:
|
||||
# why???
|
||||
pass
|
||||
return data
|
||||
|
||||
|
||||
def replaceEntranceHex(decompRoot: str):
|
||||
"""Updates the entrances from OoT scenes"""
|
||||
entrDict = getEntranceDict(decompRoot)
|
||||
scenePaths = getPaths(f"{decompRoot}/assets/scenes/", ".c")
|
||||
|
||||
for path in scenePaths:
|
||||
data = []
|
||||
sceneName = None
|
||||
with open(path, "r") as file:
|
||||
if file.name.find("room") == -1:
|
||||
data = file.readlines()
|
||||
sceneName = file.name.split("/")[6][:-2]
|
||||
if sceneName is not None:
|
||||
newData = getNewFileData(data, entrDict, f"{sceneName}ExitList")
|
||||
with open(path, "w") as file:
|
||||
for line in newData:
|
||||
file.write(line)
|
||||
|
||||
|
||||
# -------------------------------------------------------
|
||||
|
||||
### [FIX SEGMENTS] ###
|
||||
|
||||
|
||||
def fixSegments(decompRoot):
|
||||
"""Adds u32 casts to room's segment symbols"""
|
||||
paths = getPaths(f"{decompRoot}/assets/scenes", ".c")
|
||||
for path in paths:
|
||||
with open(path, "r") as curFile:
|
||||
fileData = curFile.read()
|
||||
|
||||
fileData = sub(
|
||||
r"\w*SegmentRom\w*|\(u32\)\w*SegmentRom\w*",
|
||||
lambda match: f"(u32){match.group(0)}" if not match.group(0).startswith("(u32)") else match.group(0),
|
||||
fileData,
|
||||
)
|
||||
with open(path, "w") as curFile:
|
||||
curFile.write(fileData)
|
||||
Reference in New Issue
Block a user