From 70e647076418d114111aa76b5d3639a5b4271e94 Mon Sep 17 00:00:00 2001 From: Owl Date: Fri, 26 Sep 2025 14:32:34 -0400 Subject: bcsv and other stuff --- bck_funcs.py | 167 +++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 94 insertions(+), 73 deletions(-) (limited to 'bck_funcs.py') diff --git a/bck_funcs.py b/bck_funcs.py index e8482f8..73fd9de 100644 --- a/bck_funcs.py +++ b/bck_funcs.py @@ -1,5 +1,6 @@ -import os, struct, math +import struct, math from . import smg_common +from . import file_ops # 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 @@ -41,11 +42,11 @@ class smg_bck_raw: 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 += " 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 @@ -72,26 +73,26 @@ class smg_bck_raw: 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" + 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: kf count, data index, interp mode\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 += " 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 @@ -109,15 +110,15 @@ class smg_bck_raw: 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__()) + 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 @@ -143,12 +144,12 @@ class smg_bck_anim: 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" + 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 += " Bone %d\n" % (i) rtn += "%s" % (self.anim_data[i].__str__()) rtn += "### SMG_BCK_ANIM END" return rtn @@ -167,15 +168,15 @@ class smg_bck_anim: 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__()) + 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 @@ -190,10 +191,10 @@ class smg_bck_anim: 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__()) + 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 @@ -201,7 +202,6 @@ class smg_bck_anim: bck_raw_info = None bck_raw_error_str = "bck-raw-error: " bck_anim_error_str = "bck-anim-error: " -pad_str = "hoot" f = None # main function @@ -286,7 +286,8 @@ def read_bck_file(filepath): def pre_read_bck_file(filepath): # check its size first - if (os.path.getsize(filepath) <= 32): + file_size = file_ops.get_file_size(filepath) + if (file_size <= 32): return bck_raw_error_str + "file size" # make global variables editable @@ -325,7 +326,7 @@ def pre_read_bck_file(filepath): # 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)): + if (bck_raw_info.header.file_size != file_ops.get_file_size(filepath)): return bck_raw_error_str + "file size" # section count @@ -375,23 +376,23 @@ def pre_read_bck_file(filepath): # 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): + if ((bck_raw_info.ank1.anim_data_offset + (bck_raw_info.ank1.bone_count * 9 * 6) > bck_raw_info.ank1.size) + or (bck_raw_info.ank1.anim_data_offset % 4 != 0)): return bck_raw_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): + if ((bck_raw_info.ank1.scale_arr_offset + (bck_raw_info.ank1.scale_arr_length * 4) > bck_raw_info.ank1.size) + or (bck_raw_info.ank1.scale_arr_offset % 4 != 0)): return bck_raw_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): + if ((bck_raw_info.ank1.rot_arr_offset + (bck_raw_info.ank1.rot_arr_length * 2) > bck_raw_info.ank1.size) + or (bck_raw_info.ank1.rot_arr_offset % 4 != 0)): return bck_raw_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): + if ((bck_raw_info.ank1.transl_arr_offset + (bck_raw_info.ank1.transl_arr_length * 4) > bck_raw_info.ank1.size) + or (bck_raw_info.ank1.transl_arr_offset % 4 != 0)): return bck_raw_error_str + "ank1 translation array offset" # refer to the offsets to read the animation data always (SMG does this) @@ -550,7 +551,7 @@ def check_smg_bck_anim(anim): # assumes angles are in radians # and that the timings between the keyframes are t0 = 0 and tf = 1 # (cubic hermite spline) -def create_smg_bck_raw(anim): +def create_smg_bck_raw(anim, use_std_pad_size): # calls check_smg_bck_anim() result = check_smg_bck_anim(anim) @@ -716,21 +717,26 @@ def create_smg_bck_raw(anim): raw.ank1.transl_arr = arr # assign these variables now - # dont be crazy with it an assign the data tables to the "standard offsets" 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) pad_str = smg_common.padding() - raw.ank1.anim_data_offset = 0x40 # yes + + # decide to use standard/compact padding sizes + pad_byte_align = 32 + raw.ank1.anim_data_offset = 0x40 + if (use_std_pad_size == False): + pad_byte_align = 4 + raw.ank1.anim_data_offset = 0x24 raw.ank1.scale_arr_offset = raw.ank1.anim_data_offset + (raw.ank1.bone_count * 9 * 6) - raw.ank1.scale_arr_offset += len(pad_str.string_fill(32, raw.ank1.scale_arr_offset)) + raw.ank1.scale_arr_offset += len(pad_str.string_fill(pad_byte_align, raw.ank1.scale_arr_offset)) raw.ank1.rot_arr_offset = raw.ank1.scale_arr_offset + (raw.ank1.scale_arr_length * 4) - raw.ank1.rot_arr_offset += len(pad_str.string_fill(32, raw.ank1.rot_arr_offset)) + raw.ank1.rot_arr_offset += len(pad_str.string_fill(pad_byte_align, raw.ank1.rot_arr_offset)) raw.ank1.transl_arr_offset = raw.ank1.rot_arr_offset + (raw.ank1.rot_arr_length * 2) - raw.ank1.transl_arr_offset += len(pad_str.string_fill(32, raw.ank1.transl_arr_offset)) + raw.ank1.transl_arr_offset += len(pad_str.string_fill(pad_byte_align, raw.ank1.transl_arr_offset)) # section size and file size raw.ank1.size = raw.ank1.transl_arr_offset + (raw.ank1.transl_arr_length * 4) - raw.ank1.size += len(pad_str.string_fill(32, raw.ank1.size)) + raw.ank1.size += len(pad_str.string_fill(pad_byte_align, raw.ank1.size)) raw.header.file_size = 32 + raw.ank1.size # done! @@ -766,18 +772,27 @@ def write_smg_bck_raw(raw, filepath, endian_ch): f.write(struct.pack(endian_ch + "I", raw.ank1.rot_arr_offset)) f.write(struct.pack(endian_ch + "I", raw.ank1.transl_arr_offset)) pad = smg_common.padding() - f.write(pad.string_fill(32, 0x24)) + i = 0 + while (f.tell() - 32 != raw.ank1.anim_data_offset): + f.write(pad.string[i].encode("ascii")) + i += 1 # anim data for i in range(raw.ank1.bone_count): for j in range(9): f.write(struct.pack(endian_ch + "H", raw.ank1.anim_data[i].comp[j].keyframe_count)) f.write(struct.pack(endian_ch + "H", raw.ank1.anim_data[i].comp[j].anim_data_index)) f.write(struct.pack(endian_ch + "H", raw.ank1.anim_data[i].comp[j].interpolation_mode)) - f.write(pad.string_fill(32, raw.ank1.anim_data_offset + (raw.ank1.bone_count * 9 * 6))) + i = 0 + while (f.tell() - 32 != raw.ank1.scale_arr_offset): + f.write(pad.string[i].encode("ascii")) + i += 1 # scale array for i in range(raw.ank1.scale_arr_length): f.write(struct.pack(endian_ch + "f", raw.ank1.scale_arr[i])) - f.write(pad.string_fill(32, raw.ank1.scale_arr_offset + (raw.ank1.scale_arr_length * 4))) + i = 0 + while (f.tell() - 32 != raw.ank1.rot_arr_offset): + f.write(pad.string[i].encode("ascii")) + i += 1 # rotation array for i in range(raw.ank1.rot_arr_length): # check if the slope is larger in magnitude than the max representation container @@ -788,11 +803,17 @@ def write_smg_bck_raw(raw, filepath, endian_ch): elif (value < -0x7FFF): value = -0x7FFF f.write(struct.pack(endian_ch + "h", value)) - f.write(pad.string_fill(32, raw.ank1.rot_arr_offset + (raw.ank1.rot_arr_length * 2))) + i = 0 + while (f.tell() - 32 != raw.ank1.transl_arr_offset): + f.write(pad.string[i].encode("ascii")) + i += 1 # translation array for i in range(raw.ank1.transl_arr_length): f.write(struct.pack(endian_ch + "f", raw.ank1.transl_arr[i])) - f.write(pad.string_fill(32, raw.ank1.transl_arr_offset + (raw.ank1.transl_arr_length * 4))) + i = 0 + while (f.tell() != raw.header.file_size): + f.write(pad.string[i].encode("ascii")) + i += 1 # done! f.close() -- cgit v1.2.3-70-g09d2