Last active
September 4, 2021 05:37
-
-
Save duncathan/8a8e617b4560d192620211f41de9ea95 to your computer and use it in GitHub Desktop.
TSC utility scripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import math | |
# converts a tsc value (e.g. "1234" or "000/") to a number (1234 or -1) | |
# can handle input values of any length, for niche use cases such as custom TSC commands | |
def tsc_value_to_num(a, input_length=4): | |
b = 0 | |
for i in range(input_length): | |
b += (ord(a[i]) - ord('0')) * 10**((input_length-1)-i) | |
return b | |
# converts a number (e.g. 1234 or -1) to a tsc value ("1234" or "000/") | |
# generates an "ideal" value for a given number according to the smallest and largest permissible ASCII values | |
# where possible, generates a number using only a single out of bounds character which can be typed on a normal keyboard | |
# can generate output values of any length, for niche use cases such as custom TSC commands | |
def num_to_tsc_value(a, output_length=4, min_char=' ', max_char='~'): | |
# within standard bounds | |
if a >= 0 and a <= 10**(output_length-1): | |
return str(a).zfill(output_length) | |
# out of bounds given the provided min_char and max_char | |
if a < tsc_value_to_num(min_char*output_length, output_length) or a > tsc_value_to_num(max_char*output_length, output_length): | |
return None | |
# magnitude is greater than can be represented with a single out of bounds character | |
if a < tsc_value_to_num(' '+"0"*(output_length-1), output_length) or a > tsc_value_to_num('~'+"9"*(output_length-1), output_length): | |
return multi_char_value(a, output_length, min_char, max_char) | |
# within range for a value with a single out of bounds character | |
return single_char_value(a, output_length-1) | |
# generates a tsc value from a given number | |
# character usage limited only by the arguments passed to it | |
# adapted from code by @Brayconn | |
def multi_char_value(a, output_length, min_char, max_char): | |
b = "" | |
for i in range(output_length): | |
dec_place = 10**((output_length-1)-i) | |
char = max(ord(min_char)-ord('0'), min(int(a/dec_place), ord(max_char)-ord('0'))) | |
a -= dec_place * char | |
b += chr(char+ord('0')) | |
return b | |
# recursively generates a tsc value from a given number, using only a single out of bounds character | |
# limited to characters between ' ' and '~', inclusive | |
def single_char_value(a, dec_place): | |
digit = int(a/(10**dec_place)) | |
remainder = a % (10**dec_place) | |
if remainder and digit < 0: | |
digit -= 1 | |
out = chr(ord('0') + digit) | |
if dec_place == 0: | |
return out | |
if digit != 0: | |
return out + str(remainder).zfill(dec_place) | |
return out + single_char_value(a, dec_place-1) | |
# converts a flag string to its memory address and specific bit | |
def flag_string_to_address(flag, base=0x49DDA0): | |
return flag_num_to_address(tsc_value_to_num(flag), base) | |
# converts a flag number to its memory address and specific bit | |
def flag_num_to_address(num, base=0x49DDA0): | |
return hex(int(num / 8) + base) + ", bit " + str(num % 8) | |
# converts a memory address to a list of flags or optionally a list of <FL-/<FL+ commands to set a given value | |
# based on code by @thomas-xin for Miza | |
def address_to_flag(address, value=None, bits=32, base=0x49DDA0, min_char=chr(0), max_char=chr(255)): | |
if value >= 1<<(bits): | |
return "Value too large for provided size" | |
num = address - base | |
flags = [] | |
for i in range(bits): | |
f = num_to_tsc_value(num+i, 4, min_char, max_char) | |
if f is None: | |
return "Flag out of accessible range" | |
if value is None: | |
flags.append(f+", ") | |
else: | |
command = "<FL+" if value&(1<<i) != 0 else "<FL-" | |
flags.append(command+f) | |
return "".join(flags) | |
# generates a TSC script which decodes the value in a given set of flags and calls a different event for each possible value | |
# this is mostly useful for rando stuff but is completely applicable to other mods if for some reason <VAR can't be used | |
def codec(event_no, flags, max_val=-1, credit=False, behavior=(lambda val : "BEHAVIOR " + str(val))): | |
highest_possible = 2**len(flags) | |
if max_val == -1: | |
max_val = highest_possible | |
max_val = min(max_val, highest_possible) | |
label = "l" if credit else "#" | |
conditional = "f" if credit else "<FLJ" | |
script = "" | |
for val in range(max_val): | |
eve = num_to_tsc_value(tsc_value_to_num(event_no)+val) | |
script += label + eve + "\n" | |
first_flag_to_check = val.bit_length() | |
if first_flag_to_check < len(flags): | |
flagjumps = "" | |
for i in range(first_flag_to_check, len(flags)): | |
num = (val|(1<<i)) | |
if num >= max_val: | |
continue | |
flagjumps += conditional + flags[i] + ":" + num_to_tsc_value(tsc_value_to_num(event_no)+num) | |
if len(flagjumps) > 0: | |
script += flagjumps + "\n" | |
script += behavior(val) + "\n" | |
return script | |
# generates a TSC script with events in a certain range of values and different behavior per-event | |
# used in tandem with codec if the behavior for the codec needs to be in a different place for whatever reason | |
def decode(base_event_no, values, behavior=(lambda val : "BEHAVIOR " + str(val))): | |
s = "" | |
base = tsc_value_to_num(base_event_no) | |
for i in values: | |
s += "#" + num_to_tsc_value(base + i) + "\n" | |
s += behavior(i) + "\n" | |
return s |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment