Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /******************************************************************************/
- /******************************************************************************/
- //"kw32g\asset_scripts\convert_all_assets.py":
- #(DOES NOT CONVERT SCENE DATA!)
- #(ALSO DOESN'T TOUCH MUSIC, AS THAT DOESN'T NEED CONVERTING!)
- #this program relies on its current working directory being exact,
- #which is not guaranteed if __name__ == '__main__'
- if __name__ != '__main__': exit(0)
- from time import time
- from os import getcwd, system as cmd
- from os.path import abspath, join
- timeStart = time()
- from _ambience import convert_all_ambience
- from _background import convert_all_background
- from _img import convert_all_img
- from _objimg import convert_all_objimg
- from _sfx import convert_all_sfx
- from _tileset import convert_all_tileset
- root = abspath(getcwd())
- convert_all_ambience.main( join(root, '_ambience'))
- convert_all_background.main( join(root, '_background'))
- convert_all_img.main( join(root, '_img'))
- convert_all_objimg.main( join(root, '_objimg'))
- convert_all_sfx.main( join(root, '_sfx'))
- convert_all_tileset.main( join(root, '_tileset'))
- print(f'ALL ASSETS CONVERTED IN {time()-timeStart} SECONDS')
- cmd('powershell -command pause')/******************************************************************************/
- /******************************************************************************/
- //"kw32g\asset_scripts\convert_all_background.py":
- _PRINT = True
- src_folder = '.\\_png\\'
- dst_folder = '.\\_qoi\\'
- from time import time
- from os import system as cmd, walk, path, rename, chdir
- import subprocess
- def tryInt(string):
- try:
- return int(string)
- except ValueError:
- return None
- except TypeError:
- return None
- def run_program(args, fatal=False):
- if type(args) != list:
- print('args must be of type list')
- exit(-1)
- if _PRINT: print((' ').join(args))
- returnCode = subprocess.call(args, shell=True)
- if returnCode >= 2**31: returnCode -= 2**32
- if fatal:
- if returnCode != 0:
- if _PRINT: print(f'failed to execute "{args}"')
- exit(returnCode)
- return True
- else:
- return returnCode == 0
- def tryCmd(s, ps=True):
- if _PRINT: print(s)
- if ps: cmd("powershell -command "+s)
- else : cmd(s)
- def abspath(local_folder, file_name):
- return path.abspath(path.join(local_folder,file_name))
- def main(cwd = None):
- if cwd != None: chdir(cwd)
- timeStart = time()
- tryCmd(f'rm {dst_folder}*.*')
- tryCmd(f'XCOPY /E /Y {src_folder} {dst_folder}')
- #assumes there are no folders in .\_qoi\
- files = []
- for root, dirs, _files in walk(dst_folder):
- files += _files
- args = ['Super.QOI.converter.Console.exe']
- for file in files:
- if file.split('.')[-1] != 'qoi':
- args.append(abspath(dst_folder, file))
- #this qoi converter seems to be quite buggy, and it
- #refuses to edit existing files without throwing an exception,
- #so i need to tell it to not touch anything that already exists
- args.append('-nc') #don't copy attr&dates; skip confirmation
- args.append('-nd') #don't del originals; skip confirmation
- args.append('-no') #don't overwrite output; skip confirmation
- run_program(args)
- tryCmd(f'rm {dst_folder}*.png')
- print(f'BACKGROUND CONVERTED IN {time()-timeStart} SECONDS')
- if __name__ == '__main__':
- tryCmd("pause")
- exit(0)
- if __name__ == '__main__': main()/******************************************************************************/
- /******************************************************************************/
- //"kw32g\asset_scripts\convert_all_img.py":
- _PRINT = True
- src_folder = '.\\_png\\'
- dst_folder = '.\\_qoi\\'
- from time import time
- from os import system as cmd, walk, path, chdir
- import subprocess
- def tryInt(string):
- try:
- return int(string)
- except ValueError:
- return None
- except TypeError:
- return None
- def run_program(args, fatal=False):
- if type(args) != list:
- print('args must be of type list')
- exit(-1)
- if _PRINT: print((' ').join(args))
- returnCode = subprocess.call(args, shell=True)
- if returnCode >= 2**31: returnCode -= 2**32
- if fatal:
- if returnCode != 0:
- if _PRINT: print(f'failed to execute "{args}"')
- exit(-1)
- return True
- else:
- return returnCode == 0
- def tryCmd(s, ps=True):
- if _PRINT: print(s)
- if ps: cmd("powershell -command "+s)
- else : cmd(s)
- def abspath(local_folder, file_name):
- return path.abspath(path.join(local_folder,file_name))
- def main(cwd = None):
- if cwd != None: chdir(cwd)
- timeStart = time()
- tryCmd(f'rm {dst_folder}*.*')
- tryCmd(f'XCOPY /E /Y {src_folder} {dst_folder}')
- #assumes there are no folders in .\_qoi\
- files = []
- for root, dirs, _files in walk(dst_folder):
- files += _files
- args = ['Super.QOI.converter.Console.exe']
- for file in files:
- if file.split('.')[-1] != 'qoi':
- args.append(abspath(dst_folder, file))
- #this qoi converter seems to be quite buggy, and it
- #refuses to edit existing files without throwing an exception,
- #so i need to tell it to not touch anything that already exists
- args.append('-nc') #don't copy attr&dates; skip confirmation
- args.append('-nd') #don't del originals; skip confirmation
- args.append('-no') #don't overwrite output; skip confirmation
- run_program(args)
- tryCmd(f'rm {dst_folder}*.png')
- print(f'IMG CONVERTED IN {time()-timeStart} SECONDS')
- if __name__ == '__main__':
- tryCmd("pause")
- exit(0)
- if __name__ == '__main__': main()/******************************************************************************/
- /******************************************************************************/
- //"kw32g\asset_scripts\convert_all_objimg.py":
- _PRINT = True
- src_folder = '.\\_png\\'
- dst_folder = '.\\_qoi\\'
- from time import time
- from os import system as cmd, walk, path, rename, chdir
- import subprocess
- def tryInt(string):
- try:
- return int(string)
- except ValueError:
- return None
- except TypeError:
- return None
- def run_program(args, fatal=False):
- if type(args) != list:
- print('args must be of type list')
- exit(-1)
- if _PRINT: print((' ').join(args))
- returnCode = subprocess.call(args, shell=True)
- if returnCode >= 2**31: returnCode -= 2**32
- if fatal:
- if returnCode != 0:
- if _PRINT: print(f'failed to execute "{args}"')
- exit(returnCode)
- return True
- else:
- return returnCode == 0
- def tryCmd(s, ps=True):
- if _PRINT: print(s)
- if ps: cmd("powershell -command "+s)
- else : cmd(s)
- def abspath(local_folder, file_name):
- return path.abspath(path.join(local_folder,file_name))
- def main(cwd = None):
- if cwd != None: chdir(cwd)
- timeStart = time()
- tryCmd(f'rm {dst_folder}*.*')
- tryCmd(f'XCOPY /E /Y {src_folder} {dst_folder}')
- #assumes there are no folders in .\_qoi\
- files = []
- for root, dirs, _files in walk(dst_folder):
- files += _files
- args = ['Super.QOI.converter.Console.exe']
- for file in files:
- if file.split('.')[-1] != 'qoi':
- args.append(abspath(dst_folder, file))
- #this qoi converter seems to be quite buggy, and it
- #refuses to edit existing files without throwing an exception,
- #so i need to tell it to not touch anything that already exists
- args.append('-nc') #don't copy attr&dates; skip confirmation
- args.append('-nd') #don't del originals; skip confirmation
- args.append('-no') #don't overwrite output; skip confirmation
- run_program(args)
- tryCmd(f'rm {dst_folder}*.png')
- #convert names to "obj-img_<id>.qoi"
- for root, dirs, files in walk(dst_folder):
- for file in files:
- file_src = path.join(root, file)
- objimg_id = tryInt(file.split('_')[0])
- if objimg_id == None:
- print(f'failed to get obj-img id from "{file_src}"')
- exit(-1)
- file_dst = path.join(root, f'obj-img_{objimg_id}.qoi')
- rename(file_src, file_dst)
- print(f'OBJ-IMG CONVERTED IN {time()-timeStart} SECONDS')
- if __name__ == '__main__':
- tryCmd("pause")
- exit(0)
- if __name__ == '__main__': main()/******************************************************************************/
- /******************************************************************************/
- //"kw32g\asset_scripts\convert_all_sfx.py":
- _PRINT = True
- src_folder = '.\\_wav\\'
- dst_folder = '.\\_qoa\\'
- tst_folder = '.\\_test\\'
- def_prefix = '_opt_'
- from time import time
- from os import system as cmd, walk, path, chdir
- import subprocess
- def tryInt(string):
- try:
- return int(string)
- except ValueError:
- return None
- except TypeError:
- return None
- def run_program(args, fatal=False):
- if type(args) != list:
- print('args must be of type list')
- exit(-1)
- if _PRINT: print((' ').join(args))
- returnCode = subprocess.call(args, shell=True)
- if returnCode >= 2**31: returnCode -= 2**32
- if fatal:
- if returnCode != 0:
- if _PRINT: print(f'failed to execute "{args}"')
- exit(-1)
- return True
- else:
- return returnCode == 0
- def tryCmd(s, ps=True):
- if _PRINT: print(s)
- if ps: cmd("powershell -command "+s)
- else : cmd(s)
- def convertPath(path, prefix = def_prefix):
- name = path.split(prefix)[-1]
- #assumes filename actually has an extension
- return dst_folder + ('.').join(name.split('.')[:-1]) + '.qoa'
- def main(cwd = None):
- if cwd != None: chdir(cwd)
- timeStart = time()
- tryCmd(f'rm {dst_folder}*.*')
- #assumes there are no folders in .\_wav\
- files = []
- for root, dirs, _files in walk(src_folder):
- files += _files
- for file in files:
- file_src = path.join(src_folder,file)
- file_dst = convertPath(file)
- file_tst = path.join(tst_folder,file)
- #first, convert all wavs to qoa
- run_program(['qoa_convert.exe', file_src, file_dst])
- #then, do the reverse to check its output for
- #any artifacts or 'rip headphone users' moments
- run_program(['qoa_convert.exe', file_dst, file_tst])
- print(f'SFX CONVERTED IN {time()-timeStart} SECONDS')
- if __name__ == '__main__':
- tryCmd('pause')
- exit(0)
- if __name__ == '__main__': main()/******************************************************************************/
- /******************************************************************************/
- //"kw32g\asset_scripts\convert_all_tileset.py":
- _PRINT = True
- src_folder = '.\\_png\\'
- dst_folder = '.\\_qoi\\'
- from time import time
- from os import system as cmd, walk, path, rename, chdir
- import subprocess
- def tryInt(string):
- try:
- return int(string)
- except ValueError:
- return None
- except TypeError:
- return None
- def run_program(args, fatal=False):
- if type(args) != list:
- print('args must be of type list')
- exit(-1)
- if _PRINT: print((' ').join(args))
- returnCode = subprocess.call(args, shell=True)
- if returnCode >= 2**31: returnCode -= 2**32
- if fatal:
- if returnCode != 0:
- if _PRINT: print(f'failed to execute "{args}"')
- exit(returnCode)
- return True
- else:
- return returnCode == 0
- def tryCmd(s, ps=True):
- if _PRINT: print(s)
- if ps: cmd("powershell -command "+s)
- else : cmd(s)
- def abspath(local_folder, file_name):
- return path.abspath(path.join(local_folder,file_name))
- def main(cwd = None):
- if cwd != None: chdir(cwd)
- timeStart = time()
- tryCmd(f'rm {dst_folder}*.*')
- tryCmd(f'XCOPY /E /Y {src_folder} {dst_folder}')
- #assumes there are no folders in .\_qoi\
- files = []
- for root, dirs, _files in walk(dst_folder):
- files += _files
- args = ['Super.QOI.converter.Console.exe']
- for file in files:
- if file.split('.')[-1] != 'qoi':
- args.append(abspath(dst_folder, file))
- #this qoi converter seems to be quite buggy, and it
- #refuses to edit existing files without throwing an exception,
- #so i need to tell it to not touch anything that already exists
- args.append('-nc') #don't copy attr&dates; skip confirmation
- args.append('-nd') #don't del originals; skip confirmation
- args.append('-no') #don't overwrite output; skip confirmation
- run_program(args)
- tryCmd(f'rm {dst_folder}*.png')
- tryCmd(f'rm {dst_folder}_tileset_collision.qoi')
- print(f'TILESET CONVERTED IN {time()-timeStart} SECONDS')
- if __name__ == '__main__':
- tryCmd("pause")
- exit(0)
- if __name__ == '__main__': main()/******************************************************************************/
- /******************************************************************************/
- //"kw32g\asset_scripts\copy_all_assets.py":
- _PRINT = True
- project_folder = '\\kw32g'
- from time import time
- from os import chdir, system as cmd
- from os.path import exists
- def tryCmd(s, ps=True):
- if _PRINT:
- print(s)
- if ps: cmd('powershell -command '+s)
- else : cmd(s)
- else:
- if ps: cmd(f'powershell -command {s} >nul')
- else : cmd(s+' >nul')
- import _generate_arrlen
- def main(cwd = None):
- #if not None, cwd should be the root asset folder
- if cwd != None: chdir(cwd)
- timeStart = time()
- #and the game's root folder should be on
- #the same level as the assets folder
- game_bin = f'..{project_folder}\\bin'
- deb_dat = game_bin+'\\_Debug\\dat'
- rel_dat = game_bin+'\\_Release\\dat'
- dat_amb = '\\ambience'
- dat_bgn = '\\background'
- dat_img = '\\img'
- dat_mus = '\\music'
- dat_obj = '\\obj-img'
- dat_scn = '\\scene'
- dat_sfx = '\\sfx'
- dat_tls = '\\tileset'
- #delete save to account for any potential
- #new differences in binary structure
- #(otherwise it'll fail to read properly!)
- tryCmd(f'rm {game_bin}\\_Debug\\save\\*.*')
- tryCmd(f'rm {game_bin}\\_Release\\save\\*.*')
- deb_sys = f'{game_bin}\\_Debug\\dat\\system_states.bin'
- rel_sys = f'{game_bin}\\_Release\\dat\\system_states.bin'
- if exists(deb_sys): tryCmd(f'rm {deb_sys}')
- if exists(rel_sys): tryCmd(f'rm {rel_sys}')
- tryCmd(f'rm {deb_dat}{dat_amb}\\*.*')
- tryCmd(f'rm {deb_dat}{dat_bgn}\\*.*')
- tryCmd(f'rm {deb_dat}{dat_img}\\*.*')
- tryCmd(f'rm {deb_dat}{dat_mus}\\*.*')
- tryCmd(f'rm {deb_dat}{dat_obj}\\*.*')
- tryCmd(f'rm {deb_dat}{dat_scn}\\*.*')
- tryCmd(f'rm {deb_dat}{dat_sfx}\\*.*')
- tryCmd(f'rm {deb_dat}{dat_tls}\\*.*')
- tryCmd(f'rm {rel_dat}{dat_amb}\\*.*')
- tryCmd(f'rm {rel_dat}{dat_bgn}\\*.*')
- tryCmd(f'rm {rel_dat}{dat_img}\\*.*')
- tryCmd(f'rm {rel_dat}{dat_mus}\\*.*')
- tryCmd(f'rm {rel_dat}{dat_obj}\\*.*')
- tryCmd(f'rm {rel_dat}{dat_scn}\\*.*')
- tryCmd(f'rm {rel_dat}{dat_sfx}\\*.*')
- tryCmd(f'rm {rel_dat}{dat_tls}\\*.*')
- tryCmd(f'XCOPY /E /Y .\\_ambience\\_qoa { deb_dat}{dat_amb}')
- tryCmd(f'XCOPY /E /Y .\\_background\\_qoi {deb_dat}{dat_bgn}')
- tryCmd(f'XCOPY /E /Y .\\_img\\_qoi { deb_dat}{dat_img}')
- tryCmd(f'XCOPY /E /Y .\\_music { deb_dat}{dat_mus}')
- tryCmd(f'XCOPY /E /Y .\\_objimg\\_qoi { deb_dat}{dat_obj}')
- tryCmd(f'XCOPY /E /Y .\\_scene\\_ksd { deb_dat}{dat_scn}')
- tryCmd(f'XCOPY /E /Y .\\_sfx\\_qoa { deb_dat}{dat_sfx}')
- tryCmd(f'XCOPY /E /Y .\\_tileset\\_qoi { deb_dat}{dat_tls}')
- tryCmd(f'XCOPY /E /Y .\\_ambience\\_qoa { rel_dat}{dat_amb}')
- tryCmd(f'XCOPY /E /Y .\\_background\\_qoi {rel_dat}{dat_bgn}')
- tryCmd(f'XCOPY /E /Y .\\_img\\_qoi { rel_dat}{dat_img}')
- tryCmd(f'XCOPY /E /Y .\\_music { rel_dat}{dat_mus}')
- tryCmd(f'XCOPY /E /Y .\\_objimg\\_qoi { rel_dat}{dat_obj}')
- tryCmd(f'XCOPY /E /Y .\\_scene\\_ksd { rel_dat}{dat_scn}')
- tryCmd(f'XCOPY /E /Y .\\_sfx\\_qoa { rel_dat}{dat_sfx}')
- tryCmd(f'XCOPY /E /Y .\\_tileset\\_qoi { rel_dat}{dat_tls}')
- _generate_arrlen.main()
- print(f'ALL ASSETS COPIED IN {time()-timeStart} SECONDS')
- if __name__ == '__main__':
- tryCmd('pause')
- exit(0)
- if __name__ == '__main__': main()/******************************************************************************/
- /******************************************************************************/
- //"kw32g\asset_scripts\_compile_and_run.py":
- _DEBUG = True #will run release build otherwise
- _PRINT = True
- project_folder = "\\kw32g"
- from sys import argv
- from os import system as cmd, chdir, getcwd
- import subprocess
- from os import listdir
- from os.path import isfile, join
- def filesInDirectory(_dir):
- return len([name for name in listdir(_dir) if isfile(join(_dir, name))])
- def tryInt(string):
- try:
- return int(string)
- except ValueError:
- return None
- except TypeError:
- return None
- def run_program(args, fatal=False):
- if type(args) != list:
- print('args must be of type list')
- exit(-1)
- if _PRINT: print((' ').join(args))
- returnCode = subprocess.call(args, shell=True)
- if returnCode >= 2**31: returnCode -= 2**32
- if fatal:
- if returnCode != 0:
- if _PRINT: print(f'failed to execute "{args}"')
- exit(returnCode)
- return True
- else:
- return returnCode == 0
- def tryCmd(s, ps=True):
- if _PRINT:
- print(s)
- if ps: cmd('powershell -command '+s)
- else : cmd(s)
- else:
- if ps: cmd(f'powershell -command {s} >nul')
- else : cmd(s+' >nul')
- import _compile_scene
- def main(rangeMin = 1, rangeMax = -1):
- if rangeMin < 1:
- print("rangeMin cannot be less than 1")
- exit(-1)
- #-2 to account for create_new_scene.py and scene 0,
- #which only exists as a template when creating scene tilemaps
- if rangeMax == -1:
- rangeMax = filesInDirectory("../_tmx")-2
- if rangeMin > rangeMax:
- print("rangeMin cannot be larger than rangeMax")
- exit(-1)
- _compile_scene.main(rangeMin, rangeMax)
- cwd = getcwd()
- game_bin = f'..\\..\\..{project_folder}\\bin'
- if _DEBUG:
- chdir(f'{game_bin}\\_Debug')
- run_program(['kw32g_debug.exe'])
- else:
- chdir(f'{game_bin}\\_Release')
- run_program(['kw32g.exe'])
- chdir(cwd) #just in case
- #workaround for a problem with Tiled's console output
- tryCmd('pause >nul')
- #(all args are optional!)
- #args go: rangeMin, rangeMax, "-r" to run release build
- #if only 1 arg is given (excluding -r), rangeMax will
- #be set to rangeMin
- #if none are given (excluding -r), rangeMin is 1,
- # and rangeMax will be the total number of scenes
- #(-r should be put as the last arg, regardless of
- # if rangeMin and rangeMax is given!)
- if __name__ == "__main__":
- rangeMin, rangeMax = 1, -1
- if len(argv) > 1:
- _rangeMin = tryInt(argv[1])
- if _rangeMin != None: rangeMin = _rangeMin
- if argv[-1].lower() == "-r": _DEBUG = False
- if len(argv) > 2:
- _rangeMax = tryInt(argv[2])
- if _rangeMax != None: rangeMax = _rangeMax
- main(rangeMin, rangeMax)/******************************************************************************/
- /******************************************************************************/
- //"kw32g\asset_scripts\_compile_scene.py":
- _DEBUG = True
- scene_tmx_fmt = '..\\_tmx\\scene_{}.tmx'
- from time import time
- from sys import argv
- from struct import pack as struct_pack
- from os import system as cmd
- import _parse_scene as ps
- '''public stuff from ps:
- ps.obj_data_types = {
- "u8", "s8", "u16", "s16", "u24", "s24",
- "u32", "s32", "u64", "s64", "f32", "f64",
- "rgb", "argb"
- }
- ps.printError(s, fatal=True)
- ps.sceneIdToPath(scene_id)
- #returns None if int() conversion fails
- ps.tryInt(string)
- #returns None if key is not in dict[ionary]
- ps.tryDict(dict, key)
- #dataValue should always be of type str
- #type should be one of: int, bool, float, color, object, file
- ps.convertType(dataType, dataValue)
- ps.printScene(scene, printLayers=_PRINT_LAYERS)
- ps.parseSceneMap(scene_id, announce=False)
- '''
- # 8 16 24 32 64
- unsigned_max = (-1, 0xff, 0xffff, 0xffffff, 0xffffffff, -1, -1, -1, 0xffffffffffffffff)
- signed_min = (-1,-0x80,-0x8000,-0x800000,-0x80000000, -1, -1, -1,-0x8000000000000000)
- signed_max = (-1, 0x7f, 0x7fff, 0x7fffff, 0x7fffffff, -1, -1, -1, 0x7fffffffffffffff)
- def to_bytes_integer(_v, byteCount, isSigned):
- v = ps.tryInt(_v)
- if v == None: ps.printError(f'cannot convert {_v} ({hex(v)}) to an integer')
- if isSigned:
- if v < signed_min[byteCount] or v > signed_max[byteCount]:
- ps.printError(f'cannot convert {v} ({hex(v)}) to s{byteCount*8}')
- if v < 0:
- v += unsigned_max[byteCount]+1
- else:
- if v < 0 or v > unsigned_max[byteCount]:
- ps.printError(f'cannot convert {v} ({hex(v)}) to u{byteCount*8}')
- return v.to_bytes(byteCount, "little")
- def to_bytes_float(_v, doublePrecision):
- v = float(_v)
- if doublePrecision: return struct_pack("d", v)
- else : return struct_pack("f", v)
- #assumes (r,g,b,a), converts to [a]rgb in bytes
- def to_bytes_color(_v, useAlpha):
- if type(_v) != list and type(_v != tuple):
- ps.printError("color must be of type \"list\" or \"tuple\"")
- if len(_v) < 3:
- ps.printError("color has less than 3 channels")
- red = to_bytes_integer(_v[0], 1, False)
- blue = to_bytes_integer(_v[1], 1, False)
- green = to_bytes_integer(_v[2], 1, False)
- if useAlpha:
- if len(_v) < 4:
- ps.printError("color has less than 4 channels, despite using alpha")
- alpha = to_bytes_integer(_v[3], 1, False)
- return alpha + red + green + blue
- else:
- return red + green + blue
- def to_bytes_u8(v): return to_bytes_integer(v, 1, False)
- def to_bytes_s8(v): return to_bytes_integer(v, 1, True )
- def to_bytes_u16(v): return to_bytes_integer(v, 2, False)
- def to_bytes_s16(v): return to_bytes_integer(v, 2, True )
- def to_bytes_u24(v): return to_bytes_integer(v, 3, False)
- def to_bytes_s24(v): return to_bytes_integer(v, 3, True )
- def to_bytes_u32(v): return to_bytes_integer(v, 4, False)
- def to_bytes_s32(v): return to_bytes_integer(v, 4, True )
- def to_bytes_u64(v): return to_bytes_integer(v, 8, False)
- def to_bytes_s64(v): return to_bytes_integer(v, 8, True )
- def to_bytes_f32(v): return to_bytes_float(v, False)
- def to_bytes_f64(v): return to_bytes_float(v, True )
- def to_bytes_rgb (v): return to_bytes_color(v, False)
- def to_bytes_argb(v): return to_bytes_color(v, True )
- to_bytes = { #for example, "to_bytes["u16"](55)" will output "b'\x37\x00'"
- "u8" : to_bytes_u8, "s8" : to_bytes_s8,
- "u16" : to_bytes_u16, "s16" : to_bytes_s16,
- "u24" : to_bytes_u24, "s24" : to_bytes_s24,
- "u32" : to_bytes_u32, "s32" : to_bytes_s32,
- "u64" : to_bytes_u64, "s64" : to_bytes_s64,
- "f32" : to_bytes_f32, "f64" : to_bytes_f64,
- "rgb" : to_bytes_rgb, "argb" : to_bytes_argb
- }
- def total_byte_length(bytes_list): return sum(len(i) for i in bytes_list)
- def join_bytes(bytes_list): return (b'').join(bytes_list)
- def high_bit_boolean(boolean, integer, byte_width):
- bool_binary = str(int(boolean))
- int_binary = bin(integer)[2:].rjust(byte_width*8-1,'0') #([2:] to remove the "0b" prefix)
- result_binary = bool_binary + int_binary
- if len(result_binary) > byte_width*8:
- ps.printError(f"high_bit_boolean() result longer than {byte_width} bytes")
- return to_bytes_integer(int(result_binary, 2), byte_width, False)
- '''struct Object { //48B (28 bytes reserved for the user data)
- kit::u64 _user_0; //data depends on object type
- kit::u64 _user_1; //
- kit::u64 _user_2; //
- kit::u32 _user_3; //
- struct {
- kit::u16 x;
- kit::u16 y;
- } /*-----------*/ size;
- struct {
- kit::u8 x; //hitbox's offset relative to the
- kit::u8 y; //object's actual position
- } /*---------*/ offset;
- kit::s16 x;
- kit::s16 y;
- struct {
- kit::u16 type : 14;
- kit::u16 persistent : 1; //'never reset this object's cached data?'
- kit::u16 in_front : 1; //to either display before player or after foreground
- };
- Object_TickCallback update; //called for every tick that the object is active
- };'''
- #returns a single byte object
- def assemble_object(obj):
- if _DEBUG: print(f' assembling object "{obj["name"]}"...')
- #first 28 bytes of the 48 byte object
- customs = []
- for c in obj["data"]:
- customs.append(to_bytes[c[0]](c[1]))
- customs_len = total_byte_length(customs)
- if customs_len > 28: ps.printError("total length of properties exceeded 28 bytes")
- if customs_len < 28: customs.append(b'\x00'*(28-customs_len)) #add padding to end
- type = obj["type"]
- persistent = obj["name"][-1:] == "*"
- in_front = obj["name"][0:1] == "^"
- if type >= 2**14: ps.printError("object type id cannot exceed 16383")
- if persistent: type += 16384 #set bit 14
- #last 20 bytes of the 48 byte object
- intrinsics = [
- to_bytes_integer(obj["width" ], 2, False), #u16: hb.size.x
- to_bytes_integer(obj["height" ], 2, False), #u16: hb.size.y
- to_bytes_integer(obj["hb_offset_x"], 1, False), #u8 : hb.offset.x
- to_bytes_integer(obj["hb_offset_y"], 1, False), #u8 : hb.offset.y
- to_bytes_integer(obj["x" ], 2, True ), #s16: x
- to_bytes_integer(obj["y" ], 2, True ), #s16: y
- high_bit_boolean( in_front, type, 2 ), #u16: in_front, persistent, type
- #(doesn't matter what this is set to, as it's overwritten at runtime anyway)
- b'\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA' #ptr: update
- ]
- #concatenate the result of the object's custom and intrinsic properties
- return join_bytes(customs + intrinsics)
- '''//for storing the contents of a .ksd file (compressed scene data)
- #define KSD_MAGIC 0x4644536B //"kSDF"
- #define SD_FILELEN(_scene_desc) ((_scene_desc).dataSize+sizeof(SceneDescriptor))
- struct SceneDescriptor { //64B
- /*0x00*/ kit::u32 magic; //= 0x4644536B = "kSDF" (no null terminator)
- /*0x04*/ kit::u32 dataSize; //size of file in bytes, minus the header (which is always 64B)
- //offsets to array data in file (if nullptr, data is assumed to not be present!)
- //(also, objs[x].type refers to the index inside gl_objCallbacks used by the object.
- //each element of gl_objCallbacks is of type Object_TickCallback)
- /*0x08*/ Object* objs; //contains the original states of each object in the scene
- /*0x10*/ Tile* pat_mg; //pattern data is compressed using RLE, where the 1st element's .value
- /*0x18*/ Tile* pat_fg; //member is the run length, with the 2nd being the actual tile data
- struct {
- /*0x20*/ kit::u16 bmp_bg : 15; //associated background id
- /*0x21*/ kit::u16 repeat_bg : 1; //'should bg repeat?' (stretches to screen otherwise)
- };
- struct {
- /*0x22*/ kit::u16 objs_len : 15; //number of objects in scene
- /*0x23*/ kit::u16 visited : 1; //used to help determine if objects should reset on load
- };
- /*0x24*/ kit::u16 tileset_a; //1st associated tileset
- /*0x26*/ kit::u16 tileset_b; //2nd associated tileset
- /*0x28*/ kit::u16 edge_n; //scene id for north edge
- /*0x2A*/ kit::u16 edge_s; //scene id for south edge
- /*0x2C*/ kit::u16 edge_w; //scene id for west edge
- /*0x2E*/ kit::u16 edge_e; //scene id for east edge
- /*0x30*/ kit::u16 scene; //scene id for scene itself
- /*0x32*/ kit::u16 music; //music id; 0 for no change, -1 (65535) to stop
- /*0x34*/ kit::u16 ambience_a; //ambient track id a; 0 for no change, -1 to stop
- /*0x36*/ kit::u16 ambience_b; //ambient track id b; 0 for no change, -1 to stop
- /*0x38*/ kit::BinaryData* fileData = nullptr; //raw file data; appears as nullptr in file
- /*0x40*/ //... (array data is stored in order of: objs, pat_mg, and pat_fg)
- };'''
- #returns a single byte object
- def assemble_header(props, dataSize, objs, pat_mg, pat_fg):
- bmp_bg = props["bmp_bg" ]
- repeat_bg = props["repeat_bg"]
- if props["objs_len"] < 0:
- ps.printError("objs_len cannot be < 0")
- return join_bytes((
- to_bytes_integer( 0x4644536B, 4, False), #u32
- to_bytes_integer( dataSize, 4, False), #u32
- to_bytes_integer( objs, 8, False), #ptr
- to_bytes_integer( pat_mg, 8, False), #ptr
- to_bytes_integer( pat_fg, 8, False), #ptr
- high_bit_boolean( repeat_bg, bmp_bg, 2 ), #u16
- to_bytes_integer(props["objs_len" ], 2, True ), #u15 (yes, 15-bit)
- to_bytes_integer(props["tileset_a" ], 2, False), #u16
- to_bytes_integer(props["tileset_b" ], 2, False), #u16
- to_bytes_integer(props["edge_n" ], 2, False), #u16
- to_bytes_integer(props["edge_s" ], 2, False), #u16
- to_bytes_integer(props["edge_w" ], 2, False), #u16
- to_bytes_integer(props["edge_e" ], 2, False), #u16
- to_bytes_integer(props["scene" ], 2, False), #u16
- to_bytes_integer(props["music" ], 2, False), #u16
- to_bytes_integer(props["ambience_a"], 2, False), #u16
- to_bytes_integer(props["ambience_b"], 2, False), #u16
- b'\x00\x00\x00\x00\x00\x00\x00\x00' #ptr
- ))
- #returns a list of byte objects
- def assemble_layer(layers, which):
- layer_tile = layers[which]
- layer_collision = layers["collision"]
- raw = [None,]*len(layer_tile)
- for i in range(len(layer_tile)):
- tile = to_bytes_integer(layer_tile [i], 1, False)
- collision = to_bytes_integer(layer_collision[i], 1, False)
- raw[i] = tile + collision
- rle = []
- previous = raw[0]
- count = 0
- for current in raw:
- if current == previous:
- count += 1
- else:
- run = to_bytes_integer(count, 2, False)
- rle.append(run + previous)
- count = 1
- previous = current
- if count != 0:
- run = to_bytes_integer(count, 2, False)
- rle.append(run + previous)
- return rle
- #returns a list of byte objects
- def assemble_scene(scene):
- if _DEBUG: print(f' GENERATING SCENE { scene["properties"]["scene"] }\'S DATA:')
- if ps.tryDict(scene, "objs"):
- #sort objects alphabetically based on their name (excluding ^)
- scene["objs"] = sorted(scene["objs"], key = lambda x:
- x["name"][1:] if x["name"].startswith("^") else x["name"])
- #ps.pprint(scene["objs"])
- objs = [ assemble_object(obj) for obj in scene["objs"] ]
- mg = assemble_layer(scene["layers"], "mg")
- fg = assemble_layer(scene["layers"], "fg")
- objs_nonzero = len(scene["objs"]) > 0
- fg_nonzero = scene["properties"]["fg_nonzero"]
- mg_nonzero = scene["properties"]["mg_nonzero"]
- #header_len = 64
- objs_len = total_byte_length(objs)
- mg_len = total_byte_length(mg )
- fg_len = total_byte_length(fg )
- dataSize = 0
- if objs_nonzero: dataSize += objs_len
- else : objs_len = 0
- if mg_nonzero : dataSize += mg_len
- else : mg_len = 0
- if fg_nonzero : dataSize += fg_len
- else : fg_len = 0
- offset_objs = 64
- offset_mg = offset_objs + objs_len
- offset_fg = offset_mg + mg_len
- #offsets should be 0 in file if array is not used
- offset_objs *= objs_nonzero # x*False = 0, x*True = x
- offset_mg *= mg_nonzero
- offset_fg *= fg_nonzero
- header = assemble_header(scene["properties"], dataSize,
- offset_objs, offset_mg, offset_fg)
- output = [header,]
- if objs_nonzero: output += objs
- if mg_nonzero : output += mg
- if fg_nonzero : output += fg
- return output
- def write_scene(list_of_byte_objects, scene_id):
- if _DEBUG: print(f' WRITING SCENE { scene_id }\'S DATA TO FILE:')
- with open(f'../_ksd/scene_{scene_id}.ksd', "wb") as file:
- for chunk in list_of_byte_objects:
- file.write(chunk)
- from os.path import exists
- #writes scene data to descriptor file
- def compile_scene(scene_id):
- timeStartTotal = time()
- if scene_id < 1:
- ps.printError('scene_id cannot be less than 1')
- scene_tmx_path = scene_tmx_fmt.format(scene_id)
- if not exists(scene_tmx_path):
- ps.printError(f'scene "{scene_tmx_path}" doesn\'t exist')
- timeStart = time()
- scene = ps.parseSceneMap(scene_id, announce=_DEBUG)
- timeTakenMS = (time()-timeStart)*1000
- if _DEBUG: print(" FINISHED PARSING IN: {:.4}ms".format(timeTakenMS))
- timeStart = time()
- scene_bytes = assemble_scene(scene)
- timeTakenMS = (time()-timeStart)*1000
- if _DEBUG: print(" FINISHED GENERATING IN: {:.4}ms".format(timeTakenMS))
- timeStart = time()
- write_scene(scene_bytes, scene_id)
- timeTakenMS = (time()-timeStart)*1000
- if _DEBUG: print(" FINISHED WRITING IN: {:.4}ms".format(timeTakenMS))
- timeTakenMS = (time()-timeStartTotal)*1000
- if _DEBUG: print(" TOTAL TIME SPENT: {:.4}ms".format(timeTakenMS))
- return timeTakenMS
- from os import listdir
- from os.path import isfile, join
- def filesInDirectory(_dir):
- return len([name for name in listdir(_dir) if isfile(join(_dir, name))])
- def tryCmd(s, ps=True):
- if _DEBUG:
- print(s)
- if ps: cmd('powershell -command '+s)
- else : cmd(s)
- else:
- if ps: cmd(f'powershell -command {s} >nul')
- else : cmd(s+' >nul')
- import _copy_all_scenes
- import _generate_arrlen_clone
- def main(rangeMin, rangeMax, pauseOnExit=False):
- timeStart = time()
- if rangeMin < 1:
- ps.printError('rangeMin cannot be less than 1')
- if rangeMax < 1:
- ps.printError('rangeMax cannot be less than 1')
- tryCmd('rm ..\\_ksd\\*.*')
- for i in range(rangeMin, rangeMax+1):
- compile_scene(i)
- _copy_all_scenes.main()
- _generate_arrlen_clone.main()
- timeTakenMS = (time()-timeStart)*1000
- if _DEBUG: print(" SCENES {} -> {} COMPILED IN: {}ms".format(rangeMin, rangeMax, int(timeTakenMS)))
- if pauseOnExit and __name__ == "__main__":
- cmd("powershell -command pause")
- #arguments go as follows: rangeMin, rangeMax, pauseOnExit, disablePrinting (AKA _DEBUG)
- #rangeMax and pauseOnExit are optional, as if only rangeMin
- #is specified, rangeMax will be set to rangeMin as well
- #(pauseOnExit and disablePrinting are false by default)
- if __name__ == "__main__":
- #if no args are given, calculate the number of scenes,
- #and compile all of them
- if len(argv) < 2:
- #-2 to account for create_new_scene.py and scene 0,
- #which only exists as a template when creating scene tilemaps
- rangeMax = filesInDirectory("../_tmx")-2
- if rangeMax < 1:
- ps.printError('no valid scenes found! (or none greater than 0)')
- main(1, rangeMax)
- exit(0)
- rangeMin = int(argv[1])
- #(aka disablePrinting)
- if len(argv) >= 5: _DEBUG = argv[4].lower() == "false"
- pauseOnExit = False
- if len(argv) >= 4: pauseOnExit = argv[3].lower() == "true"
- rangeMax = rangeMin
- if len(argv) >= 3: rangeMax = int(argv[2])
- main(rangeMin, rangeMax, pauseOnExit)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement