Advertisement
Kitomas

_compile_scene.py as of 2024-05-16

May 17th, 2024
648
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.51 KB | None | 0 0
  1. if __name__ != "__main__": exit(0) #lol
  2.  
  3. _DEBUG = True
  4.  
  5. #from os import listdir, system as cmd
  6. #from os.path import join
  7. from time import time
  8. from sys import argv
  9. from struct import pack as struct_pack
  10. from os import system as cmd
  11.  
  12. import _parse_scene as ps
  13. '''public stuff from ps:
  14.  
  15. ps.obj_data_types = {
  16.     "u8",  "s8",  "u16", "s16",  "u24", "s24",
  17.    "u32", "s32",  "u64", "s64",  "f32", "f64",
  18.    "rgb", "argb"
  19. }
  20.  
  21. ps.printError(s, fatal=True)
  22.  
  23. ps.sceneIdToPath(scene_id)
  24.  
  25. #returns None if int() conversion fails
  26. ps.tryInt(string)
  27.  
  28. #returns None if key is not in dict[ionary]
  29. ps.tryDict(dict, key)
  30.  
  31. #dataValue should always be of type str
  32. #type should be one of: int, bool, float, color, object, file
  33. ps.convertType(dataType, dataValue)
  34.  
  35. ps.printScene(scene, printLayers=_PRINT_LAYERS)
  36.  
  37. ps.parseSceneMap(scene_id, announce=False)
  38. '''
  39.  
  40.  
  41.  
  42.  
  43. #                      8      16        24          32                              64
  44. unsigned_max = (-1, 0xff, 0xffff, 0xffffff, 0xffffffff, -1, -1, -1, 0xffffffffffffffff)
  45. signed_min   = (-1,-0x80,-0x8000,-0x800000,-0x80000000, -1, -1, -1,-0x8000000000000000)
  46. signed_max   = (-1, 0x7f, 0x7fff, 0x7fffff, 0x7fffffff, -1, -1, -1, 0x7fffffffffffffff)
  47.  
  48. def to_bytes_integer(_v, byteCount, isSigned):
  49.     v = ps.tryInt(_v)
  50.     if v == None: ps.printError(f'cannot convert {_v} ({hex(v)}) to an integer')
  51.    
  52.     if isSigned:
  53.         if v < signed_min[byteCount]  or  v > signed_max[byteCount]:
  54.             ps.printError(f'cannot convert {v} ({hex(v)}) to s{byteCount*8}')
  55.         if v < 0:
  56.             v += unsigned_max[byteCount]+1
  57.            
  58.     else:
  59.         if v < 0  or  v > unsigned_max[byteCount]:
  60.             ps.printError(f'cannot convert {v} ({hex(v)}) to u{byteCount*8}')
  61.        
  62.     return v.to_bytes(byteCount, "little")
  63.    
  64.  
  65. def to_bytes_float(_v, doublePrecision):
  66.     v = float(_v)
  67.     if doublePrecision: return struct_pack("d", v)
  68.     else              : return struct_pack("f", v)
  69.    
  70.  
  71. #assumes (r,g,b,a), converts to [a]rgb in bytes
  72. def to_bytes_color(_v, useAlpha):
  73.     if type(_v) != list  and type(_v != tuple):
  74.         ps.printError("color must be of type \"list\" or \"tuple\"")
  75.     if len(_v) < 3:
  76.         ps.printError("color has less than 3 channels")
  77.        
  78.     red   = to_bytes_integer(_v[0], 1, False)
  79.     blue  = to_bytes_integer(_v[1], 1, False)
  80.     green = to_bytes_integer(_v[2], 1, False)
  81.    
  82.     if useAlpha:
  83.         if len(_v) < 4:
  84.             ps.printError("color has less than 4 channels, despite using alpha")
  85.         alpha = to_bytes_integer(_v[3], 1, False)
  86.         return alpha + red + green + blue
  87.        
  88.     else:
  89.         return red + green + blue
  90.  
  91.  
  92. def to_bytes_u8(v): return to_bytes_integer(v, 1, False)
  93. def to_bytes_s8(v): return to_bytes_integer(v, 1, True )
  94.  
  95. def to_bytes_u16(v): return to_bytes_integer(v, 2, False)
  96. def to_bytes_s16(v): return to_bytes_integer(v, 2, True )
  97.  
  98. def to_bytes_u24(v): return to_bytes_integer(v, 3, False)
  99. def to_bytes_s24(v): return to_bytes_integer(v, 3, True )
  100.  
  101. def to_bytes_u32(v): return to_bytes_integer(v, 4, False)
  102. def to_bytes_s32(v): return to_bytes_integer(v, 4, True )
  103.  
  104. def to_bytes_u64(v): return to_bytes_integer(v, 8, False)
  105. def to_bytes_s64(v): return to_bytes_integer(v, 8, True )
  106.  
  107. def to_bytes_f32(v): return to_bytes_float(v, False)
  108. def to_bytes_f64(v): return to_bytes_float(v, True )
  109.  
  110. def to_bytes_rgb (v): return to_bytes_color(v, False)
  111. def to_bytes_argb(v): return to_bytes_color(v, True )
  112.  
  113.  
  114. to_bytes = { #for example, "to_bytes["u16"](55)" will output "b'\x37\x00'"
  115.     "u8"  : to_bytes_u8,   "s8"  : to_bytes_s8,
  116.     "u16" : to_bytes_u16,  "s16" : to_bytes_s16,
  117.     "u24" : to_bytes_u24,  "s24" : to_bytes_s24,
  118.     "u32" : to_bytes_u32,  "s32" : to_bytes_s32,
  119.     "u64" : to_bytes_u64,  "s64" : to_bytes_s64,
  120.     "f32" : to_bytes_f32,  "f64" : to_bytes_f64,
  121.     "rgb" : to_bytes_rgb, "argb" : to_bytes_argb
  122. }
  123.  
  124.  
  125.  
  126.  
  127. def total_byte_length(bytes_list): return sum(len(i) for i in bytes_list)
  128. def join_bytes(bytes_list): return (b'').join(bytes_list)
  129. def high_bit_boolean(boolean, integer, byte_width):
  130.     bool_binary = str(int(boolean))
  131.     int_binary  = bin(integer)[2:].rjust(byte_width*8-1,'0') #([2:] to remove the "0b" prefix)
  132.     result_binary = bool_binary + int_binary
  133.    
  134.     if len(result_binary) > byte_width*8:
  135.         ps.printError(f"high_bit_boolean() result longer than {byte_width} bytes")
  136.        
  137.     return to_bytes_integer(int(result_binary, 2), byte_width, False)
  138.  
  139. '''struct Object { //48B (28 bytes reserved for the user data)
  140.  kit::u64           _user_0; //data depends on object type
  141.  kit::u64           _user_1;  //
  142.  kit::u64           _user_2;  //
  143.  kit::u32           _user_3;  //
  144.  
  145.  struct {
  146.    struct {
  147.      kit::u16         x;
  148.      kit::u16         y;
  149.    } /*-----------*/ size;
  150.    struct {
  151.      kit::u8          x; //hitbox's offset relative to the
  152.      kit::u8          y;  //object's actual position
  153.    } /*---------*/ offset;
  154.  } /*-----------------*/ hb; //h[it]b[ox]
  155.  
  156.  kit::s16                 x;
  157.  kit::s16                 y;
  158.  
  159.  struct {
  160.    kit::u16            type : 14;
  161.    kit::u16      persistent :  1; //'never reset this object's cached data?'
  162.    kit::u16        in_front :  1; //'should be displayed in front of player?'
  163.  };
  164.  
  165.  Object_TickCallback update;
  166. };'''
  167. #returns a single byte object
  168. def assemble_object(obj):
  169.     if _DEBUG: print(f'    assembling object "{obj["name"]}"...')
  170.  
  171.  
  172.     #first 28 bytes of the 48 byte object
  173.     customs = []
  174.     for c in obj["data"]:
  175.         customs.append(to_bytes[c[0]](c[1]))
  176.    
  177.     customs_len = total_byte_length(customs)
  178.     if customs_len > 28: ps.printError("total length of properties exceeded 28 bytes")
  179.     if customs_len < 28: customs.append(b'\x00'*(28-customs_len)) #add padding to end
  180.    
  181.    
  182.     type       = obj["type"]
  183.     persistent = obj["name"][-1:] == "*"
  184.     in_front   = obj["name"][0:1] == "^"
  185.    
  186.     if type >= 2**14: ps.printError("object type id cannot exceed 16383")
  187.     if persistent: type += 16384 #set bit 14
  188.  
  189.     #last 20 bytes of the 48 byte object
  190.     intrinsics = [
  191.         to_bytes_integer(obj["width"      ], 2, False), #u16: hb.size.x
  192.         to_bytes_integer(obj["height"     ], 2, False), #u16: hb.size.y
  193.         to_bytes_integer(obj["hb_offset_x"], 1, False), #u8 : hb.offset.x
  194.         to_bytes_integer(obj["hb_offset_y"], 1, False), #u8 : hb.offset.y
  195.         to_bytes_integer(obj["x"          ], 2, True ), #s16: x
  196.         to_bytes_integer(obj["y"          ], 2, True ), #s16: y
  197.         high_bit_boolean(    in_front, type, 2       ), #u16: in_front, persistent, type
  198.         #(doesn't matter what this is set to, as it's overwritten at runtime anyway)
  199.         b'\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA'             #ptr: update
  200.     ]
  201.    
  202.    
  203.     #concatenate the result of the object's custom and intrinsic properties
  204.     return join_bytes(customs + intrinsics)
  205.  
  206.  
  207.  
  208.  
  209. '''//for storing the contents of a .ksd file (compressed scene data)
  210. #define KSD_MAGIC 0x4644536B //"kSDF"
  211. #define SD_FILELEN(_scene_desc) ((_scene_desc).dataSize+sizeof(SceneDescriptor))
  212. struct SceneDescriptor { //64B
  213. /*0x00*/  kit::u32      magic; //= 0x4644536B = "kSDF" (no null terminator)
  214. /*0x04*/  kit::u32   dataSize; //size of file in bytes, minus the header (which is always 64B)
  215.  
  216.          //offsets to array data in file (if nullptr, data is assumed to not be present!)
  217.          //(also, objs[x].type refers to the index inside gl_objCallbacks used by the object.
  218.           //each element of gl_objCallbacks is of type Object_TickCallback)
  219. /*0x08*/  Object*        objs; //contains the original states of each object in the scene
  220. /*0x10*/  Tile*        pat_mg; //pattern data is compressed using RLE, where the 1st element's .value
  221. /*0x18*/  Tile*        pat_fg;  //member is the run length, with the 2nd being the actual tile data
  222.  
  223.        struct {
  224. /*0x20*/  kit::u16     bmp_bg : 15; //associated background id
  225. /*0x21*/  kit::u16  repeat_bg :  1; //'should bg repeat?' (stretches to screen otherwise)
  226.        };
  227.  
  228.        struct {
  229. /*0x22*/  kit::u16   objs_len : 15; //number of objects in scene
  230. /*0x23*/  kit::u16    visited :  1; //used to help determine if objects should reset on load
  231.        };
  232.  
  233. /*0x24*/  kit::u16  tileset_a; //1st associated tileset
  234. /*0x26*/  kit::u16  tileset_b; //2nd associated tileset
  235.  
  236. /*0x28*/  kit::u16     edge_n; //scene id for north edge
  237. /*0x2A*/  kit::u16     edge_s; //scene id for south edge
  238. /*0x2C*/  kit::u16     edge_w; //scene id for west edge
  239. /*0x2E*/  kit::u16     edge_e; //scene id for east edge
  240. /*0x30*/  kit::u16      scene; //scene id for scene itself
  241.  
  242. /*0x32*/  kit::u16      music; //music id;  0 for no change, -1 (65535) to stop
  243. /*0x34*/  kit::u16 ambience_a; //ambient track id a;  0 for no change, -1 to stop
  244. /*0x36*/  kit::u16 ambience_b; //ambient track id b;  0 for no change, -1 to stop
  245.  
  246. /*0x38*/  kit::BinaryData* fileData = nullptr; //raw file data; appears as nullptr in file
  247.  
  248. /*0x40*/  //... (array data is stored in order of: objs, pat_mg, and pat_fg)
  249. };'''
  250. #returns a single byte object
  251. def assemble_header(props, dataSize, objs, pat_mg, pat_fg):
  252.     bmp_bg    = props["bmp_bg"   ]
  253.     repeat_bg = props["repeat_bg"]
  254.    
  255.     if props["objs_len"] < 0:
  256.         ps.printError("objs_len cannot be < 0")
  257.  
  258.     return join_bytes((
  259.         to_bytes_integer(         0x4644536B, 4, False), #u32
  260.         to_bytes_integer(           dataSize, 4, False), #u32
  261.        
  262.         to_bytes_integer(               objs, 8, False), #ptr
  263.         to_bytes_integer(             pat_mg, 8, False), #ptr
  264.         to_bytes_integer(             pat_fg, 8, False), #ptr
  265.        
  266.         high_bit_boolean(  repeat_bg, bmp_bg, 2       ), #u16
  267.        
  268.         to_bytes_integer(props["objs_len"  ], 2, True ), #u15 (yes, 15-bit)
  269.        
  270.         to_bytes_integer(props["tileset_a" ], 2, False), #u16
  271.         to_bytes_integer(props["tileset_b" ], 2, False), #u16
  272.        
  273.         to_bytes_integer(props["edge_n"    ], 2, False), #u16
  274.         to_bytes_integer(props["edge_s"    ], 2, False), #u16
  275.         to_bytes_integer(props["edge_w"    ], 2, False), #u16
  276.         to_bytes_integer(props["edge_e"    ], 2, False), #u16
  277.         to_bytes_integer(props["scene"     ], 2, False), #u16
  278.        
  279.         to_bytes_integer(props["music"     ], 2, False), #u16
  280.         to_bytes_integer(props["ambience_a"], 2, False), #u16
  281.         to_bytes_integer(props["ambience_b"], 2, False), #u16
  282.        
  283.         b'\x00\x00\x00\x00\x00\x00\x00\x00'              #ptr
  284.     ))
  285.  
  286.  
  287.  
  288.  
  289. #returns a list of byte objects
  290. def assemble_layer(layers, which):
  291.     layer_tile      = layers[which]
  292.     layer_collision = layers["collision"]
  293.    
  294.     raw = [None,]*len(layer_tile)
  295.     for i in range(len(layer_tile)):
  296.         tile      = to_bytes_integer(layer_tile     [i], 1, False)
  297.         collision = to_bytes_integer(layer_collision[i], 1, False)
  298.         raw[i] = tile + collision
  299.        
  300.     rle      = []
  301.     previous = raw[0]
  302.     count    = 0
  303.     for current in raw:
  304.         if current == previous:
  305.             count += 1
  306.         else:
  307.             run = to_bytes_integer(count, 2, False)
  308.             rle.append(run + previous)
  309.             count = 1
  310.             previous = current
  311.            
  312.     if count != 0:
  313.         run = to_bytes_integer(count, 2, False)
  314.         rle.append(run + previous)
  315.            
  316.     return rle
  317.  
  318.  
  319.  
  320.  
  321. #returns a list of byte objects
  322. def assemble_scene(scene):
  323.     if _DEBUG: print(f' GENERATING SCENE { scene["properties"]["scene"] }\'S DATA:')
  324.     objs = [ assemble_object(obj) for obj in scene["objs"] ]
  325.     mg   = assemble_layer(scene["layers"], "mg")
  326.     fg   = assemble_layer(scene["layers"], "fg")
  327.    
  328.  
  329.     objs_nonzero = len(scene["objs"]) > 0
  330.     fg_nonzero   = scene["properties"]["fg_nonzero"]
  331.     mg_nonzero   = scene["properties"]["mg_nonzero"]
  332.    
  333.     #header_len = 64
  334.     objs_len = total_byte_length(objs)
  335.     mg_len   = total_byte_length(mg  )
  336.     fg_len   = total_byte_length(fg  )
  337.    
  338.    
  339.     dataSize    = objs_len + mg_len + fg_len
  340.     offset_objs = 64
  341.     offset_mg   = offset_objs + objs_len
  342.     offset_fg   = offset_mg   +   mg_len
  343.    
  344.     #offsets should be 0 in file if array is not used
  345.     offset_objs *= objs_nonzero # x*0 = 0, x*1 = x, blah blah blah
  346.     offset_mg   *= mg_nonzero
  347.     offset_fg   *= fg_nonzero
  348.    
  349.    
  350.     header = assemble_header(scene["properties"], dataSize,
  351.                              offset_objs, offset_mg, offset_fg)
  352.    
  353.     output = [header,]
  354.     if objs_nonzero: output += objs
  355.     if mg_nonzero  : output += mg
  356.     if fg_nonzero  : output += fg
  357.     return output
  358.  
  359.  
  360.  
  361.  
  362. def write_scene(list_of_byte_objects, scene_id):
  363.     if _DEBUG: print(f' WRITING SCENE { scene_id }\'S DATA TO FILE:')
  364.     with open(f'scene_{scene_id}.ksd', "wb") as file:
  365.         for chunk in list_of_byte_objects:
  366.             file.write(chunk)
  367.  
  368.  
  369.  
  370.  
  371. #writes scene data to descriptor file
  372. def compile_scene(scene_id):
  373.     timeStartTotal = time()
  374.  
  375.  
  376.     timeStart = time()
  377.     scene = ps.parseSceneMap(scene_id, announce=_DEBUG)
  378.     timeTakenMS = (time()-timeStart)*1000
  379.     if _DEBUG: print("  FINISHED PARSING IN: {:.4}ms".format(timeTakenMS))
  380.    
  381.     timeStart = time()
  382.     scene_bytes = assemble_scene(scene)
  383.     timeTakenMS = (time()-timeStart)*1000
  384.     if _DEBUG: print("  FINISHED GENERATING IN: {:.4}ms".format(timeTakenMS))
  385.    
  386.     timeStart = time()
  387.     write_scene(scene_bytes, scene_id)
  388.     timeTakenMS = (time()-timeStart)*1000
  389.     if _DEBUG: print("  FINISHED WRITING IN: {:.4}ms".format(timeTakenMS))
  390.    
  391.    
  392.     timeTakenMS = (time()-timeStartTotal)*1000
  393.     if _DEBUG: print(" TOTAL TIME SPENT: {:.4}ms".format(timeTakenMS))
  394.     return timeTakenMS
  395.    
  396.  
  397.  
  398. #arguments go as follows: rangeMin, rangeMax, pauseOnExit, disablePrinting
  399. #rangeMax and pauseOnExit are optional, as if only rangeMin
  400.  #is specified, rangeMax will be set to rangeMin as well
  401. #(pauseOnExit and disablePrinting are false by default)
  402.  
  403. if len(argv) < 2: ps.printError("must provide at least 1 argument for rangeMin")
  404. rangeMin = int(argv[1])
  405.  
  406. if len(argv) >= 5: _DEBUG = argv[4].lower() == "false"
  407.  
  408. pauseOnExit = False
  409. if len(argv) >= 4: pauseOnExit = argv[3].lower() == "true"
  410.  
  411. rangeMax = rangeMin
  412. if len(argv) >= 3: rangeMax = int(argv[2])
  413.  
  414.  
  415. cmd("cls")
  416.  
  417. timeStart = time()
  418.  
  419. for i in range(rangeMin, rangeMax+1):
  420.     compile_scene(i)
  421.  
  422. timeTakenMS = (time()-timeStart)*1000
  423. if _DEBUG: print(" SCENES {} -> {} COMPILED IN: {:.4}ms".format(rangeMin, rangeMax, timeTakenMS))
  424.  
  425. if pauseOnExit: cmd("pause")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement