1
0
mirror of https://github.com/blawar/ooot.git synced 2024-07-05 10:33:36 +00:00
ooot/tools/extract_assets.py

136 lines
5.6 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import argparse, os, shutil, time
from itertools import repeat
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
2022-02-06 23:29:28 +00:00
from tqdm import tqdm
import shutil
from oot import *
def ExtractFile(xmlPath: Path, outputPath: Path, outputSourcePath: Path, unaccounted: bool):
# Create target folder recursivly, ignore error if it already exists
createDir(outputSourcePath)
zapd = Path(zapdBinary())
zapdConfig = Path(romPath("zapd/Config.xml"))
baserom = assetPath('baserom')
externalXmlFolder = assetPath('xml')
execStr = f"{zapd} e -i {xmlPath} -b {baserom} -o {outputPath} -osf {outputSourcePath} -gsf 1 -rconf {zapdConfig} --externalXmlFolder {externalXmlFolder}"
# Use error handler only if non-windows environment, because it's not supported in windows
if os.name != 'nt':
execStr += " -eh"
if "overlays" in str(xmlPath):
execStr += " --static"
if unaccounted == True:
execStr += " --warn-unaccounted"
# print(f"Extracting {xmlPath.stem}")
# print(execStr)
2020-03-17 04:31:30 +00:00
exitValue = os.system(execStr)
if exitValue != 0:
return ("failed", f"Error extracting {xmlPath}: {os.sys.stderr}")
else:
return ("success", f"Successfully extracted {xmlPath}")
def ExtractFunc(fullPath: Path, force: bool, unaccounted: bool):
# Ensure that file exists. It can happen that files don't exist if paths are manually passed
if fullPath.exists() == False:
return ("failed", f"File cannot be found: {fullPath}")
# Remove xml directory and suffix in path to use as output directory
# Example: assets/xml/objects/object_example.xml -> assets/objects/object_bdoor
rel = relPath(fullPath, romPath())[4:-4]
outPath = Path(assetPath(rel))
# If output is more recent than source file: skip extraction
# Don't skip if force == True was passed
if force == False and outPath.is_dir() and outPath.stat().st_mtime > fullPath.stat().st_mtime:
return ("skipped", f"Skipped extraction of {fullPath}")
# Delete target folder before extraction, ignore errors in case there is no folder to delete
shutil.rmtree(outPath, ignore_errors=True)
return ExtractFile(fullPath, outPath, outPath, unaccounted)
def ExtractText(force: bool):
extract_text_path = Path(assetPath("text/message_data.h"))
extract_staff_text_path = Path(assetPath("text/message_data_staff.h"))
# Ensure target folder exists
createDir(extract_text_path.parent)
# Always extract if force flag was passed
# otherwise check if target already exists and skip (by setting it to None) if it does
if force == False:
if extract_text_path.exists():
extract_text_path = None
if extract_staff_text_path.exists():
extract_staff_text_path = None
if extract_text_path is not None or extract_staff_text_path is not None:
print("Start Extracting text")
2022-02-18 05:09:37 +00:00
import msgdis
msgdis.extract_all_text(extract_text_path, extract_staff_text_path)
print("Finished extracting text")
def ExtractXMLAssets(xmlFiles, force: bool, unaccounted: bool):
thread_count = os.cpu_count() or 1
print(f"Start extracting {len(xmlFiles)} assets with %d threads" % thread_count)
success = skipped = failed = 0
errors = []
# Multithreading instead of multiprocessing, because of IO heavy operation
with ThreadPoolExecutor(max_workers = thread_count) as executor:
2022-02-06 23:29:28 +00:00
with tqdm(total=len(xmlFiles)) as progress:
for result in executor.map(ExtractFunc, xmlFiles, repeat(force), repeat(unaccounted)):
progress.update()
# Parsing of results
status = result[0]
message = result[1]
if status == "success":
success += 1
elif status == "skipped":
skipped += 1
else:
failed += 1
errors.append(message)
print(f"Extracted: {success}, Skipped: {skipped}, Failed: {failed}")
if errors:
print(f"Errors: {errors}")
def main():
# Command Line Interface
parser = argparse.ArgumentParser(description="baserom asset extractor")
parser.add_argument("assets", help="asset path(s) relative to assets/xml/, e.g. objects/gameplay_keep. Passing nothing will extract entire assets/xml/ tree", nargs="*", default=None)
parser.add_argument("-f", "--force", help="Force the extraction of every asset instead of only recently modified", action="store_true", default=False)
parser.add_argument("-u", "--unaccounted", help="Enables ZAPD unaccounted detector warning system.", action="store_true", default=False)
args = parser.parse_args()
shutil.rmtree(Path(assetPath('xml')), ignore_errors = True)
2022-03-24 00:46:26 +00:00
shutil.copytree(Path(romPath('xml')), Path(assetPath('xml')))
# List of xml files for extraction
xmlFiles = None
if args.assets and len(args.assets) > 1:
# Generate list of Paths by transforming asset inputs into full Path structure
# Example: objects/gameplay_keep --> assets/xml/objects/gameplay_keep.xml
xmlFiles = [Path(assetPath('xml')).joinpath(file).with_suffix('.xml') for file in args.assets[1:]]
else:
# Get list of all xml files in assets/xml/ subdirectories recursivly
xmlFiles = sorted(Path(romPath()).glob('xml/**/*.xml'))
start = time.perf_counter()
ExtractText(args.force)
ExtractXMLAssets(xmlFiles, args.force, args.unaccounted)
finish = time.perf_counter()
print(f"Finished extracting assets in {round(finish-start, 2)} seconds")
if __name__ == "__main__":
z_message_PAL, message_data_static and surrounding doc (#996) * Initial progress on z_message_PAL, very messy * Fix merge * Some more progress * Fix merge * More z_message_PAL * Small progress * More small progress * message_data_static files OK * Prepare z_message_tables * Matched another function, small updates * Attempt to use asm-processor static-symbols branch * Refactor text id declarations * Begin large text codes parser function * Fix merge * Refactor done * Build OK, add color and highscore names * Remove encoded text headers and automatically encode during build * Fix kanfont * Various cleanups * DISP macros * Another match aside data * Further progress * Small improvements * Deduplicate magic values for text control codes, small improvements * Tiny progress * Minor cleanups * Clean up z_message_PAL comment * Progress on large functions * Further progress on large functions * Changes to mkldscript to link .data in the .rodata section * data OK * Few improvements * Use gDPLoadTextureBlock macros where appropriate * rm z_message_tables, progress on large functions * 2 more matches * Improvements * Small progress * More progress on big function * progress * match func_80107980 * match Message_Update * match func_8010BED8 * done * Progress on remaining large functions * Small progress on largest function * Another match, extract text and move to assets, improve text build system * Small nonmatchings improvements * docs wip * Largest function maybe equivalent * Fix merge * Document do_action values, largest function is almost instruction-matching * Rename NAVI do_action to NONE, as that appears to be how that value is used in practice * Fix merge * one match * Last function is instruction-matching * Fix * Improvements thanks to engineer124 * Stack matched thanks to petrie911, now just a/v/low t regalloc issues, some cleanup * More variables labeled, use text state enum everywhere * More labels and names * Fix * Actor_IsTalking -> Actor_TalkRequested * Match func_8010C39C and remove unused asm * More docs * Mostly ocarina related docs * All msgModes named * Fix assetclean * Cleanup * Extraction fixes and headers * Suggestions * Review suggestions * Change text extraction again, only extract if the headers do not already exist * Fix * Use ast for charmap, fix assetclean for real this time * Review suggestions * BGM ids and ran formatter * Review comments * rename include_readonly to include_data_with_rodata * Remove leading 0s in number directives * Review suggestions for message_data_static * textbox pos enum comments, rename several enum names from Message to TextBox Co-authored-by: Thar0 <maximilianc64@gmail.com> Co-authored-by: Zelllll <56516451+Zelllll@users.noreply.github.com> Co-authored-by: petrie911 <pmontag@DESKTOP-LG8A167.localdomain> Co-authored-by: Roman971 <romanlasnier@hotmail.com>
2021-11-23 01:20:30 +00:00
main()