2020-03-23 00:49:30 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
2020-05-26 20:36:30 +00:00
|
|
|
import argparse
|
2021-02-11 12:10:05 +00:00
|
|
|
import json
|
2020-06-21 21:50:32 +00:00
|
|
|
import csv
|
2020-06-22 00:58:56 +00:00
|
|
|
import git
|
2020-03-17 04:31:30 +00:00
|
|
|
import os
|
2020-03-23 00:49:30 +00:00
|
|
|
import re
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description="Computes current progress throughout the whole project.")
|
2021-02-11 12:10:05 +00:00
|
|
|
parser.add_argument("format", nargs="?", default="text", choices=["text", "csv", "shield-json"])
|
2020-03-23 00:49:30 +00:00
|
|
|
parser.add_argument("-m", "--matching", dest='matching', action='store_true',
|
|
|
|
help="Output matching progress instead of decompilation progress")
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
2020-05-26 20:36:30 +00:00
|
|
|
NON_MATCHING_PATTERN = r"#ifdef\s+NON_MATCHING.*?#pragma\s+GLOBAL_ASM\s*\(\s*\"(.*?)\"\s*\).*?#endif"
|
2020-03-23 00:49:30 +00:00
|
|
|
|
|
|
|
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
|
2020-03-17 04:31:30 +00:00
|
|
|
|
|
|
|
def ReadAllLines(fileName):
|
|
|
|
lineList = list()
|
|
|
|
with open(fileName) as f:
|
|
|
|
lineList = f.readlines()
|
|
|
|
|
|
|
|
return lineList
|
|
|
|
|
2020-03-23 00:49:30 +00:00
|
|
|
def GetFiles(path, ext):
|
2020-03-17 04:31:30 +00:00
|
|
|
files = []
|
|
|
|
|
|
|
|
for r, d, f in os.walk(path):
|
|
|
|
for file in f:
|
2020-03-23 00:49:30 +00:00
|
|
|
if file.endswith(ext):
|
2020-03-17 04:31:30 +00:00
|
|
|
files.append(os.path.join(r, file))
|
|
|
|
|
|
|
|
return files
|
|
|
|
|
2020-03-23 00:49:30 +00:00
|
|
|
nonMatchingFunctions = GetNonMatchingFunctions(GetFiles("src", ".c")) if not args.matching else []
|
|
|
|
|
2020-03-17 04:31:30 +00:00
|
|
|
def GetNonMatchingSize(path):
|
|
|
|
size = 0
|
|
|
|
|
2020-03-23 00:49:30 +00:00
|
|
|
asmFiles = GetFiles(path, ".s")
|
2020-03-17 04:31:30 +00:00
|
|
|
|
|
|
|
for asmFilePath in asmFiles:
|
2020-03-23 00:49:30 +00:00
|
|
|
if asmFilePath not in nonMatchingFunctions:
|
|
|
|
asmLines = ReadAllLines(asmFilePath)
|
2020-03-17 04:31:30 +00:00
|
|
|
|
2020-03-23 00:49:30 +00:00
|
|
|
for asmLine in asmLines:
|
|
|
|
if (asmLine.startswith("/*")):
|
|
|
|
size += 4
|
2020-03-17 04:31:30 +00:00
|
|
|
|
|
|
|
return size
|
|
|
|
|
|
|
|
|
|
|
|
mapFile = ReadAllLines("build/z64.map")
|
2021-12-01 13:41:38 +00:00
|
|
|
curSegment = None
|
2020-03-17 04:31:30 +00:00
|
|
|
src = 0
|
|
|
|
code = 0
|
|
|
|
boot = 0
|
|
|
|
ovl = 0
|
|
|
|
|
|
|
|
for line in mapFile:
|
2021-12-01 13:41:38 +00:00
|
|
|
|
|
|
|
if "_codeSegmentStart" in line:
|
|
|
|
curSegment = "code"
|
|
|
|
elif "_bootSegmentStart" in line:
|
|
|
|
curSegment = "boot"
|
|
|
|
elif "_codeSegmentEnd" in line or "_bootSegmentEnd" in line:
|
|
|
|
curSegment = None
|
|
|
|
|
2020-03-17 04:31:30 +00:00
|
|
|
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 (objFile.startswith("build/src")):
|
|
|
|
src += size
|
|
|
|
|
2021-12-01 13:41:38 +00:00
|
|
|
if (objFile.startswith("build/src/code") or (objFile.startswith("build/src/libultra/") and curSegment == "code")):
|
2020-03-17 04:31:30 +00:00
|
|
|
code += size
|
2021-12-01 13:41:38 +00:00
|
|
|
elif (objFile.startswith("build/src/boot") or (objFile.startswith("build/src/libultra/") and curSegment == "boot")):
|
2020-03-17 04:31:30 +00:00
|
|
|
boot += size
|
|
|
|
elif (objFile.startswith("build/src/overlays")):
|
|
|
|
ovl += size
|
|
|
|
|
|
|
|
nonMatchingASM = GetNonMatchingSize("asm/non_matchings")
|
|
|
|
nonMatchingASMBoot = GetNonMatchingSize("asm/non_matchings/boot")
|
2021-08-09 00:08:59 +00:00
|
|
|
nonMatchingASMCode = GetNonMatchingSize("asm/non_matchings/code")
|
2020-03-17 04:31:30 +00:00
|
|
|
nonMatchingASMOvl = GetNonMatchingSize("asm/non_matchings/overlays")
|
|
|
|
|
|
|
|
src -= nonMatchingASM
|
|
|
|
code -= nonMatchingASMCode
|
|
|
|
boot -= nonMatchingASMBoot
|
|
|
|
ovl -= nonMatchingASMOvl
|
2020-03-23 22:16:05 +00:00
|
|
|
|
2021-01-31 18:27:50 +00:00
|
|
|
bootSize = 31408 # decompilable code only
|
2021-11-23 18:31:44 +00:00
|
|
|
codeSize = 999984 # decompilable code only
|
2020-03-23 22:16:05 +00:00
|
|
|
ovlSize = 2812000 # .text sections
|
|
|
|
|
2021-08-09 00:08:59 +00:00
|
|
|
total = src + nonMatchingASM
|
2020-03-17 04:31:30 +00:00
|
|
|
srcPct = 100 * src / total
|
|
|
|
codePct = 100 * code / codeSize
|
|
|
|
bootPct = 100 * boot / bootSize
|
|
|
|
ovlPct = 100 * ovl / ovlSize
|
2021-08-09 00:08:59 +00:00
|
|
|
|
2021-11-28 11:06:27 +00:00
|
|
|
bytesPerHeartPiece = total // 80
|
2020-03-17 04:31:30 +00:00
|
|
|
|
2021-02-11 12:10:05 +00:00
|
|
|
if args.format == 'csv':
|
2021-12-18 15:09:51 +00:00
|
|
|
csv_version = 2
|
2020-06-22 00:58:56 +00:00
|
|
|
git_object = git.Repo().head.object
|
|
|
|
timestamp = str(git_object.committed_date)
|
|
|
|
git_hash = git_object.hexsha
|
2021-12-18 15:09:51 +00:00
|
|
|
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))]
|
2020-06-22 00:58:56 +00:00
|
|
|
print(",".join(csv_list))
|
2021-02-11 12:10:05 +00:00
|
|
|
elif args.format == 'shield-json':
|
|
|
|
# https://shields.io/endpoint
|
|
|
|
print(json.dumps({
|
|
|
|
"schemaVersion": 1,
|
|
|
|
"label": "progress",
|
|
|
|
"message": f"{srcPct:.3g}%",
|
2021-11-23 18:31:44 +00:00
|
|
|
"color": 'yellow' if srcPct < 100 else 'brightgreen',
|
2021-02-11 12:10:05 +00:00
|
|
|
}))
|
|
|
|
elif args.format == 'text':
|
2020-05-26 20:36:30 +00:00
|
|
|
adjective = "decompiled" if not args.matching else "matched"
|
|
|
|
|
|
|
|
print(str(total) + " total bytes of decompilable code\n")
|
|
|
|
print(str(src) + " bytes " + adjective + " in src " + str(srcPct) + "%\n")
|
|
|
|
print(str(boot) + "/" + str(bootSize) + " bytes " + adjective + " in boot " + str(bootPct) + "%\n")
|
|
|
|
print(str(code) + "/" + str(codeSize) + " bytes " + adjective + " in code " + str(codePct) + "%\n")
|
|
|
|
print(str(ovl) + "/" + str(ovlSize) + " bytes " + adjective + " in overlays " + str(ovlPct) + "%\n")
|
|
|
|
print("------------------------------------\n")
|
|
|
|
|
|
|
|
heartPieces = int(src / bytesPerHeartPiece)
|
|
|
|
rupees = int(((src % bytesPerHeartPiece) * 100) / bytesPerHeartPiece)
|
|
|
|
|
|
|
|
if (rupees > 0):
|
|
|
|
print("You have " + str(heartPieces) + "/80 heart pieces and " + str(rupees) + " rupee(s).\n")
|
|
|
|
else:
|
|
|
|
print("You have " + str(heartPieces) + "/80 heart pieces.\n")
|
2021-02-11 12:10:05 +00:00
|
|
|
else:
|
|
|
|
print("Unknown format argument: " + args.format)
|