diff --git a/tools/progress.py b/tools/progress.py new file mode 100644 index 0000000..35fe30f --- /dev/null +++ b/tools/progress.py @@ -0,0 +1,110 @@ +import argparse +import json +import git +from pathlib import Path +import math + +parser = argparse.ArgumentParser(description="Computes current progress throughout the whole project.") +parser.add_argument("format", nargs="?", default="text", choices=["text", "csv", "shield-json"]) +args = parser.parse_args() + + +def ReadAllLines(fileName: Path) -> list[str]: + with open(fileName) as f: + return f.readlines() + + +def GetFiles(path: Path, ext) -> list[Path]: + files: list[Path] = list() + + for (dirpath, _, filenames) in path.walk(): + for file in filenames: + if Path(file).suffix == ext: + files.append(dirpath / file) + + return files + + +def GetSizeOfASMSourceFile(asmSrc: Path) -> int: + size: int = 0 + asmLines: list[str] = ReadAllLines(asmSrc) + + for asmLine in asmLines: + if asmLine.startswith("\t"): + asmLineStripped = asmLine.strip() + if not (asmLineStripped.startswith("AREA") or + asmLineStripped.startswith("EXPORT") or + asmLineStripped.startswith("END") or + asmLineStripped.startswith("DCD") or + asmLineStripped.startswith("DCB")): + size += 4 + elif asmLineStripped.startswith("DCD "): + size += 4 + elif asmLineStripped.startswith("DCB "): + size += len(asmLineStripped.split(',')) + + size = 4 * math.ceil(size / 4.0) + return size + + +def GetAllNonMatchingASMFunctionSourceFiles(nonmatchFiles: list[Path]) -> list[Path]: + nonmatchASMFunctionSourceFiles: list[Path] = list() + + for nonmatchFile in nonmatchFiles: + with open(nonmatchFile, 'r') as nonmatch: + nonmatchLines: list[str] = nonmatch.readlines() + for line in nonmatchLines: + if line.startswith("\tGET") and line.count("disassembly\\asm") > 0: + nonmatchASMFunctionSourceFiles.append(Path(line.split(' ')[-1].strip())) + + return nonmatchASMFunctionSourceFiles + + +def GetTotalNonMatchSize() -> int: + nonmatchFiles: list[Path] = GetFiles(Path("build/nonmatch"), ".s") + nonMatchingASMFunctionSourceFiles: list[Path] = GetAllNonMatchingASMFunctionSourceFiles(nonmatchFiles) + + size: int = 0 + for nonMatchFunction in nonMatchingASMFunctionSourceFiles: + size += GetSizeOfASMSourceFile(nonMatchFunction) + return size + + +nonMatchingASMSize: int = GetTotalNonMatchSize() + +totalSize: int = 0x3C97B8 +srcSize: int = totalSize - nonMatchingASMSize + +srcPct: float = 100.0 * srcSize / totalSize + +bytesPerHeartPiece: float = totalSize / 80.0 + +if args.format == 'csv': + version = 1 + git_object = git.Repo().head.object + timestamp = str(git_object.committed_date) + git_hash = git_object.hexsha + csv_list = [str(version), timestamp, git_hash, str(srcSize), str(nonMatchingASMSize)] + print(",".join(csv_list)) +elif args.format == 'shield-json': + # https://shields.io/endpoint + print(json.dumps({ + "schemaVersion": 1, + "label": "progress", + "message": f"{srcPct:.3g}%", + "color": 'yellow', + })) +elif args.format == 'text': + print(f"{totalSize} total bytes of decompilable code\n") + print(f"{srcSize} bytes decompiled in src {srcPct}%\n") + print("------------------------------------\n") + + heartPieces = int(srcSize / bytesPerHeartPiece) + rupees = int(((srcSize % bytesPerHeartPiece) * 100) / bytesPerHeartPiece) + + if (rupees > 0): + print(f"You have {heartPieces}/80 heart pieces and {rupees} rupee{"s" if rupees != 1 else ""}.\n") + else: + print(f"You have {heartPieces}/80 heart pieces.\n") +else: + print(f"Unknown format argument: {args.format}")