2021-11-05 05:17:09 -06:00
#!/usr/bin/env python3
import argparse
import json
import csv
import os
import re
2024-04-13 02:54:59 +02:00
from pybadges import badge
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Script arguments.
2021-11-05 05:17:09 -06:00
parser = argparse . ArgumentParser ( description = " Computes current progress throughout the whole project. " )
2024-04-13 02:54:59 +02:00
parser . add_argument ( " format " , nargs = " ? " , default = " text " , choices = [ " text " , " verbose " , " totalBadge " , " gameBadge " , " mainBadge " , " endingBadge " , " racingBadge " , " audioBadge " , " osBadge " , " bytesToDecompile " , " globalAsmFuncs " , " m2cFuncs " , " nonmatchingFuncs " , " badge " ] )
2023-11-05 15:57:14 -05:00
parser . add_argument ( " -d " , " --debug " , dest = ' debug ' , action = ' store_true ' ,
help = " Adds additional debug information, outputs files parsed and score for each file " )
parser . add_argument ( " -n " , " --nonmatching " , dest = ' nonmatching ' , action = ' store_true ' ,
help = " Tracks progress counting non matching functions " )
2021-11-05 05:17:09 -06:00
args = parser . parse_args ( )
2023-11-05 15:57:14 -05:00
# Patterns for "NON_MATCHING" defines, one of those is used depending of the project.
NON_MATCHING_FUNC_PATTERN = r " #ifdef \ s+NON_MATCHING.*?GLOBAL_ASM \ s* \ ( \ s* \" (.*?) \" \ s* \ ).*?#endif "
NON_MATCHING_PRAGMA_PATTERN = r " #ifdef \ s+NON_MATCHING.*?#pragma \ s+GLOBAL_ASM \ s* \ ( \ s* \" (.*?) \" \ s* \ ).*?#endif "
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Gets the assembly file around a NON_MATCHING define.
2021-11-05 05:17:09 -06:00
def GetNonMatchingFunctions ( files ) :
functions = [ ]
for file in files :
2024-04-13 02:54:59 +02:00
with open ( file , encoding = " utf-8 " ) as f :
2023-11-05 15:57:14 -05:00
functions + = re . findall ( NON_MATCHING_FUNC_PATTERN , f . read ( ) , re . DOTALL )
functions + = re . findall ( NON_MATCHING_PRAGMA_PATTERN , f . read ( ) , re . DOTALL )
2021-11-05 05:17:09 -06:00
return functions
2023-11-05 15:57:14 -05:00
# Pattern for "MIPS_TO_C" define
MIPS_TO_C_FUNC_PATTERN = r " #ifdef \ s+MIPS_TO_C.*?GLOBAL_ASM \ s* \ ( \ s* \" (.*?) \" \ s* \ ).*?#endif "
# Gets the assembly file around a MIPS_TO_C define.
2021-12-03 04:33:36 -07:00
def CountMipsToCFunctions ( files ) :
functions = [ ]
for file in files :
2024-04-13 02:54:59 +02:00
with open ( file , encoding = " utf-8 " ) as f :
2023-11-05 15:57:14 -05:00
functions + = re . findall ( MIPS_TO_C_FUNC_PATTERN , f . read ( ) , re . DOTALL )
2021-12-03 04:33:36 -07:00
return functions
2023-11-05 15:57:14 -05:00
# Pattern for "GLOBAL_ASM" macros
GLOBAL_ASM_FUNC_PATTERN = r " GLOBAL_ASM \ s* \ ( \ s* \" (.*?) \" \ s* \ ). "
2021-12-03 04:33:36 -07:00
2023-11-05 15:57:14 -05:00
# Gets the assembly file defined in a GLOBAL_ASM macro.
2023-10-14 13:07:09 -05:00
def CountGlobalAsmFunctions ( files ) :
functions = [ ]
2021-12-03 04:33:36 -07:00
2023-10-14 13:07:09 -05:00
for file in files :
2024-04-13 02:54:59 +02:00
with open ( file , encoding = " utf-8 " ) as f :
2023-11-05 15:57:14 -05:00
functions + = re . findall ( GLOBAL_ASM_FUNC_PATTERN , f . read ( ) , re . DOTALL )
2023-10-14 13:07:09 -05:00
return functions
2023-11-05 15:57:14 -05:00
# Regex to find a non static C function. Consists of 3 groups, comment documentation, function type and function name.
# We only take the function name here for simple needs.
2023-10-14 13:07:09 -05:00
C_FUNCTION_PATERN_REGEX = r ' ^(?<!static \ s)(?:( \ /[*][*!][*]* \ n(?:[^/]* \ n)+? \ s*[*] \ / \ n)(?: \ s*)*?)?(?: \ s*UNUSED \ s+)?([^ \ s]+) \ s(?: \ s|[*])*?([0-9A-Za-z_]+) \ s*[(][^)]*[)] \ s* { '
2023-11-05 15:57:14 -05:00
# Gets the function name and the C file which has it.
2023-10-14 13:07:09 -05:00
def GetCFunctions ( files ) :
functions = [ ]
for file in files :
2024-04-13 02:54:59 +02:00
with open ( file , encoding = " utf-8 " ) as f :
2023-10-14 13:07:09 -05:00
source_code = f . read ( )
2023-11-05 15:57:14 -05:00
# Parse regex pattern
2023-10-14 13:07:09 -05:00
matches = re . finditer ( C_FUNCTION_PATERN_REGEX , source_code , re . MULTILINE )
for match in matches :
2023-11-05 15:57:14 -05:00
# Group 3 has functions names taken from the regex
2023-10-14 13:07:09 -05:00
function_name = match . group ( 3 )
2023-11-05 15:57:14 -05:00
# Get the C file which has the function
2023-10-14 13:07:09 -05:00
functions . append ( ( file , function_name ) )
return functions
2021-12-03 04:33:36 -07:00
2023-11-05 15:57:14 -05:00
# Reads the string of all lines in a file.
2021-11-05 05:17:09 -06:00
def ReadAllLines ( fileName ) :
lineList = list ( )
with open ( fileName ) as f :
lineList = f . readlines ( )
return lineList
2023-11-05 15:57:14 -05:00
# Gets the file on an extension in a path and it's subdirectories.
2021-11-05 05:17:09 -06:00
def GetFiles ( path , ext ) :
files = [ ]
2023-10-14 13:07:09 -05:00
2021-11-05 05:17:09 -06:00
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
2023-11-05 15:57:14 -05:00
# Gets the file on an extension in a path and it's subdirectories except the ones under a blacklist.
2023-10-14 13:07:09 -05:00
def GetFilesBlackList ( path , ext , blacklist = [ ] ) :
files = [ ]
2021-11-05 05:17:09 -06:00
2023-10-14 13:07:09 -05:00
for r , d , f in os . walk ( path ) :
d [ : ] = [ dir for dir in d if dir not in blacklist ]
2023-09-06 20:39:16 -06:00
2023-10-14 13:07:09 -05:00
for file in f :
if file . endswith ( ext ) :
files . append ( os . path . join ( r , file ) )
return files
2023-11-05 15:57:14 -05:00
# Gets the file on an extension in a path and only it's subdirectories under a whilelist.
2023-10-14 13:07:09 -05:00
def GetFilesWhiteList ( path , ext , whitelist = [ ] ) :
files = [ ]
2023-11-05 15:57:14 -05:00
for r , d , f in os . walk ( path ) :
if r == path or any ( subdir in r for subdir in whitelist ) :
for file in f :
if file . endswith ( ext ) :
files . append ( os . path . join ( r , file ) )
2023-10-14 13:07:09 -05:00
return files
2023-11-05 15:57:14 -05:00
# Segments have their own folder, used later for the following:
# Whilelist (C Functions list, includes src folder and only these subdirectories).
# Blacklist (For main segment, includes all folders in nonmatching except these subdirectories).
gameExclusiveDirs = [ " ending " , " racing " , " os " , " audio " ]
2023-10-14 13:07:09 -05:00
2023-11-05 15:57:14 -05:00
# Get non matching functions and count how many there is.
# If --nonmatching is set, then the score counts their progress by excluding their asm file.
nonMatchingFunctions = GetNonMatchingFunctions ( GetFiles ( " src " , " .c " ) ) if args . nonmatching else [ ]
TotalNonMatchingFunctions = len ( GetNonMatchingFunctions ( GetFiles ( " src " , " .c " ) ) )
# Counts how many functions are under a MIPS_TO_C define and GLOBAL_ASM macro respectively.
2023-10-14 13:07:09 -05:00
TotalMipsToCFunctions = len ( CountMipsToCFunctions ( GetFiles ( " src " , " .c " ) ) )
TotalGlobalAsmFunctions = len ( CountGlobalAsmFunctions ( GetFiles ( " src " , " .c " ) ) )
2023-11-05 15:57:14 -05:00
# Counts how many functions are decompiled. To account for the uncompiled functions,
# we do a bit of subtraction from functions with NON_MATCHING and MIPS_TO_C defines
# and GLOBAL_ASM macros without the defines mentioned.
# If --nonmatching is set, then we exclude non matching count subsraction.
2023-10-14 13:07:09 -05:00
TotalCFunctions = len ( GetCFunctions ( GetFilesWhiteList ( " src " , " .c " , gameExclusiveDirs ) ) )
2023-11-05 15:57:14 -05:00
TotalCFunctions - = TotalGlobalAsmFunctions + ( TotalGlobalAsmFunctions - ( TotalNonMatchingFunctions if args . nonmatching else 0 ) - TotalMipsToCFunctions )
2023-10-14 13:07:09 -05:00
2023-11-05 15:57:14 -05:00
# Gets the non matching size for each segment depending of the path set.
def GetNotMatchingSize ( path ) :
2021-11-05 05:17:09 -06:00
size = 0
2023-11-05 15:57:14 -05:00
files = [ ]
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Asm non matching files for main segment.
2021-11-05 05:17:09 -06:00
if ( path == " main " ) :
2023-11-05 15:57:14 -05:00
files = GetFilesBlackList ( " asm/non_matchings " , " .s " , gameExclusiveDirs )
2023-10-14 13:07:09 -05:00
2023-11-05 15:57:14 -05:00
# Asm non matching files for ending segment.
2023-10-14 13:07:09 -05:00
elif ( path == " ending " ) :
2023-11-05 15:57:14 -05:00
files = GetFiles ( " asm/non_matchings/ending " , " .s " )
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Asm non matching files for racing segment.
elif ( path == " racing " ) :
files = GetFiles ( " asm/non_matchings/racing " , " .s " )
# Asm non matching files for libultra segment.
2021-11-05 05:17:09 -06:00
elif ( path == " os " ) :
2023-11-05 15:57:14 -05:00
files = GetFiles ( " asm/non_matchings/os " , " .s " )
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Asm non matching files for libultra segment.
2021-11-05 05:17:09 -06:00
elif ( path == " audio " ) :
2023-11-05 15:57:14 -05:00
files = GetFiles ( " asm/non_matchings/audio " , " .s " )
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Asm non matching files for a path specified, in a general case:
# asm/non_matchings and it's subdirectories (for total segment).
else :
files = GetFiles ( path , " .s " )
# Get the total size for the files specified above.
size = GetAsmSize ( files )
2021-11-05 05:17:09 -06:00
return size
2023-11-05 15:57:14 -05:00
# Gets the assembly size of a file using a text pattern.
def GetAsmSize ( path ) :
2021-11-05 05:17:09 -06:00
size = 0
2023-11-05 15:57:14 -05:00
asmFiles = path
2021-11-05 05:17:09 -06:00
for asmFilePath in asmFiles :
if asmFilePath not in nonMatchingFunctions :
asmLines = ReadAllLines ( asmFilePath )
2023-11-05 15:57:14 -05:00
# Checks each line if it starts with a comment, for context:
# split/splat programs generate assembly files with comments to
# provide additional information of the mips instruction such as:
# /* Hex Location - Address location - Instruction in Hex */
2021-11-05 05:17:09 -06:00
for asmLine in asmLines :
2023-11-05 15:57:14 -05:00
# If there's a comment, assume there's an instruction there
# as well, each mips instruction is 4 bytes long.
2021-11-05 05:17:09 -06:00
if ( asmLine . startswith ( " /* " ) ) :
size + = 4
return size
2023-11-05 15:57:14 -05:00
# Size for all object segments.
total = 0
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Sizes for each object segment.
2023-10-14 13:07:09 -05:00
segMain = 0
segEnding = 0
segRacing = 0
2021-11-05 05:17:09 -06:00
libultra = 0
2023-11-05 15:57:14 -05:00
audio = 0
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# List of objects parsed for all segments (Debug).
obj_list_total = [ ]
# List of objects parsed for each segment (Debug).
obj_list_game = [ ]
obj_list_main = [ ]
obj_list_ending = [ ]
obj_list_racing = [ ]
obj_list_libultra = [ ]
obj_list_audio = [ ]
# Read the linker map file, has correct results with a matching one.
mapFile = ReadAllLines ( " build/us/mk64.us.map " )
# Check for every line in the map file.
2021-11-05 05:17:09 -06:00
for line in mapFile :
lineSplit = list ( filter ( None , line . split ( " " ) ) )
2023-11-05 15:57:14 -05:00
# Check if is a memory segment list by a pattern.
2021-11-05 05:17:09 -06:00
if ( len ( lineSplit ) == 4 and lineSplit [ 0 ] . startswith ( " . " ) ) :
section = lineSplit [ 0 ]
size = int ( lineSplit [ 2 ] , 16 )
objFile = lineSplit [ 3 ]
2023-11-05 15:57:14 -05:00
# Checks for the ".text" line to get the function size of a file and
# store the size for each segment by checking their respective folders
# Ensure we parse files that have an actual size value.
# Also list object files parsed (Debug).
if ( section == " .text " ) and size > 0 :
# This takes the size of every C file in the src directory
# including subdirectories.
2021-11-05 05:17:09 -06:00
if ( objFile . startswith ( " build/us/src " ) ) :
2023-11-05 15:57:14 -05:00
total + = size
if args . debug :
obj_list_total . append ( " File: " + str ( objFile . rstrip ( " \n " ) ) + " - Size: " + str ( size ) )
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# This takes the size of every C file in the src directory
# excluding subdirectories using an additional check.
2023-10-14 13:07:09 -05:00
if ( objFile . startswith ( " build/us/src " ) and objFile . count ( " / " ) == 3 ) :
segMain + = size
2023-11-05 15:57:14 -05:00
if args . debug :
obj_list_main . append ( " File: " + str ( objFile . rstrip ( " \n " ) ) + " - Size: " + str ( size ) )
2023-10-14 13:07:09 -05:00
2023-11-05 15:57:14 -05:00
# Object size for ending segment.
2023-10-14 13:07:09 -05:00
if ( objFile . startswith ( " build/us/src/ending " ) ) :
segEnding + = size
2023-11-05 15:57:14 -05:00
if args . debug :
obj_list_ending . append ( " File: " + str ( objFile . rstrip ( " \n " ) ) + " - Size: " + str ( size ) )
2023-10-14 13:07:09 -05:00
2023-11-05 15:57:14 -05:00
# Object size for racing segment.
2023-10-14 13:07:09 -05:00
if ( objFile . startswith ( " build/us/src/racing " ) ) :
segRacing + = size
2023-11-05 15:57:14 -05:00
if args . debug :
obj_list_racing . append ( " File: " + str ( objFile . rstrip ( " \n " ) ) + " - Size: " + str ( size ) )
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Object size for libultra segment.
2021-11-05 05:17:09 -06:00
if ( objFile . startswith ( " build/us/src/os " ) ) :
libultra + = size
2023-11-05 15:57:14 -05:00
if args . debug :
obj_list_libultra . append ( " File: " + str ( objFile . rstrip ( " \n " ) ) + " - Size: " + str ( size ) )
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Object size for audio segment.
2023-10-14 13:07:09 -05:00
if ( objFile . startswith ( " build/us/src/audio " ) ) :
2021-11-05 05:17:09 -06:00
audio + = size
2023-11-05 15:57:14 -05:00
if args . debug :
obj_list_audio . append ( " File: " + str ( objFile . rstrip ( " \n " ) ) + " - Size: " + str ( size ) )
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# List game object files (Main + Ending + Racing) (Debug).
obj_list_game = obj_list_main + obj_list_ending + obj_list_racing
2023-10-14 13:07:09 -05:00
2023-11-05 15:57:14 -05:00
# Asm size for all segments not decompiled.
nonMatchingTotal = GetNotMatchingSize ( " asm/non_matchings " )
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Asm size for each segment not decompiled.
nonMatchingMain = GetNotMatchingSize ( " main " )
nonMatchingEnding = GetNotMatchingSize ( " ending " )
nonMatchingRacing = GetNotMatchingSize ( " racing " )
nonMatchingLibultra = GetNotMatchingSize ( " os " )
nonMatchingAudio = GetNotMatchingSize ( " audio " )
2021-12-03 04:33:36 -07:00
2023-11-05 15:57:14 -05:00
# Set total size for each segment.
seg_main_size = segMain # 744112
seg_ending_size = segEnding # 20032
seg_racing_size = segRacing # 174224
libultra_size = libultra # 48848
audio_size = audio # 86912
# Set total size for game segment (Main + Ending + Racing).
game = segMain + segEnding + segRacing # 938368
game_code_size = seg_main_size + seg_ending_size + seg_racing_size # 938368
# Set total size for all segments.
total_code_size = game_code_size + libultra_size + audio_size # 1074128
# Set progress size depending of the size of non matching files for each segments.
2023-10-14 13:07:09 -05:00
segMain - = nonMatchingMain
segEnding - = nonMatchingEnding
segRacing - = nonMatchingRacing
2021-11-05 05:17:09 -06:00
libultra - = nonMatchingLibultra
2023-11-05 15:57:14 -05:00
audio - = nonMatchingAudio
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Set progress size for game segment (Main + Ending + Racing).
2023-10-14 13:07:09 -05:00
game - = nonMatchingMain + nonMatchingEnding + nonMatchingRacing
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Set progress size for all segments.
total - = nonMatchingTotal
2023-10-14 13:07:09 -05:00
2023-11-05 15:57:14 -05:00
# Set remaining size substacting total size with progress size.
decompilable = total
2023-10-14 13:07:09 -05:00
remaining_size = total_code_size - decompilable
2023-11-05 15:57:14 -05:00
# Set percentage progress for each segment.
2023-10-14 13:07:09 -05:00
segMainPct = 100 * segMain / seg_main_size
segEndingPct = 100 * segEnding / seg_ending_size
segRacingPct = 100 * segRacing / seg_racing_size
2021-11-05 05:17:09 -06:00
libultraPct = 100 * libultra / libultra_size
audioPct = 100 * audio / audio_size
2023-11-05 15:57:14 -05:00
# Set percentage progress for game segment (Main + Ending + Racing).
2023-10-14 13:07:09 -05:00
gamePct = 100 * game / game_code_size
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Set percentage progress for all segments.
totalPct = 100 * total / total_code_size
# Gets a string from a table index position.
def get_string_from_table ( idx , table ) :
if 0 < = idx < len ( table ) :
return table [ idx ]
2023-10-14 13:07:09 -05:00
else :
return " Number out of range "
2021-12-03 04:33:36 -07:00
2023-11-05 15:57:14 -05:00
# Finds the closest divisible of a number to get a result without decimals.
2023-10-14 13:07:09 -05:00
def find_closest_divisible ( number , divisor ) :
closest_smaller = number - ( number % divisor )
closest_larger = closest_smaller + divisor
2021-12-03 04:33:36 -07:00
2023-10-14 13:07:09 -05:00
if abs ( number - closest_smaller ) < abs ( number - closest_larger ) :
return closest_smaller
else :
return closest_larger
2023-11-05 15:57:14 -05:00
# Centers a text around a specified filled character and how long it should be.
2023-10-14 13:07:09 -05:00
def center_text ( text , total_width , fill_character = " " ) :
empty_spaces = total_width - len ( text )
left_padding = empty_spaces / / 2
right_padding = empty_spaces - left_padding
centered_text = fill_character * left_padding + text + fill_character * right_padding
return centered_text
2023-11-05 15:57:14 -05:00
# Moves a base character around a filled character depending of the position set.
# Simulates a lap line progress like in the original game.
2023-10-14 13:07:09 -05:00
def move_character_from_bar ( position , total_length , charbase = " 0 " , charfill = " 1 " ) :
if position < 0 :
position = 0
elif position > total_length :
position = total_length
line = charfill * total_length
line = list ( line )
line [ position ] = charbase
return " " . join ( line )
2023-11-05 15:57:14 -05:00
# Checks the progress of a condition depending of where the total is.
# Tracks the progress which cup are we using a table set by a condition.
2023-10-14 13:07:09 -05:00
def check_table_cond ( cond , total , table ) :
if total > cond :
sym = " (V) "
elif total == cond :
sym = " (~) "
else :
sym = " (X) "
return str ( table [ cond ] ) + str ( sym )
2023-11-05 15:57:14 -05:00
# Lists detailed progress for each badge set and the object files parsed on it (Debug).
def list_detailed_progress_and_files ( pct , prog , size , objlist , charseg ) :
print ( " Percentage progress: " + str ( pct ) + " % " )
print ( " Size progress: " + str ( prog ) )
print ( " Size total: " + str ( size ) )
print ( str ( charseg ) + " object files: \n " + " \n " . join ( objlist ) )
# All the cups in the game in order.
2023-10-14 13:07:09 -05:00
mkCups = [
" Mushroom Cup " ,
" Flower Cup " ,
" Star Cup " ,
" Special Cup " ,
]
2023-11-05 15:57:14 -05:00
# All the courses in the game in order.
2023-10-14 13:07:09 -05:00
mkCourses = [
" Luigi Raceway " ,
" Moo Moo Farm " ,
" Koopa Troopa Beach " ,
" Kalimari Desert " ,
" Toad ' s Turnpike " ,
" Frappe Snowland " ,
" Choco Mountain " ,
" Mario Raceway " ,
" Wario Stadium " ,
" Sherbet Land " ,
" Royal Raceway " ,
" Bowser ' s Castle " ,
" D.K. ' s Jungle Parkway " ,
" Yoshi Valley " ,
" Banshee Boardwalk " ,
" Rainbow Road " ,
]
2023-11-05 15:57:14 -05:00
# Shows total segment progress.
2023-10-14 13:07:09 -05:00
if args . format == ' totalBadge ' :
2023-11-05 15:57:14 -05:00
if not args . debug :
print ( str ( round ( totalPct , 2 ) ) + " % " )
else :
list_detailed_progress_and_files ( totalPct , total , total_code_size , obj_list_total , " Total " )
# Shows game segment progress (Main + Ending + Racing).
2021-12-18 03:30:41 -07:00
elif args . format == ' gameBadge ' :
2023-11-05 15:57:14 -05:00
if not args . debug :
print ( str ( round ( gamePct , 2 ) ) + " % " )
else :
list_detailed_progress_and_files ( gamePct , game , game_code_size , obj_list_game , " Game " )
# Shows main segment progress.
2023-10-14 13:07:09 -05:00
elif args . format == ' mainBadge ' :
2023-11-05 15:57:14 -05:00
if not args . debug :
print ( str ( round ( segMainPct , 2 ) ) + " % " )
else :
list_detailed_progress_and_files ( segMainPct , segMain , seg_main_size , obj_list_main , " Main " )
# Shows ending segment progress.
2023-10-14 13:07:09 -05:00
elif args . format == ' endingBadge ' :
2023-11-05 15:57:14 -05:00
if not args . debug :
print ( str ( round ( segEndingPct , 2 ) ) + " % " )
else :
list_detailed_progress_and_files ( segEndingPct , segEnding , seg_ending_size , obj_list_ending , " Ending " )
# Shows racing segment progress.
2023-10-14 13:07:09 -05:00
elif args . format == ' racingBadge ' :
2023-11-05 15:57:14 -05:00
if not args . debug :
print ( str ( round ( segRacingPct , 2 ) ) + " % " )
else :
list_detailed_progress_and_files ( segRacingPct , segRacing , seg_racing_size , obj_list_racing , " Racing " )
# Shows libultra segment progress.
2023-10-14 13:07:09 -05:00
elif args . format == ' osBadge ' :
2023-11-05 15:57:14 -05:00
if not args . debug :
print ( str ( round ( libultraPct , 2 ) ) + " % " )
else :
list_detailed_progress_and_files ( libultraPct , libultra , libultra_size , obj_list_libultra , " Libultra " )
# Shows audio segment progress.
elif args . format == ' audioBadge ' :
if not args . debug :
print ( str ( round ( audioPct , 2 ) ) + " % " )
else :
list_detailed_progress_and_files ( audioPct , audio , audio_size , obj_list_audio , " Audio " )
# Shows current bytes left to decompile out of the total.
2021-12-18 03:30:41 -07:00
elif args . format == ' bytesToDecompile ' :
2023-10-14 13:07:09 -05:00
print ( str ( remaining_size ) + " of " + str ( total_code_size ) + " \n " )
2023-11-05 15:57:14 -05:00
# Shows how many GLOBAL_ASM functions are left.
2023-10-14 13:07:09 -05:00
elif args . format == ' globalAsmFuncs ' :
print ( str ( TotalGlobalAsmFunctions ) )
2023-11-05 15:57:14 -05:00
# Shows how many MIPS_TO_C functions are left.
2021-12-18 03:30:41 -07:00
elif args . format == ' m2cFuncs ' :
print ( str ( TotalMipsToCFunctions ) )
2023-11-05 15:57:14 -05:00
# Shows how many NON_MATCHING functions are left.
2021-12-18 03:30:41 -07:00
elif args . format == ' nonmatchingFuncs ' :
print ( str ( TotalNonMatchingFunctions ) )
2023-11-05 15:57:14 -05:00
# Shows decompilation progress output in a fancy way.
2021-12-03 04:33:36 -07:00
elif args . format == ' text ' :
2023-11-05 15:57:14 -05:00
outputLength = 67 # Horizontal length of the text in the terminal output
# "Magic" number is 48, which is 3 laps * 4 courses * 3 cups
bytesPerTotalLaps = total_code_size / / 47 # Total size // (Magic number - 1)
srcDivNear = find_closest_divisible ( total , 49 ) # Correct division by diving closest divisible with (Magic number + 1)
lapTotalCounts = int ( srcDivNear / bytesPerTotalLaps ) # Game progress count, sets where are we in a simulated game, can be between 0 - 47
curLapProgress = int ( ( ( srcDivNear % bytesPerTotalLaps ) * ( outputLength - 1 ) ) / ( bytesPerTotalLaps ) ) # Progress of a lap depending of the output length
curLapCount = int ( ( lapTotalCounts % 3 ) + 1 ) # Lap count, can be between 1 - 3 (3 laps total)
curCourseCount = int ( lapTotalCounts / 3 ) # Course count, can be between 0 - 15 (16 courses total)
curCupCount = int ( ( lapTotalCounts / 12 ) % 12 ) # Cup count, can be between 0 - 3 (4 cups total)
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Print current decompilation progress.
print ( str ( center_text ( ( str ( " Non Matching progress mode " ) if args . nonmatching else str ( " " ) ) , outputLength , " = " ) ) )
print ( str ( center_text ( " All Cups (Decompilation) " , outputLength ) ) )
print ( str ( center_text ( " " + str ( round ( totalPct , 2 ) ) + " % Complete " , outputLength , " - " ) ) )
print ( str ( center_text ( " # Decompiled functions: " + str ( TotalCFunctions ) + " " , outputLength ) ) )
print ( str ( center_text ( " # GLOBAL_ASM remaining: " + str ( TotalGlobalAsmFunctions ) + " " , outputLength ) ) )
print ( str ( center_text ( " # NON_MATCHING remaining: " + str ( TotalNonMatchingFunctions ) + " " , outputLength ) ) )
print ( str ( center_text ( " # MIPS_TO_C remaining: " + str ( TotalMipsToCFunctions ) + " " , outputLength ) ) )
print ( str ( center_text ( " Game Status " , outputLength , " - " ) ) )
2021-11-05 05:17:09 -06:00
2023-11-05 15:57:14 -05:00
# Simlautes an All Cups race, prints how much the player has been progressing.
2023-10-14 13:07:09 -05:00
if TotalGlobalAsmFunctions > 0 :
2023-11-05 15:57:14 -05:00
print ( str ( center_text ( check_table_cond ( 0 , curCupCount , mkCups ) + " - " + check_table_cond ( 1 , curCupCount , mkCups ) , outputLength ) ) )
print ( str ( center_text ( check_table_cond ( 2 , curCupCount , mkCups ) + " - " + check_table_cond ( 3 , curCupCount , mkCups ) , outputLength ) ) )
print ( str ( center_text ( " Lap Progress Bar and Race Status " , outputLength , " - " ) ) )
print ( str ( move_character_from_bar ( curLapProgress , outputLength , " O " , " - " ) ) )
print ( str ( center_text ( " We are in " + str ( get_string_from_table ( curCupCount , mkCups ) ) + " racing at " + str ( get_string_from_table ( curCourseCount , mkCourses ) ) + " (Lap " + str ( curLapCount ) + " /3) " , outputLength ) ) )
2021-11-05 05:17:09 -06:00
else :
2023-11-05 15:57:14 -05:00
print ( str ( center_text ( " Mushroom Cup (V) - Flower Cup (V) " , outputLength ) ) )
print ( str ( center_text ( " Star Cup (V) - Special Cup (V) " , outputLength ) ) )
print ( str ( center_text ( " We finished All Cups! We got all 4 Gold Cups! " , outputLength ) ) )
2023-10-14 13:07:09 -05:00
2023-11-05 15:57:14 -05:00
print ( str ( center_text ( ( str ( " Non Matching progress mode " ) if args . nonmatching else str ( " " ) ) , outputLength , " = " ) ) )
# Shows decompilation progress output in verbose mode.
2023-10-14 13:07:09 -05:00
elif args . format == ' verbose ' :
2023-11-05 15:57:14 -05:00
adjective = " decompiled " if args . nonmatching else " matched "
print ( " Total decompilable bytes remaining: " + str ( remaining_size ) + " out of " + str ( total_code_size ) + " \n " )
print ( str ( total ) + " / " + str ( total_code_size ) + " bytes " + adjective + " in total code " + str ( totalPct ) + " % " )
print ( str ( game ) + " / " + str ( game_code_size ) + " bytes " + adjective + " in game code " + str ( gamePct ) + " % \n " )
print ( str ( TotalCFunctions ) + " " + adjective + " functions - " + str ( TotalGlobalAsmFunctions ) + " GLOBAL_ASM functions remaining. " + " " )
2023-10-14 13:07:09 -05:00
print ( str ( TotalNonMatchingFunctions ) + " NON_MATCHING functions - " + str ( TotalMipsToCFunctions ) + " MIPS_TO_C functions. " + " \n " )
2023-11-05 15:57:14 -05:00
print ( str ( segMain ) + " / " + str ( seg_main_size ) + " bytes " + adjective + " in segMain " + str ( segMainPct ) + " % " )
print ( str ( segEnding ) + " / " + str ( seg_ending_size ) + " bytes " + adjective + " in segEnding " + str ( segEndingPct ) + " % " )
print ( str ( segRacing ) + " / " + str ( seg_racing_size ) + " bytes " + adjective + " in segRacing " + str ( segRacingPct ) + " % " )
print ( str ( audio ) + " / " + str ( audio_size ) + " bytes " + adjective + " in audio " + str ( audioPct ) + " % " )
print ( str ( libultra ) + " / " + str ( libultra_size ) + " bytes " + adjective + " in libultra " + str ( libultraPct ) + " % \n " )
2024-04-13 02:54:59 +02:00
elif args . format == ' badge ' :
adjective = " decompiled " if args . nonmatching else " matched "
badge_size_left = badge ( left_text = " Size left " , right_text = str ( remaining_size ) , right_color = " blue " )
with open ( " docs/html/size_left.svg " , " w " ) as f :
f . write ( badge_size_left )
badge_total_size = badge ( left_text = " Total size " , right_text = str ( total_code_size ) , right_color = " blue " )
with open ( " docs/html/total_size.svg " , " w " ) as f :
f . write ( badge_total_size )
badge_total_pct = badge ( left_text = " Total progress " , right_text = str ( round ( totalPct , 2 ) ) + " % " , right_color = " green " )
with open ( " docs/html/total_progress.svg " , " w " ) as f :
f . write ( badge_total_pct )
badge_game_pct = badge ( left_text = " Game progress " , right_text = str ( round ( gamePct , 2 ) ) + " % " , right_color = " green " )
with open ( " docs/html/game_progress.svg " , " w " ) as f :
f . write ( badge_game_pct )
2024-04-12 19:16:13 -06:00
badge_asm_funcs = badge ( left_text = " WIP functions " , right_text = str ( TotalGlobalAsmFunctions ) , right_color = " blue " )
2024-04-13 02:54:59 +02:00
with open ( " docs/html/asm_funcs.svg " , " w " ) as f :
f . write ( badge_asm_funcs )
badge_nonmatching_funcs = badge ( left_text = " NON_MATCHING functions " , right_text = str ( TotalNonMatchingFunctions ) , right_color = " blue " )
with open ( " docs/html/nonmatching_funcs.svg " , " w " ) as f :
f . write ( badge_nonmatching_funcs )
badge_m2c_funcs = badge ( left_text = " MIPS_TO_C functions " , right_text = str ( TotalMipsToCFunctions ) , right_color = " blue " )
with open ( " docs/html/m2c_funcs.svg " , " w " ) as f :
f . write ( badge_m2c_funcs )
badge_seg_main = badge ( left_text = " Main progress " , right_text = str ( round ( segMainPct , 2 ) ) + " % " , right_color = " green " )
with open ( " docs/html/seg_main_progress.svg " , " w " ) as f :
f . write ( badge_seg_main )
badge_seg_ending = badge ( left_text = " Ending progress " , right_text = str ( round ( segEndingPct , 2 ) ) + " % " , right_color = " green " )
with open ( " docs/html/seg_ending_progress.svg " , " w " ) as f :
f . write ( badge_seg_ending )
badge_seg_racing = badge ( left_text = " Racing progress " , right_text = str ( round ( segRacingPct , 2 ) ) + " % " , right_color = " green " )
with open ( " docs/html/seg_racing_progress.svg " , " w " ) as f :
f . write ( badge_seg_racing )
badge_audio = badge ( left_text = " Audio progress " , right_text = str ( round ( audioPct , 2 ) ) + " % " , right_color = " green " )
with open ( " docs/html/audio_progress.svg " , " w " ) as f :
f . write ( badge_audio )
badge_libultra = badge ( left_text = " Libultra progress " , right_text = str ( round ( libultraPct , 2 ) ) + " % " , right_color = " green " )
with open ( " docs/html/libultra_progress.svg " , " w " ) as f :
f . write ( badge_libultra )
2021-11-05 05:17:09 -06:00
else :
print ( " Unknown format argument: " + args . format )