Files
mm/tools/buildtools/extract_yars.py
T
Derek Hensley 471d86f530 Build Sync (#1600)
* Rename outputs

* Makefile target renames

* Add run target

* yeet z64compress

* venv

* baserom_uncompressed -> baserom-decompressed

* input rom name to baserom.z64

* Add BUILD_DIR makefile variable

* Move built roms to build dir

* Move baserom to baseroms folder

* Add version to map file name

* Makefile cleanup

* Rename ldscript to include version

* Multiversion build

* n64-us version name

* Remove venv as dependency of setup

* Readme wording

* extract_baserom.py suggestion

* Readd checksums

* Make .venv work with windows

* missed an endif

* Cleaner windows venv implementation

* Remove duplciate process

* Build process steps

* Move make_options back

* Fix schedule build directory

* Fix schedule includes

* Makefile NON_MATCHING check -> != 0

* OOT 1704 changes

* Small cleanups

* Missed 1 thing

* UNSET -> SYMS

* Update extract_baserom.py

* dmadata.py

* Small cleanup

* dmadata_start

* Format

* dmadata files

* Fix makefile comment

* Jenkins report fix

* extracted dir

* Python dependencies order in readme
2024-04-06 11:07:58 -07:00

124 lines
3.4 KiB
Python
Executable File

#!/usr/bin/env python3
# SPDX-FileCopyrightText: © 2023 ZeldaRET
# SPDX-License-Identifier: MIT
# yar (Yaz0 ARchive) decompressor
#
# This program decompresses every raw yar binary file listed in
# `tools/filelists/{version}/archives.csv` to a corresponding
# `extracted/{version}/baserom/{file}.unarchive` raw file.
#
# It works by decompressing every Yaz0 block and appending them one by one into
# a new raw binary file so it can be processed normally by other tools.
from __future__ import annotations
import argparse
import crunch64
import dataclasses
from pathlib import Path
import struct
PRINT_XML = False
@dataclasses.dataclass
class ArchiveMeta:
start: int
end: int
def readFileAsBytearray(filepath: Path) -> bytearray:
with filepath.open(mode="rb") as f:
return bytearray(f.read())
def readBytesAsWord(array_of_bytes: bytearray, offset: int) -> int:
return struct.unpack_from(f">I", array_of_bytes, offset)[0]
def getOffsetsList(archiveBytes: bytearray) -> list[ArchiveMeta]:
archivesOffsets: list[ArchiveMeta] = []
firstEntryOffset = readBytesAsWord(archiveBytes, 0)
firstEntrySize = readBytesAsWord(archiveBytes, 4)
archivesOffsets.append(
ArchiveMeta(firstEntryOffset, firstEntryOffset + firstEntrySize)
)
offset = 4
while offset < firstEntryOffset - 4:
entry = readBytesAsWord(archiveBytes, offset)
nextEntry = readBytesAsWord(archiveBytes, offset + 4)
entryStart = entry + firstEntryOffset
entryEnd = nextEntry + firstEntryOffset
archivesOffsets.append(ArchiveMeta(entryStart, entryEnd))
offset += 4
return archivesOffsets
def extractArchive(archivePath: Path, outPath: Path):
archiveBytes = readFileAsBytearray(archivePath)
if readBytesAsWord(archiveBytes, 0) == 0:
# Empty file, ignore it
return
print(f"Extracting '{archivePath}' -> '{outPath}'")
archivesOffsets = getOffsetsList(archiveBytes)
if PRINT_XML:
print("<Root>")
print(f' <File Name="{outPath.stem}">')
with outPath.open("wb") as out:
currentOffset = 0
for meta in archivesOffsets:
decompressedBytes = crunch64.yaz0.decompress(
archiveBytes[meta.start : meta.end]
)
decompressedSize = len(decompressedBytes)
out.write(decompressedBytes)
if PRINT_XML:
print(
f' <Blob Name="{archivePath.stem}_Blob_{currentOffset:06X}" Size="0x{decompressedSize:04X}" Offset="0x{currentOffset:X}" />'
)
currentOffset += decompressedSize
if PRINT_XML:
print(f" </File>")
print("</Root>")
def main():
parser = argparse.ArgumentParser(description="MM archives extractor")
parser.add_argument("version", help="version to process", default="n64-us")
parser.add_argument("--xml", help="Generate xml to stdout", action="store_true")
args = parser.parse_args()
global PRINT_XML
PRINT_XML = args.xml
archivesCsvPath = Path(f"tools/filelists/{args.version}/archives.csv")
with archivesCsvPath.open() as f:
for line in f:
archiveName = line.strip().split(",")[1]
archivePath = Path(f"extracted/{args.version}/baserom/{archiveName}")
extractedPath = Path(str(archivePath) + ".unarchive")
extractArchive(archivePath, extractedPath)
if __name__ == "__main__":
main()