Skip to content

Instantly share code, notes, and snippets.

@duncathan
Last active September 4, 2021 05:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save duncathan/8a8e617b4560d192620211f41de9ea95 to your computer and use it in GitHub Desktop.
Save duncathan/8a8e617b4560d192620211f41de9ea95 to your computer and use it in GitHub Desktop.
TSC utility scripts
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