From ef4d0e08b2d312bcf3034aa3ae48436f5d8b56a5 Mon Sep 17 00:00:00 2001 From: Owl Date: Thu, 21 Aug 2025 20:07:13 -0400 Subject: all the stuff --- bck_funcs.py | 730 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 730 insertions(+) create mode 100644 bck_funcs.py (limited to 'bck_funcs.py') diff --git a/bck_funcs.py b/bck_funcs.py new file mode 100644 index 0000000..8ea2c72 --- /dev/null +++ b/bck_funcs.py @@ -0,0 +1,730 @@ +import os, struct, math + +# python file to read the important information out of a BCK file +# will try its best to decode the information either on big/little endian +# https://humming-owl.neocities.org/smg-stuff/pages/tutorials/bck + +# what this file will do is the following: +# pre_read_bck_file() takes the first look into the BCK and it checks if a BCK is correct. +# On the way it assigns important variables to the final bck_raw_info (smg_bck class). +# If the file is correct, then read_bck_file() will assign the remaining variables, if any +# and return the respective structure (a copy of it, not the reference). +# +# in case of any error pre_read_bck_file() returns a string that can +# be read by a human to identify what it is wrong with the BCK file +# if all is good it will return exactly that (as a string) + +# all the raw variables on a BCK file +class smg_bck_raw: + def __init__(self): + self.endian = None + self.header = self.header() + self.ank1 = self.ank1() + + def __str__(self): + rtn = "### SMG_BCK_RAW - START\n" + rtn += "Endian: %s\n" % (self.endian) + rtn += self.header.__str__() + rtn += self.ank1.__str__() + rtn += "### SMG_BCK_RAW - END\n" + return rtn + + # header + class header: + def __init__(self): + self.magic = None + self.ftype = None + self.file_size = None + self.section_count = None + self.unknown1 = None + + def __str__(self): + rtn = " ### HEADER START\n" + rtn += " Magic: %s\n" % (self.magic.__str__()) + rtn += " File type: %s\n" % (self.ftype.__str__()) + rtn += " File size: %s\n" % (self.file_size.__str__()) + rtn += " Section count: %s\n" % (self.section_count.__str__()) + rtn += " Unknown 1: %s\n" % (self.unknown1.__str__()) + rtn += " ### HEADER END\n" + return rtn + + # ank1 + class ank1: + def __init__(self): + self.magic = None + self.size = None + self.loop_mode = None + self.rot_lshift = None + self.anim_length = None + self.bone_count = None + self.scale_arr_length = None + self.rot_arr_length = None + self.transl_arr_length = None + self.anim_data_offset = None + self.scale_arr_offset = None + self.rot_arr_offset = None + self.transl_arr_offset = None + self.anim_data = [] # list of length bone_count + self.scale_arr = [] # list of length scale_arr_length + self.rot_arr = [] # list of length rot_arr_length + self.transl_arr = [] # list of length transl_arr_length + + def __str__(self): + rtn = " ### ANK1 - START\n" + rtn += " Magic: %s\n" % (self.magic.__str__()) + rtn += " Section size: %s\n" % (self.size.__str__()) + rtn += " Loop mode: %s\n" % (self.loop_mode.__str__()) + rtn += " Rotation left shift: %s\n" % (self.rot_lshift.__str__()) + rtn += " Anim length: %s\n" % (self.anim_length.__str__()) + rtn += " Bone count: %s\n" % (self.bone_count.__str__()) + rtn += " Scale array length: %s\n" % (self.scale_arr_length.__str__()) + rtn += " Rotation array length: %s\n" % (self.rot_arr_length.__str__()) + rtn += " Translation array length: %s\n" % (self.transl_arr_length.__str__()) + rtn += " Anim data offset: %s\n" % (self.anim_data_offset.__str__()) + rtn += " Scale array offset: %s\n" % (self.scale_arr_offset.__str__()) + rtn += " Rotation array offset: %s\n" % (self.rot_arr_offset.__str__()) + rtn += " Translation array offset: %s\n" % (self.transl_arr_offset.__str__()) + rtn += " Animation data:\n" + for i in range(len(self.anim_data)): + rtn += " Bone %d\n" % (i) + rtn += "%s" % (self.anim_data[i].__str__()) + rtn += " Scale array: %s\n" % (self.scale_arr.__str__()) + rtn += " Rotation array: %s\n" % (self.rot_arr.__str__()) + rtn += " Translation array: %s\n" % (self.transl_arr.__str__()) + rtn += " ### ANK1 - END\n" + return rtn + + # bone animation data table + class anim_data: + def __init__(self): + self.comp = [self.comp(), # scale x + self.comp(), # rot x + self.comp(), # transl x + self.comp(), # scale y + self.comp(), # rot y + self.comp(), # transl y + self.comp(), # scale z + self.comp(), # rot z + self.comp()] # transl z + + def __str__(self): + rtn = " Scale X: %s" % (self.comp[0].__str__()) + rtn += " Rot X: %s" % (self.comp[1].__str__()) + rtn += " Transl X: %s" % (self.comp[2].__str__()) + rtn += " Scale Y: %s" % (self.comp[3].__str__()) + rtn += " Rot Y: %s" % (self.comp[4].__str__()) + rtn += " Transl Y: %s" % (self.comp[5].__str__()) + rtn += " Scale Z: %s" % (self.comp[6].__str__()) + rtn += " Rot Z: %s" % (self.comp[7].__str__()) + rtn += " Transl Z: %s" % (self.comp[8].__str__()) + return rtn + + + # the animation data for each of the animation components + class comp: + def __init__(self): + self.keyframe_count = None + self.anim_data_index = None + self.interpolation_mode = None + + def __str__(self): + return "%s %s %s\n" % (self.keyframe_count.__str__(), + self.anim_data_index.__str__(), + self.interpolation_mode.__str__()) + +# the actually useful information encoded in the BCK file +class smg_bck_anim: + def __init__(self): + self.loop_mode = None + self.anim_length = None + self.bone_count = None + self.anim_data = [] # list of length bone_count + + def __str__(self): + rtn = "### SMG_BCK_ANIM START\n" + rtn += " Loop mode: %s\n" % (self.loop_mode.__str__()) + rtn += " Animation length: %s\n" % (self.anim_length.__str__()) + rtn += " Bone count: %s\n" % (self.bone_count.__str__()) + rtn += " Animation data:\n" + for i in range(len(self.anim_data)): + rtn += " Bone %d\n" % (i) + rtn += "%s" % (self.anim_data[i].__str__()) + rtn += "### SMG_BCK_ANIM END" + return rtn + + # anim_data + class anim_data: + def __init__(self): + self.comp = [self.comp(), # scale x + self.comp(), # rot x + self.comp(), # transl x + self.comp(), # scale y + self.comp(), # rot y + self.comp(), # transl y + self.comp(), # scale z + self.comp(), # rot z + self.comp()] # transl z + + def __str__(self): + rtn = " Scale X: %s" % (self.comp[0].__str__()) + rtn += " Rot X: %s" % (self.comp[1].__str__()) + rtn += " Transl X: %s" % (self.comp[2].__str__()) + rtn += " Scale Y: %s" % (self.comp[3].__str__()) + rtn += " Rot Y: %s" % (self.comp[4].__str__()) + rtn += " Transl Y: %s" % (self.comp[5].__str__()) + rtn += " Scale Z: %s" % (self.comp[6].__str__()) + rtn += " Rot Z: %s" % (self.comp[7].__str__()) + rtn += " Transl Z: %s" % (self.comp[8].__str__()) + return rtn + + # anim_data + class comp: + def __init__(self): + self.kf_count = None + self.interp_mode = None + self.time = [] # list of length kf_count (if kf_count > 1) + self.value = [] # list of length kf_count (the only list available to read if kf_count == 1) + self.in_slope = [] # list of length kf_count (if kf_count > 1) + self.out_slope = [] # list of length kf_count (if kf_count > 1) + + def __str__(self): + rtn = "%s %s\n" % (self.kf_count, self.interp_mode) + rtn += " time: %s\n" % (self.time.__str__()) + rtn += " value: %s\n" % (self.value.__str__()) + rtn += " in slope: %s\n" % (self.in_slope.__str__()) + rtn += " out slope: %s\n" % (self.out_slope.__str__()) + return rtn + + +# create a global variable to hold temporal information +bck_raw_info = None +bck_error_str = "bck-error: " +bck_anim_error_str = "bck-anim-error: " +pad_str = "This is padding data to alignme" +f = None + +# main function +# will read and will check while reading +def read_bck_file(filepath): + + # make global variables editable + global f + global bck_raw_info + # "pre read" the file + result_str = pre_read_bck_file(filepath) + print(result_str) + + # all good + bck_anim_info = None + if (result_str == bck_error_str + "all good"): + # construct the data structure that is easier to deal with + print(bck_raw_info) + bck_anim_info = smg_bck_anim() + + # assign the easy variables + bck_anim_info.loop_mode = bck_raw_info.ank1.loop_mode + bck_anim_info.anim_length = bck_raw_info.ank1.anim_length + bck_anim_info.bone_count = bck_raw_info.ank1.bone_count + + # construct the animation tracks + + # iterate over each bone + for i in range(bck_anim_info.bone_count): + bck_anim_info.anim_data.append(smg_bck_anim.anim_data()) + # iterate over each animation component + for j in range(9): + bck_anim_info.anim_data[i].comp[j].kf_count = bck_raw_info.ank1.anim_data[i].comp[j].keyframe_count + bck_anim_info.anim_data[i].comp[j].interp_mode = bck_raw_info.ank1.anim_data[i].comp[j].interpolation_mode + data_index = bck_raw_info.ank1.anim_data[i].comp[j].anim_data_index + arr = None + + # select the array and the items per read + rot_mult = 1 # rotation consideration (convert to radians) + if (j == 0 or j == 3 or j == 6): # scale + arr = bck_raw_info.ank1.scale_arr + elif (j == 1 or j == 4 or j == 7): # rotation + arr = bck_raw_info.ank1.rot_arr + rot_mult = (math.pi * math.pow(2, bck_raw_info.ank1.rot_lshift)) / 0x7FFF + elif (j == 2 or j == 5 or j == 8): # translation + arr = bck_raw_info.ank1.transl_arr + + # assign the data + + # 1 keyframe + if (bck_anim_info.anim_data[i].comp[j].kf_count == 1): + bck_anim_info.anim_data[i].comp[j].time.append(None) + bck_anim_info.anim_data[i].comp[j].value.append(arr[data_index] * rot_mult) + bck_anim_info.anim_data[i].comp[j].in_slope.append(None) + bck_anim_info.anim_data[i].comp[j].out_slope.append(None) + # more than 1 + elif (bck_anim_info.anim_data[i].comp[j].kf_count > 1): + for k in range(bck_anim_info.anim_data[i].comp[j].kf_count): + # time + bck_anim_info.anim_data[i].comp[j].time.append(arr[data_index]) + data_index += 1 + # value + bck_anim_info.anim_data[i].comp[j].value.append(arr[data_index] * rot_mult) + data_index += 1 + # in slope + bck_anim_info.anim_data[i].comp[j].in_slope.append(arr[data_index] * rot_mult) + data_index += 1 + # out slope + if (bck_anim_info.anim_data[i].comp[j].interp_mode == 1): + bck_anim_info.anim_data[i].comp[j].out_slope.append(arr[data_index] * rot_mult) + data_index += 1 + else: + bck_anim_info.anim_data[i].comp[j].out_slope.append(bck_anim_info.anim_data[i].comp[j].in_slope[-1]) + + # done! + f.close() + f = None + bck_raw_info = None + return bck_anim_info + +# function to check a BCK file before getting its full information out +def pre_read_bck_file(filepath): + + # check its size first + if (os.path.getsize(filepath) <= 32): + return bck_error_str + "file size" + + # make global variables editable + global f + global bck_raw_info + + # open the file + f = open(filepath, "rb") + + # holder for variables + bck_raw_info = smg_bck_raw(); + + ######## + # header + + # magic + bck_raw_info.header.magic = f.read(4).decode("ascii") + if (bck_raw_info.header.magic == "J3D1"): + bck_raw_info.endian = "BIG" + elif (bck_raw_info.header.magic == "1D3J"): + bck_raw_info.endian = "LITTLE" + else: + return bck_error_str + "magic" + bck_raw_info.header.magic = "J3D1" + + # variable to set for struct.unpack byte order reading + endian_ch = ">" # big endian + if (bck_raw_info.endian == "LITTLE"): + endian_ch = "<" + + # ftype + bck_raw_info.header.ftype = f.read(4).decode("ascii") + if ((bck_raw_info.header.ftype == "bck1" and bck_raw_info.endian != "BIG") + and (bck_raw_info.header.ftype == "1kcb" and bck_raw_info.endian != "LITTLE")): + return bck_error_str + "ftype" + bck_raw_info.header.ftype = "bck1" + + # file size + bck_raw_info.header.file_size = struct.unpack(endian_ch + "I", f.read(4))[0] + if (bck_raw_info.header.file_size != os.path.getsize(filepath)): + return bck_error_str + "file size" + + # section count + bck_raw_info.header.section_count = struct.unpack(endian_ch + "I", f.read(4))[0] + if (bck_raw_info.header.section_count != 1): + return bck_error_str + "section count" + + # unknown 1 + bck_raw_info.header.unknown1 = list(f.read(16)) + for i in range(16): + if (bck_raw_info.header.unknown1[i] != 0xFF): + return bck_error_str + "unknown 1" + + ############## + # ank1 section + + # magic + bck_raw_info.ank1.magic = f.read(4).decode("ascii") + if ((bck_raw_info.ank1.magic == "ANK1" and bck_raw_info.endian != "BIG") + and (bck_raw_info.ank1.magic == "1KNA" and bck_raw_info.endian != "LITTLE")): + return bck_error_str + "ank1 magic" + bck_raw_info.ank1.magic = "ANK1" + + # size + bck_raw_info.ank1.size = struct.unpack(endian_ch + "I", f.read(4))[0] + if (bck_raw_info.ank1.size != bck_raw_info.header.file_size - 32): + return bck_error_str + "ank1 size" + + # loop mode + bck_raw_info.ank1.loop_mode = struct.unpack(endian_ch + "B", f.read(1))[0] + if (bck_raw_info.ank1.loop_mode > 0x04): + return bck_error_str + "ank1 loop mode" + + # rotation left shift + bck_raw_info.ank1.rot_lshift = struct.unpack(endian_ch + "B", f.read(1))[0] + # animation length + bck_raw_info.ank1.anim_length = struct.unpack(endian_ch + "H", f.read(2))[0] + # bone count + bck_raw_info.ank1.bone_count = struct.unpack(endian_ch + "H", f.read(2))[0] + # scale array length + bck_raw_info.ank1.scale_arr_length = struct.unpack(endian_ch + "H", f.read(2))[0] + # rotation array length + bck_raw_info.ank1.rot_arr_length = struct.unpack(endian_ch + "H", f.read(2))[0] + # translation array length + bck_raw_info.ank1.transl_arr_length = struct.unpack(endian_ch + "H", f.read(2))[0] + + # offsets + + # bone animation data offset + bck_raw_info.ank1.anim_data_offset = struct.unpack(endian_ch + "I", f.read(4))[0] + if (bck_raw_info.ank1.anim_data_offset + + (bck_raw_info.ank1.bone_count * 9 * 6) > bck_raw_info.ank1.size): + return bck_error_str + "ank1 bone animation data offset" + # scale array offset + bck_raw_info.ank1.scale_arr_offset = struct.unpack(endian_ch + "I", f.read(4))[0] + if (bck_raw_info.ank1.scale_arr_offset + + (bck_raw_info.ank1.scale_arr_length * 4) > bck_raw_info.ank1.size): + return bck_error_str + "ank1 scale array offset" + # rotation array offset + bck_raw_info.ank1.rot_arr_offset = struct.unpack(endian_ch + "I", f.read(4))[0] + if (bck_raw_info.ank1.rot_arr_offset + + (bck_raw_info.ank1.rot_arr_length * 2) > bck_raw_info.ank1.size): + return bck_error_str + "ank1 rotation array offset" + # translation array offset + bck_raw_info.ank1.transl_arr_offset = struct.unpack(endian_ch + "I", f.read(4))[0] + if (bck_raw_info.ank1.transl_arr_offset + + (bck_raw_info.ank1.transl_arr_length * 4) > bck_raw_info.ank1.size): + return bck_error_str + "ank1 translation array offset" + + ######################################################################## + # refer to the offsets to read the animation data always (SMG does this) + + # bone animation data + f.seek(32 + bck_raw_info.ank1.anim_data_offset) + + # iterate over the bones + for i in range(bck_raw_info.ank1.bone_count): + # append a new item in the empty list + bck_raw_info.ank1.anim_data.append(smg_bck_raw.ank1.anim_data()) + # iterate over the animation components + for j in range(9): + bck_raw_info.ank1.anim_data[i].comp[j].keyframe_count = struct.unpack(endian_ch + "H", f.read(2))[0] + bck_raw_info.ank1.anim_data[i].comp[j].anim_data_index = struct.unpack(endian_ch + "H", f.read(2))[0] + bck_raw_info.ank1.anim_data[i].comp[j].interpolation_mode = struct.unpack(endian_ch + "H", f.read(2))[0] + # temporal shortcuts for the variables above + kf_count = bck_raw_info.ank1.anim_data[i].comp[j].keyframe_count + data_index = bck_raw_info.ank1.anim_data[i].comp[j].anim_data_index + interp_mode = bck_raw_info.ank1.anim_data[i].comp[j].interpolation_mode + + # store this file position + old_file_pos = f.tell() + + # check the interpolation mode and if nothing overflows + if (interp_mode > 1): + return bck_error_str + "ank1 interpolation mode" + + # variables to be used later + item_read_size = None + arr_offset = None + item_read_type = None + # scale or translation + if (j == 0 or j == 3 or j == 6): + item_read_size = 4 + arr_offset = bck_raw_info.ank1.scale_arr_offset + item_read_type = "f" + # rotation + elif (j == 1 or j == 4 or j == 7): + item_read_size = 2 + arr_offset = bck_raw_info.ank1.rot_arr_offset + item_read_type = "h" + # translation + elif (j == 2 or j == 5 or j == 8): + item_read_size = 4 + arr_offset = bck_raw_info.ank1.transl_arr_offset + item_read_type = "f" + + # how many items are read from the component arrays + # (depends on the interpolation mode and on the keyframe count) + number_of_items_per_kf = 1 + if (kf_count > 1 and interp_mode == 0): # soft interpolation + number_of_items_per_kf = 3 + elif (kf_count > 1 and interp_mode == 1): # custom interpolation + number_of_items_per_kf = 4 + + # check overflow + if (arr_offset + (item_read_size * data_index) + + (item_read_size * kf_count * number_of_items_per_kf) > bck_raw_info.ank1.size): + return bck_error_str + "ank1 anim data overflow" + + # read the respective arrays to check time consistency + old_time = None + cur_time = None + value = None + slope = None + + # go to that part of the file and read the respective animation data + f.seek(32 + arr_offset + (item_read_size * data_index)) + + # read the animation data + if (number_of_items_per_kf == 1): # single value read + # value + value = struct.unpack(endian_ch + item_read_type, f.read(item_read_size))[0] + else: # (3 or 4) * kf_count value reads + # iterate over the number of keyframes + for k in range(kf_count): + # time + cur_time = struct.unpack(endian_ch + item_read_type, f.read(item_read_size))[0] + if (old_time != None): + if (old_time >= cur_time): + return bck_error_str + "ank1 keyframe time" + old_time = cur_time + # value + value = struct.unpack(endian_ch + item_read_type, f.read(item_read_size))[0] + # slope + slope = struct.unpack(endian_ch + item_read_type, f.read(item_read_size))[0] + if (number_of_items_per_kf == 4): + slope = struct.unpack(endian_ch + item_read_type, f.read(item_read_size))[0] + + # return to the bone animation data table + f.seek(old_file_pos) + + # scale, rotation and translation arrays data + f.seek(32 + bck_raw_info.ank1.scale_arr_offset) + for i in range(bck_raw_info.ank1.scale_arr_length): + bck_raw_info.ank1.scale_arr.append(struct.unpack(endian_ch + "f", f.read(4))[0]) + f.seek(32 + bck_raw_info.ank1.rot_arr_offset) + for i in range(bck_raw_info.ank1.rot_arr_length): + bck_raw_info.ank1.rot_arr.append(struct.unpack(endian_ch + "h", f.read(2))[0]) + f.seek(32 + bck_raw_info.ank1.transl_arr_offset) + for i in range(bck_raw_info.ank1.transl_arr_length): + bck_raw_info.ank1.transl_arr.append(struct.unpack(endian_ch + "f", f.read(4))[0]) + + # finally done bruh + return bck_error_str + "all good" + +# check if a smg_bck_anim structure is good +def check_smg_bck_anim(anim): + + # check if the information in the smg_bck_anim struct is valid + + # the only stuff I can check is time consistency (frame -3 goes before frame 2) + # and that the lengths of the arrays are all good (also the variable types) + if (type(anim) != smg_bck_anim): + return bck_anim_error_str + "smg_bck_anim struct" + if(type(anim.anim_data) != list or len(anim.anim_data) != anim.bone_count): + return bck_anim_error_str + "number of bones or anim_data list" + + # iterate over the bones + for bone in anim.anim_data: + if (type(bone.comp) != list or len(bone.comp) != 9): + return bck_anim_error_str + "number of components or comp list" + + # iterate over the anim components + for comp in bone.comp: + + # check object types and integer data + if (type(comp) != smg_bck_anim.anim_data): + return bck_anim_error_str + "anim_data struct" + if (type(comp.kf_count) != int or comp.kf_count <= 0): + return bck_anim_error_str + "keyframe count" + if (type(comp.interp_mode) != int or (comp.kf_count != 0 and comp.kf_count != 1)): + return bck_anim_error_str + "interpolation mode" + if (type(comp.time) != list or len(comp.time) != comp.kf_count): + return bck_anim_error_str + "time list" + if (type(comp.values) != list or len(comp.values) != comp.kf_count): + return bck_anim_error_str + "values list" + if (type(comp.in_slope) != list or len(comp.in_slope) != comp.kf_count): + return bck_anim_error_str + "in_slope list" + if (type(comp.out_slope) != list or len(comp.out_slope) != comp.kf_count): + return bck_anim_error_str + "out_slope list" + + # check time consistency + for i in range(comp.kf_count): + if (i == 0): + continue + if (comp.time[i - 1] >= comp.time[i]): + return bck_anim_error_str + "time inconsistency" + + # all is good + return bck_anim_error_str + "all good" + +# create smg_bck_raw from smg_bck_anim +def create_smg_bck_raw(anim): + + # calls check_smg_bck_anim() + result = check_smg_bck_anim(anim) + print(result) + if (result != bck_anim_error_str + "all good"): + return None + + # build a new raw structure and return it + raw = smg_bck_raw() + raw.endian = "BIG" + + # header + raw.header.magic = "J3D1" + raw.header.ftype = "btp1" + raw.header.file_size = 32 # update later + raw.header.section_count = 1 + raw.header.unknown1 = [0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF] + + # find the file size (bytes) + # find if there can be merged tracks (same value animation tracks) + # find out the rot_lshift number + + # ank1 section + raw.ank1.magic = "ANK1" + raw.ank1.size = 36 # update at the end + raw.ank1.loop_mode = anim.loop_mode + raw.ank1.rot_lshift = 0 # update now + raw.ank1.anim_length = anim.anim_length + raw.ank1.bone_count = anim.bone_count + + # check all the rotation lists and get the average value + avg_angle_mag = 0 + angle_count = 0 + for i in range(anim.bone_count): + for j in range(3): + for k in range(len(anim.anim_data[i].comp[(j * 3) + 1].kf_count)): + max_angle_mag += abs(anim.anim_data[i].comp[(j * 3) + 1].values[k]) + angle_count += 1 + # calculate rot_lshift so that this value can be represented + # (shit can go crazy if this value is very large) + avg_angle_mag = avg_angle_mag / angle_count + raw.ank1.rot_lshift = int(math.ceil(math.log2(avg_angle_mag / math.pi))) + # ceil of it because I am forcing the average to be represented + # as 0x7FFF which cannot be done as rot_lshift needs to be an integer + + # start writing the animation data + + # iterate over the bones + for i in range(anim.bone_count): + # add a section for a bone + raw.ank1.anim_data.append(smg_bck_raw.anim_data()) + + # iterate over the animation components + for j in range(9): + # in case the same dataset can be found already + # written in one of the animation data arrays + match_found = True + arr = None + # scale + if (j == 0 or j == 3 or j == 6): + arr = raw.ank1.scale_arr + # rot + elif (j == 1 or j == 4 or j == 7): + arr = raw.ank1.rot_arr + # translate + elif (j == 2 or j == 5 or j == 8): + arr = raw.ank1.transl_arr + cur_index = len(arr) + + # keyframe_count, interpolation_mode + raw.ank1.anim_data[i].comp[j].keyframe_count = anim.anim_data[i].comp[j].kf_count + raw.ank1.anim_data[i].comp[j].interpolation_mode = anim.anim_data[i].comp[j].interp_mode + # number of items to write on each animation array + number_of_items = 1 + if (anim.anim_data[i].comp[j].kf_count > 1): + if (raw.ank1.anim_data[i].comp[j].interpolation_mode == 0): + number_of_items = 3 + elif (raw.ank1.anim_data[i].comp[j].interpolation_mode == 1): + number_of_items = 4 + + # check if it is a single keyframe value + if (number_of_items == 1): + # check if there is an equivalent value around the already written data + match_found = False + k = 0 + while (k < len(arr)): + if (anim.anim_data[i].comp[j].values[0] == arr[k]): + raw.ank1.anim_data[i].comp[j].anim_data_index = k + match_found = True + break + k += 1 + # something was found, index was already assigned + if (match_found == True): + continue + # otherwise, update the array + arr.append(anim.anim_data[i].comp[j].values[0]) + + # or if it is more + else: + # iterate over all the other values in the scale array to see if a match is found + k = 0 + while ((k + (anim.anim_data[i].comp[j].kf_count * number_of_items)) < len(arr)): + # check coincidence + l = 0 + match_found = True + while (l < anim.anim_data[i].comp[j].kf_count): + # check value equality + if ((anim.anim_data[i].comp[j].time[l] != arr[k + l + 0]) + or (anim.anim_data[i].comp[j].value[l] != arr[k + l + 1]) + or (anim.anim_data[i].comp[j].in_slope[l] != arr[k + l + 2])): + match_found = False + break + # interpolation mode == 1 + if ((anim.anim_data[i].comp[j].interp_mode == 1) + and (anim.anim_data[i].comp[j].out_slope[l] != arr[k + l + 3])): + match_found = False + break + l += number_of_items + # something was found + if (match_found == True): + raw.ank1.anim_data[i].comp[j].anim_data_index = k + break + # continue to next loop + k += 1 + # something was found, index was already assigned + if (match_found == True): + continue + # else append the new data + raw.ank1.anim_data[i].comp[j].anim_data_index = cur_index + # iterate over the frames and assign the scale values + k = 0 + while (k < anim.anim_data[i].comp[j].kf_count): + arr.append(anim.anim_data[i].comp[j].time[k]) + arr.append(anim.anim_data[i].comp[j].value[k]) + arr.append(anim.anim_data[i].comp[j].in_slope[k]) + if (anim.anim_data[i].comp[j].interp_mode == 1): + arr.append(anim.anim_data[i].comp[j].out_slope[k]) + k += 1 + + # update the animation arrays + # scale + if (j == 0 or j == 3 or j == 6): + raw.ank1.scale_arr = arr + # rot + elif (j == 1 or j == 4 or j == 7): + raw.ank1.rot_arr = arr + # translate + elif (j == 2 or j == 5 or j == 8): + raw.ank1.transl_arr = arr + + # assign these variables now + raw.ank1.scale_arr_length = len(raw.ank1.scale_arr) + raw.ank1.rot_arr_length = len(raw.ank1.rot_arr) + raw.ank1.transl_arr_length = len(raw.ank1.transl_arr) + raw.ank1.anim_data_offset = 0x40 + raw.ank1.scale_arr_offset = None + raw.ank1.rot_arr_offset = None + raw.ank1.transl_arr_offset = None + raw.ank1.anim_data = [] # list of length bone_count + raw.ank1.scale_arr = [] # list of length scale_arr_length + raw.ank1.rot_arr = [] # list of length rot_arr_length + raw.ank1.transl_arr = [] # list of length transl_arr_length + + return raw + +# write smg_bck_raw +def write_smg_bck_raw(raw, filepath, endian_ch): + + # assumes smg_bck_raw struct is correct so don't even + # attempt in making one yourself, use create_smg_bck_raw() + # use struct.pack() to write in different endian orders + # dont be crazy with it an assign the data tables to the "standard offsets" + + global f + f = open(filepath, "wb") + f.close() -- cgit v1.2.3-70-g09d2