diff --git a/calcprogress.py b/calcprogress.py deleted file mode 100755 index 182bbdc1..00000000 --- a/calcprogress.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import json -import csv -import git -import os -import re - -parser = argparse.ArgumentParser(description="Computes current progress throughout the whole project.") -parser.add_argument("format", nargs="?", default="text", choices=["text", "csv", "shield-json"]) -parser.add_argument("-m", "--matching", dest='matching', action='store_true', - help="Output matching progress instead of decompilation progress") -args = parser.parse_args() - -NON_MATCHING_PATTERN = r"#ifdef\s+NON_MATCHING.*?#pragma\s+GLOBAL_ASM\s*\(\s*\"(.*?)\"\s*\).*?#endif" - -def GetNonMatchingFunctions(files): - functions = [] - - for file in files: - with open(file) as f: - functions += re.findall(NON_MATCHING_PATTERN, f.read(), re.DOTALL) - - return functions - -def ReadAllLines(fileName): - lineList = list() - with open(fileName) as f: - lineList = f.readlines() - - return lineList - -def GetFiles(path, ext): - files = [] - - for r, d, f in os.walk(path): - for file in f: - if file.endswith(ext): - files.append(os.path.join(r, file)) - - return files - -nonMatchingFunctions = GetNonMatchingFunctions(GetFiles("src", ".c")) if not args.matching else [] - -def GetNonMatchingSize(path): - size = 0 - - asmFiles = GetFiles(path, ".s") - - for asmFilePath in asmFiles: - if asmFilePath not in nonMatchingFunctions: - asmLines = ReadAllLines(asmFilePath) - - for asmLine in asmLines: - if (asmLine.startswith("/*")): - size += 4 - - return size - -def IsCFile(objfile): - srcfile = objfile.strip().replace("build/", "").replace(".c.o", ".c") - return os.path.isfile(srcfile) - -mapFile = ReadAllLines("build/pokestadium.map") -src = 0 -asm = 0 - -for line in mapFile: - lineSplit = list(filter(None, line.split(" "))) - - if (len(lineSplit) == 4 and lineSplit[0].startswith(".")): - section = lineSplit[0] - size = int(lineSplit[2], 16) - objFile = lineSplit[3] - - if (section == ".text"): - if (IsCFile(objFile) and objFile.startswith("build/src")): - src += size - elif (objFile.startswith("build/asm")): - asm += size - -nonMatchingASM = GetNonMatchingSize("asm/non_matchings") - -src -= nonMatchingASM - -total = src + nonMatchingASM + asm -srcPct = 100 * src / total - -bytesPerPokemon = total // 151 - -if args.format == 'csv': - csv_version = 2 - git_object = git.Repo().head.object - timestamp = str(git_object.committed_date) - git_hash = git_object.hexsha - csv_list = [str(csv_version), timestamp, git_hash, str(src), str(total), str(code), str(codeSize), str(boot), str(bootSize), str(ovl), str(ovlSize), str(nonMatchingASM), str(len(nonMatchingFunctions))] - 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' if srcPct < 100 else 'brightgreen', - })) -elif args.format == 'text': - adjective = "decompiled" if not args.matching else "matched" - - print(str(total) + " total bytes of decompilable code") - print(str(src) + " bytes " + adjective + " in src " + str(srcPct) + "%\n") - print("------------------------------------\n") - - pokemonCount = int(src / bytesPerPokemon) - - print("You have " + str(pokemonCount) + "/151 Pokedex entries.\n") -else: - print("Unknown format argument: " + args.format) diff --git a/progress.py b/progress.py new file mode 100755 index 00000000..810500d2 --- /dev/null +++ b/progress.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: © 2023 ZeldaRET +# SPDX-License-Identifier: MIT + +from __future__ import annotations + +import argparse +import mapfile_parser +from pathlib import Path + + +ASMPATH = Path("asm") +NONMATCHINGS = "nonmatchings" + + +def getProgressFromMapFile(mapFile: mapfile_parser.MapFile, asmPath: Path, nonmatchings: Path, aliases: dict[str, str]=dict(), pathIndex: int=2) -> tuple[mapfile_parser.ProgressStats, dict[str, mapfile_parser.ProgressStats]]: + totalStats = mapfile_parser.ProgressStats() + progressPerFolder: dict[str, mapfile_parser.ProgressStats] = dict() + + for segment in mapFile: + for file in segment: + if len(file) == 0: + continue + + folder = file.filepath.parts[pathIndex] + + if ".a" in folder: + folder = folder.split('.a')[0] + + if folder in aliases: + folder = aliases[folder] + + if folder not in progressPerFolder: + progressPerFolder[folder] = mapfile_parser.ProgressStats() + + originalFilePath = Path(*file.filepath.parts[pathIndex:]) + + extensionlessFilePath = originalFilePath + while extensionlessFilePath.suffix: + extensionlessFilePath = extensionlessFilePath.with_suffix("") + + fullAsmFile = asmPath / extensionlessFilePath.with_suffix(".s") + wholeFileIsUndecomped = fullAsmFile.exists() + + + for func in file: + funcAsmPath = nonmatchings / extensionlessFilePath / f"{func.name}.s" + + symSize = 0 + if func.size is not None: + symSize = func.size + + if wholeFileIsUndecomped: + totalStats.undecompedSize += symSize + progressPerFolder[folder].undecompedSize += symSize + elif funcAsmPath.exists(): + totalStats.undecompedSize += symSize + progressPerFolder[folder].undecompedSize += symSize + else: + totalStats.decompedSize += symSize + progressPerFolder[folder].decompedSize += symSize + + return totalStats, progressPerFolder + + +def getProgress(mapPath: Path, version: str) -> tuple[mapfile_parser.ProgressStats, dict[str, mapfile_parser.ProgressStats]]: + mapFile = mapfile_parser.MapFile() + mapFile.readMapFile(mapPath) + + for segment in mapFile: + for file in segment: + if len(file) == 0: + continue + + filepathParts = list(file.filepath.parts) + if version in filepathParts: + filepathParts.remove(version) + file.filepath = Path(*filepathParts) + + nonMatchingsPath = ASMPATH / version / NONMATCHINGS + + return getProgressFromMapFile(mapFile.filterBySectionType(".text"), ASMPATH / version, nonMatchingsPath, aliases={"ultralib": "libultra"}) + +def progressMain(): + parser = argparse.ArgumentParser() + parser.add_argument("-v", "--version", help="version to process", default="us") + + args = parser.parse_args() + + mapPath = Path("build") / f"pokestadium-{args.version}.map" + + totalStats, progressPerFolder = getProgress(mapPath, args.version) + + mapfile_parser.progress_stats.printStats(totalStats, progressPerFolder) + +if __name__ == "__main__": + progressMain()