Skip to content

Commit

Permalink
Merge pull request #3 from AlphaSatanOmega/main
Browse files Browse the repository at this point in the history
Updated Exporter - Auto FlatBuffers Conversion
  • Loading branch information
WistfulHopes authored Feb 16, 2024
2 parents ef172d2 + a97985d commit 07d9be9
Show file tree
Hide file tree
Showing 3 changed files with 348 additions and 10 deletions.
147 changes: 147 additions & 0 deletions Entities/MInfo_Converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import os
import subprocess
import re
import json
import sys
import shutil
import copy
import time
# GBFR Blender .json export to .minfo converter
# Version 3.0
# By AlphaSatanOmega - https://github.com/AlphaSatanOmega
# Drag and drop the original .minfo and the Blender export .json onto this .py file

# Convert flatc json data string to proper json data string with quotes
def preprocess_flatbuffers_json(json_data):
return re.sub(r'(\w+)(?=\s*:)', r'"\1"', json_data) # Use regular expression to wrap field names in quotes

def replace_mesh_info(flatc_json, blender_json):
# Load json data from files
flatc_json_data = json.loads(flatc_json)
blender_json_data = json.loads(blender_json)

# Replace the mesh info in the flatc json with the mesh info from the blender export json
keys_to_replace = ["MeshBuffers", "Chunks", "VertCount", "PolyCountX3", "BufferTypes"]
for lod_index in range(len(flatc_json_data["LODInfos"])):
for key in keys_to_replace:
flatc_json_data["LODInfos"][lod_index][key] = blender_json_data[key]
# Just set the LODInfos array to contain the Highest LOD
flatc_json_data["LODInfos"] = [flatc_json_data["LODInfos"][0]]

# Replace the BonesToWeightIndices list with the blender export list
flatc_json_data["BonesToWeightIndices"] = blender_json_data["BonesToWeightIndices"]

# Submeshes
# Get submesh names
blender_json_submesh_names = blender_json_data["SubMeshes"]
flatc_json_submesh_names = []
for flatc_submesh in flatc_json_data["SubMeshes"]:
flatc_json_submesh_names.append(flatc_submesh["Name"])
# If a submesh name from blender is not in the flatc submeshes,
# duplicate the last submesh and change its name to match
for blender_submesh_name in blender_json_submesh_names:
if blender_submesh_name not in flatc_json_submesh_names:
new_submesh = copy.deepcopy(flatc_json_data["SubMeshes"][-1])
new_submesh["Name"] = blender_submesh_name
flatc_json_data["SubMeshes"].append(new_submesh)

return json.dumps(flatc_json_data, indent=2) # Convert and return

def convert_minfo(flatc_path, minfo_path, blender_json_path):
print ("Start MInfo Conversion.")

if os.path.dirname(minfo_path) != os.path.dirname(blender_json_path):
raise Exception("\n\nERROR: A copy of the .minfo needs to be in same location as you are exporting to.")

script_dir = os.path.dirname(__file__) # Get the script directory
export_dir = os.path.dirname(blender_json_path) # Get blender export directory
flatc_temp_dir = os.path.join(export_dir, "_flatc_temp")
minfo_fbs_path = os.path.join(script_dir,"MInfo_ModelInfo.fbs") # Get the FlatBuffers Schema
# flatc_path = os.path.join(script_dir, "flatc.exe") # Get the path to flatc.exe in the same directory
model_name = os.path.splitext(os.path.basename(minfo_path))[0] # Get the model name from the minfo

# Generate json from .minfo file using flatc.exe

print(flatc_path, "-o", f"{flatc_temp_dir}", "--json", f"{minfo_fbs_path}", "--", f"{minfo_path}", "--raw-binary")

command = [flatc_path, "-o", f"{flatc_temp_dir}", "--json", f"{minfo_fbs_path}", "--", f"{minfo_path}", "--raw-binary"]
subprocess.run(command, check=True)
# flatc json gets stored to a temp folder
flatc_json_path = os.path.join(flatc_temp_dir, f"{model_name}.json")
print(f"Generated: {flatc_json_path}")

# Open the json files
with open(flatc_json_path, 'r') as flatc_file, open(blender_json_path, 'r') as blender_file:
flatc_json = flatc_file.read()
blender_json = blender_file.read()
flatc_json = preprocess_flatbuffers_json(flatc_json) # Fix flatc json

# Replace the mesh info of flatc json with blender export json's mesh info
modified_flatc_json = replace_mesh_info(flatc_json, blender_json)
# print(modified_flatc_json)
# Save modified flatc to a file in the same directory as the script
# os.path.join(export_dir, f"{model_name}.json")
modified_flatc_json_file = blender_json_path # Overwrite blender json file
with open(modified_flatc_json_file, 'w') as file:
file.write(modified_flatc_json)
print(f"Replaced mesh info in {flatc_json_path} with mesh info from {minfo_path}")

# Create output directory next to original .minfo
output_dir = os.path.join(os.path.dirname(minfo_path), "_Exported_MInfo")
os.makedirs(output_dir, exist_ok=True)

# Run flatc.exe to generate binary minfo from the modified json
command = [flatc_path, "-o", f"{flatc_temp_dir}", "--binary", f"{minfo_fbs_path}", modified_flatc_json_file]
subprocess.run(command, check=True)
# Rename the .bin otuput to .minfo
binary_output_file = os.path.join(flatc_temp_dir, f"{model_name}.bin")
minfo_output_file = binary_output_file.replace(".bin", '.minfo')
os.rename(binary_output_file, minfo_output_file)
print(f"Modified {minfo_output_file} generated.")
# Move minfo to output_dir
try: os.remove(os.path.join(output_dir, f"{model_name}.minfo")) # Remove copy if exists
except: print(f"No copy of {model_name}.minfo found in {output_dir}, moving.")
shutil.move(minfo_output_file, output_dir)

# Move all the Blender export files into the output_dir
blender_export_file_exts = [".mmesh", ".skeleton", ".json"]
for file_ext in blender_export_file_exts:
original_file_path = os.path.join(export_dir, f"{model_name}{file_ext}")
try: os.remove(os.path.join(output_dir, f"{model_name}{file_ext}")) # Remove copy if exists
except Exception as e:
print(str(e))
print(f"No copy of {model_name}{file_ext} found in {output_dir}, moving.")
shutil.move(original_file_path, output_dir)

# Remove _flatc_temp safely
os.remove(os.path.join(flatc_temp_dir, f"{model_name}.json")) # Remove json first
os.rmdir(flatc_temp_dir) # Then delete the folder since it should be empty, fails otherwise
print(f"Removed {flatc_temp_dir}")

print(f"Modified JSON and binary files moved to: {output_dir}")


def main():
input_file_1 = sys.argv[1]
input_file_2 = sys.argv[2]
# Check which file is the .minfo and which is the .json
if input_file_1.lower().endswith('.minfo') and input_file_2.lower().endswith('.json'):
minfo_path = input_file_1
blender_json_path = input_file_2
elif input_file_1.lower().endswith('.json') and input_file_2.lower().endswith('.minfo'):
minfo_path = input_file_2
blender_json_path = input_file_1
else:
print("Error: Incorrect files input. The inputs should be an .minfo and a .json file.")
# print(minfo_path, blender_json_path)

# Process the files
try:
convert_minfo(minfo_path, blender_json_path)
print("\nConversion Complete!")
except Exception as e:
print(str(e))

if __name__ == "__main__":
main()
input("\nPress any key to exit...")
118 changes: 118 additions & 0 deletions Entities/MInfo_ModelInfo.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// all FlatSharp FBS attributes start with the 'fs_' prefix.
attribute "fs_serializer";

namespace GBFRDataTools.Entities;

// Main Root Table
table ModelInfo (fs_serializer) {
Magic:uint; // 0x134B249
LODInfos:[LODInfo];
LODInfos2:[LODInfo];
A4:[float];
SubMeshes:[SubMeshInfo];
Materials:[Material];
BonesToWeightIndices:[ushort];
DeformBoneBoundaryBox:[BoundaryBox];
A9:Vec4;
A10:ModelInfo_A10; // Used very rarely in bgXXXX files
A11:ModelInfo_A11;
A12:float;
A13:float;
A14:float;
A15:float;
A16:float;
A17:float;
A18:float;
A19:float;
A20:float;
A21:byte;
A22:byte;
// Many of these may just be bools
A23:byte;
A24:byte;
A25:byte;
A26:byte;
A27:byte;
A28:byte;
A29:byte;
A30:byte;
A31:byte;
A32:byte;
}

table LODInfo
{
MeshBuffers:[MeshBufferLocator];
Chunks:[LODChunk];
VertCount:int;
PolyCountX3:int;
BufferTypes:byte;
A6:byte;
}

table SubMeshInfo
{
Name:string;
BBox:BoundaryBox;
}

table Material
{
Hash:uint;
Unk:byte;
}

table ModelInfo_A10
{
UnkID:uint; // Not always present
A2:float;
A3:byte;
A4:byte;
}

/////////////////
// Util structs
/////////////////
struct MeshBufferLocator
{
Offset:ulong;
Size:ulong;
}

struct LODChunk
{
Offset:int;
Count:int;
SubMeshID:byte;
MaterialID:byte;
Unk1:byte;
Unk2:byte;
}

struct Vec3 {
x:float;
y:float;
z:float;
}

struct Vec4 {
x:float;
y:float;
z:float;
r:float;
}

struct BoundaryBox {
Min:Vec3;
Max:Vec3;
}

struct ModelInfo_A11
{
// Maybe just 7 floats instead
A1:int;
A2:Vec3;
A3:Vec3;
}

root_type ModelInfo;
Loading

0 comments on commit 07d9be9

Please sign in to comment.