diff --git a/calcprogress.py b/calcprogress.py new file mode 100644 index 0000000..182bbdc --- /dev/null +++ b/calcprogress.py @@ -0,0 +1,118 @@ +#!/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/splat.yaml b/splat.yaml index 1397155..793b5c9 100644 --- a/splat.yaml +++ b/splat.yaml @@ -20,7 +20,7 @@ segments: subsegments: - [0x0040, asm, boot] - [0x0B70, bin, bootcode_font] - - name: code + - name: text type: code start: 0x1000 vram: 0x80000400