2023-08-20 22:10:09 -06:00
import struct
import click
import sys
import os
import re
import subprocess
# Enum mappings
cCcD_ObjAtType = {
0x00000002 : " AT_TYPE_NORMAL_SWORD " ,
0x00000004 : " AT_TYPE_HORSE " ,
0x00000008 : " AT_TYPE_THROW_OBJ " ,
0x00000010 : " AT_TYPE_SHIELD_ATTACK " ,
0x00000020 : " AT_TYPE_BOMB " ,
0x00000040 : " AT_TYPE_40 " ,
0x00000080 : " AT_TYPE_SLINGSHOT " ,
0x00000200 : " AT_TYPE_LANTERN_SWING " ,
0x00000400 : " AT_TYPE_CSTATUE_SWING " ,
0x00000800 : " AT_TYPE_800 " ,
0x00001000 : " AT_TYPE_1000 " ,
0x00002000 : " AT_TYPE_ARROW " ,
0x00004000 : " AT_TYPE_HOOKSHOT " ,
0x00010000 : " AT_TYPE_BOOMERANG " ,
0x00040000 : " AT_TYPE_40000 " ,
0x00080000 : " AT_TYPE_SPINNER " ,
0x00100000 : " AT_TYPE_CSTATUE_BOSS_SWING " ,
0x00200000 : " AT_TYPE_HEAVY_BOOTS " ,
0x00400000 : " AT_TYPE_IRON_BALL " ,
0x00800000 : " AT_TYPE_COPY_ROD " ,
0x01000000 : " AT_TYPE_1000000 " ,
0x04000000 : " AT_TYPE_MASTER_SWORD " ,
0x08000000 : " AT_TYPE_MIDNA_LOCK " ,
0x10000000 : " AT_TYPE_10000000 " ,
0x40000000 : " AT_TYPE_WOLF_CUT_TURN " ,
0x80000000 : " AT_TYPE_WOLF_ATTACK " ,
0xD8000000 : " AT_TYPE_UNK "
}
dCcD_hitSe = {
0x0 : " dCcD_SE_NONE " ,
0x1 : " dCcD_SE_SWORD " ,
0x2 : " dCcD_SE_SHIELD_ATTACK " ,
0x3 : " dCcD_SE_WOLF_KICK " ,
0x4 : " dCcD_SE_WOLF_BITE " ,
0x5 : " dCcD_SE_WOOD " ,
0x6 : " dCcD_SE_HAMMER " ,
0x7 : " dCcD_SE_ARROW_STICK " ,
0x8 : " dCcD_SE_HOOKSHOT_STICK " ,
0x9 : " dCcD_SE_METAL " ,
0xA : " dCcD_SE_STONE " ,
0xB : " dCcD_SE_HARD_BODY " ,
0xC : " dCcD_SE_SOFT_BODY " ,
0xD : " dCcD_SE_13 " ,
0xE : " dCcD_SE_SWORD_STAB " ,
0xF : " dCcD_SE_15 " ,
0x10 : " dCcD_SE_SWORD_RUN " ,
0x11 : " dCcD_SE_17 " ,
0x12 : " dCcD_SE_THROW_OBJ " ,
0x13 : " dCcD_SE_SPINNER " ,
0x14 : " dCcD_SE_COPYROD " ,
0x15 : " dCcD_SE_STATUE_HAMMER " ,
0x16 : " dCcD_SE_PACHINKO " ,
0x17 : " dCcD_SE_23 "
}
def unpack_cylinder_data ( data ) :
# Verify data length is correct
if len ( data ) != 68 :
print ( " Invalid data length for cylinder: %d " % len ( data ) )
print ( " Cylinder data (dCcD_SrcCyl) should be 68 bytes long. " )
return None
# dCcD_SrcGObjInf
mFlags , = struct . unpack ( ' >I ' , bytes ( data [ : 4 ] ) )
mObjAt_mType , mObjAt_mAtp , mObjAt_mBase = struct . unpack ( ' >lBxxxl ' , bytes ( data [ 4 : 16 ] ) )
mObjTg_mType , mObjTg_mBase = struct . unpack ( ' >Il ' , bytes ( data [ 16 : 24 ] ) )
mObjCo_mBase , = struct . unpack ( ' >l ' , bytes ( data [ 24 : 28 ] ) )
mGObjAt_mSe , mGObjAt_mHitMark , mGObjAt_mSpl , mGObjAt_mMtrl , mGObjAt_mGFlag = struct . unpack ( ' >4BI ' , bytes ( data [ 28 : 36 ] ) )
mGObjTg_mSe , mGObjTg_mHitMark , mGObjTg_mSpl , mGObjTg_mMtrl , mGObjTg_mGFlag = struct . unpack ( ' >4BI ' , bytes ( data [ 36 : 44 ] ) )
mGObjCo_mGFlag , = struct . unpack ( ' >I ' , bytes ( data [ 44 : 48 ] ) )
# Swap in enums for known values
mObjAt_mType = cCcD_ObjAtType . get ( mObjAt_mType , hex ( mObjAt_mType ) )
mGObjAt_mSe = dCcD_hitSe . get ( mGObjAt_mSe , hex ( mGObjAt_mSe ) )
mGObjTg_mSe = dCcD_hitSe . get ( mGObjTg_mSe , hex ( mGObjTg_mSe ) )
# cM3dGCylS
mCenter_x , mCenter_y , mCenter_z , mRadius , mHeight = struct . unpack ( ' >3f2f ' , bytes ( data [ 48 : 68 ] ) )
return {
" mFlags " : mFlags ,
" mObjAt " : ( mObjAt_mType , mObjAt_mAtp , mObjAt_mBase ) ,
" mObjTg " : ( mObjTg_mType , mObjTg_mBase ) ,
" mObjCo " : mObjCo_mBase ,
" mGObjAt " : ( mGObjAt_mSe , mGObjAt_mHitMark , mGObjAt_mSpl , mGObjAt_mMtrl , mGObjAt_mGFlag ) ,
" mGObjTg " : ( mGObjTg_mSe , mGObjTg_mHitMark , mGObjTg_mSpl , mGObjTg_mMtrl , mGObjTg_mGFlag ) ,
" mGObjCo " : mGObjCo_mGFlag ,
" mCenter " : ( mCenter_x , mCenter_y , mCenter_z ) ,
" mRadius " : mRadius ,
" mHeight " : mHeight
}
def unpack_sphere_data ( data ) :
# Verify data length is correct
if len ( data ) != 64 :
print ( " Invalid data length for sphere: %d " % len ( data ) )
print ( " Sphere data (dCcD_SrcSph) should be 64 bytes long. " )
return None
# dCcD_SrcGObjInf
mFlags , = struct . unpack ( ' >I ' , bytes ( data [ : 4 ] ) )
mObjAt_mType , mObjAt_mAtp , mObjAt_mBase = struct . unpack ( ' >lBxxxl ' , bytes ( data [ 4 : 16 ] ) )
mObjTg_mType , mObjTg_mBase = struct . unpack ( ' >Il ' , bytes ( data [ 16 : 24 ] ) )
mObjCo_mBase , = struct . unpack ( ' >l ' , bytes ( data [ 24 : 28 ] ) )
mGObjAt_mSe , mGObjAt_mHitMark , mGObjAt_mSpl , mGObjAt_mMtrl , mGObjAt_mGFlag = struct . unpack ( ' >4BI ' , bytes ( data [ 28 : 36 ] ) )
mGObjTg_mSe , mGObjTg_mHitMark , mGObjTg_mSpl , mGObjTg_mMtrl , mGObjTg_mGFlag = struct . unpack ( ' >4BI ' , bytes ( data [ 36 : 44 ] ) )
mGObjCo_mGFlag , = struct . unpack ( ' >I ' , bytes ( data [ 44 : 48 ] ) )
# Swap in enums for known values
mObjAt_mType = cCcD_ObjAtType . get ( mObjAt_mType , hex ( mObjAt_mType ) )
mGObjAt_mSe = dCcD_hitSe . get ( mGObjAt_mSe , hex ( mGObjAt_mSe ) )
mGObjTg_mSe = dCcD_hitSe . get ( mGObjTg_mSe , hex ( mGObjTg_mSe ) )
# cM3dGSphS
mCenter_x , mCenter_y , mCenter_z , mRadius = struct . unpack ( ' >3f1f ' , bytes ( data [ 48 : 64 ] ) )
return {
" mFlags " : mFlags ,
" mObjAt " : ( mObjAt_mType , mObjAt_mAtp , mObjAt_mBase ) ,
" mObjTg " : ( mObjTg_mType , mObjTg_mBase ) ,
" mObjCo " : mObjCo_mBase ,
" mGObjAt " : ( mGObjAt_mSe , mGObjAt_mHitMark , mGObjAt_mSpl , mGObjAt_mMtrl , mGObjAt_mGFlag ) ,
" mGObjTg " : ( mGObjTg_mSe , mGObjTg_mHitMark , mGObjTg_mSpl , mGObjTg_mMtrl , mGObjTg_mGFlag ) ,
" mGObjCo " : mGObjCo_mGFlag ,
" mCenter " : ( mCenter_x , mCenter_y , mCenter_z ) ,
" mRadius " : mRadius
}
def unpack_triangle_data ( data ) :
# Verify data length is correct
if len ( data ) != 84 :
print ( " Invalid data length for triangle: %d " % len ( data ) )
print ( " Triangle data (dCcD_SrcTri) should be 84 bytes long. " )
return None
# dCcD_SrcGObjInf
mFlags , = struct . unpack ( ' >I ' , bytes ( data [ : 4 ] ) )
mObjAt_mType , mObjAt_mAtp , mObjAt_mBase = struct . unpack ( ' >lBxxxl ' , bytes ( data [ 4 : 16 ] ) )
mObjTg_mType , mObjTg_mBase = struct . unpack ( ' >Il ' , bytes ( data [ 16 : 24 ] ) )
mObjCo_mBase , = struct . unpack ( ' >l ' , bytes ( data [ 24 : 28 ] ) )
mGObjAt_mSe , mGObjAt_mHitMark , mGObjAt_mSpl , mGObjAt_mMtrl , mGObjAt_mGFlag = struct . unpack ( ' >4BI ' , bytes ( data [ 28 : 36 ] ) )
mGObjTg_mSe , mGObjTg_mHitMark , mGObjTg_mSpl , mGObjTg_mMtrl , mGObjTg_mGFlag = struct . unpack ( ' >4BI ' , bytes ( data [ 36 : 44 ] ) )
mGObjCo_mGFlag , = struct . unpack ( ' >I ' , bytes ( data [ 44 : 48 ] ) )
# Swap in enums for known values
mObjAt_mType = cCcD_ObjAtType . get ( mObjAt_mType , hex ( mObjAt_mType ) )
mGObjAt_mSe = dCcD_hitSe . get ( mGObjAt_mSe , hex ( mGObjAt_mSe ) )
mGObjTg_mSe = dCcD_hitSe . get ( mGObjTg_mSe , hex ( mGObjTg_mSe ) )
# cM3dGTriS
a_x , a_y , a_z , b_x , b_y , b_z , c_x , c_y , c_z = struct . unpack ( ' >3f3f3f ' , bytes ( data [ 48 : 84 ] ) )
return {
" mFlags " : mFlags ,
" mObjAt " : ( mObjAt_mType , mObjAt_mAtp , mObjAt_mBase ) ,
" mObjTg " : ( mObjTg_mType , mObjTg_mBase ) ,
" mObjCo " : mObjCo_mBase ,
" mGObjAt " : ( mGObjAt_mSe , mGObjAt_mHitMark , mGObjAt_mSpl , mGObjAt_mMtrl , mGObjAt_mGFlag ) ,
" mGObjTg " : ( mGObjTg_mSe , mGObjTg_mHitMark , mGObjTg_mSpl , mGObjTg_mMtrl , mGObjTg_mGFlag ) ,
" mGObjCo " : mGObjCo_mGFlag ,
" a " : ( a_x , a_y , a_z ) ,
" b " : ( b_x , b_y , b_z ) ,
" c " : ( c_x , c_y , c_z ) ,
}
def unpack_capsule_data ( data ) :
# Verify data length is correct
if len ( data ) != 76 :
print ( " Invalid data length for capsule: %d " % len ( data ) )
print ( " Capsule data (dCcD_SrcCps) should be 76 bytes long. " )
return None
# dCcD_SrcGObjInf
mFlags , = struct . unpack ( ' >I ' , bytes ( data [ : 4 ] ) )
mObjAt_mType , mObjAt_mAtp , mObjAt_mBase = struct . unpack ( ' >lBxxxl ' , bytes ( data [ 4 : 16 ] ) )
mObjTg_mType , mObjTg_mBase = struct . unpack ( ' >Il ' , bytes ( data [ 16 : 24 ] ) )
mObjCo_mBase , = struct . unpack ( ' >l ' , bytes ( data [ 24 : 28 ] ) )
mGObjAt_mSe , mGObjAt_mHitMark , mGObjAt_mSpl , mGObjAt_mMtrl , mGObjAt_mGFlag = struct . unpack ( ' >4BI ' , bytes ( data [ 28 : 36 ] ) )
mGObjTg_mSe , mGObjTg_mHitMark , mGObjTg_mSpl , mGObjTg_mMtrl , mGObjTg_mGFlag = struct . unpack ( ' >4BI ' , bytes ( data [ 36 : 44 ] ) )
mGObjCo_mGFlag , = struct . unpack ( ' >I ' , bytes ( data [ 44 : 48 ] ) )
# Swap in enums for known values
mObjAt_mType = cCcD_ObjAtType . get ( mObjAt_mType , hex ( mObjAt_mType ) )
mGObjAt_mSe = dCcD_hitSe . get ( mGObjAt_mSe , hex ( mGObjAt_mSe ) )
mGObjTg_mSe = dCcD_hitSe . get ( mGObjTg_mSe , hex ( mGObjTg_mSe ) )
# cM3dGTriS
mStart_x , mStart_y , mStart_z , mEnd_x , mEnd_y , mEnd_z , mRadius = struct . unpack ( ' >3f3f1f ' , bytes ( data [ 48 : 76 ] ) )
return {
" mFlags " : mFlags ,
" mObjAt " : ( mObjAt_mType , mObjAt_mAtp , mObjAt_mBase ) ,
" mObjTg " : ( mObjTg_mType , mObjTg_mBase ) ,
" mObjCo " : mObjCo_mBase ,
" mGObjAt " : ( mGObjAt_mSe , mGObjAt_mHitMark , mGObjAt_mSpl , mGObjAt_mMtrl , mGObjAt_mGFlag ) ,
" mGObjTg " : ( mGObjTg_mSe , mGObjTg_mHitMark , mGObjTg_mSpl , mGObjTg_mMtrl , mGObjTg_mGFlag ) ,
" mGObjCo " : mGObjCo_mGFlag ,
" mStart " : ( mStart_x , mStart_y , mStart_z ) ,
" mEnd " : ( mEnd_x , mEnd_y , mEnd_z ) ,
" mRadius " : mRadius ,
}
# Fetch the byte data from the file
def fetch_shape_data ( filename , variable_name ) :
with open ( filename , ' r ' ) as f :
lines = f . readlines ( )
# Find the start and end of the data
start_index = None
end_index = None
section_rodata_present = False
section_data_present = False
for i , line in enumerate ( lines ) :
if variable_name in line :
start_index = i
if ' SECTION_RODATA ' in line :
section_rodata_present = True
elif ' SECTION_DATA ' in line :
section_data_present = True
if start_index is not None and ' }; ' in line :
end_index = i
break
if start_index is None or end_index is None :
print ( f " { variable_name } data not found in the file. " )
return None
elif not section_rodata_present and not section_data_present :
print ( f " { variable_name } already setup! " )
return None
# Extract the byte data
byte_data = [ ]
for line in lines [ start_index + 1 : end_index ] :
byte_values = line . strip ( ) . split ( ' , ' )
for value in byte_values :
if value :
byte_data . append ( int ( value , 16 ) )
return ( {
" data " : byte_data ,
" section_rodata_present " : section_rodata_present ,
" start_index " : start_index ,
" end_index " : end_index
} )
# Write the unpacked data back to the file
def write_cylinder_shape_data ( filename , data , variable_name , section_rodata_present , start_index , end_index ) :
with open ( filename , ' r ' ) as f :
lines = f . readlines ( )
# Formatting the output
const_keyword = " const " if section_rodata_present else " "
formatted_output = [ ]
formatted_output . append ( f " { const_keyword } static dCcD_SrcCyl { variable_name } = {{ \n " )
formatted_output . append ( " { \n " )
# cCcD_SrcObj members
formatted_output . append ( f " {{ { hex ( data [ ' mFlags ' ] ) } , {{ {{ { data [ ' mObjAt ' ] [ 0 ] } , { hex ( data [ ' mObjAt ' ] [ 1 ] ) } , { hex ( data [ ' mObjAt ' ] [ 2 ] ) } }} , {{ { hex ( data [ ' mObjTg ' ] [ 0 ] ) } , { hex ( data [ ' mObjTg ' ] [ 1 ] ) } }} , { hex ( data [ ' mObjCo ' ] ) } }} }} , // mObj \n " )
# dCcD_SrcGObjAt members
formatted_output . append ( f " {{ { data [ ' mGObjAt ' ] [ 0 ] } , { hex ( data [ ' mGObjAt ' ] [ 1 ] ) } , { hex ( data [ ' mGObjAt ' ] [ 2 ] ) } , { hex ( data [ ' mGObjAt ' ] [ 3 ] ) } , { hex ( data [ ' mGObjAt ' ] [ 4 ] ) } }} , // mGObjAt \n " )
# dCcD_SrcGObjTg members
formatted_output . append ( f " {{ { data [ ' mGObjTg ' ] [ 0 ] } , { hex ( data [ ' mGObjTg ' ] [ 1 ] ) } , { hex ( data [ ' mGObjTg ' ] [ 2 ] ) } , { hex ( data [ ' mGObjTg ' ] [ 3 ] ) } , { hex ( data [ ' mGObjTg ' ] [ 4 ] ) } }} , // mGObjTg \n " )
# dCcD_SrcGObjCo members
formatted_output . append ( f " {{ { hex ( data [ ' mGObjCo ' ] ) } }} , // mGObjCo \n " )
formatted_output . append ( " }, // mObjInf \n " )
# cM3dGCylS members
formatted_output . append ( " { \n " )
formatted_output . append ( f " {{ { data [ ' mCenter ' ] [ 0 ] } f, { data [ ' mCenter ' ] [ 1 ] } f, { data [ ' mCenter ' ] [ 2 ] } f }} , // mCenter \n " )
formatted_output . append ( f " { data [ ' mRadius ' ] } f, // mRadius \n " )
formatted_output . append ( f " { data [ ' mHeight ' ] } f // mHeight \n " )
formatted_output . append ( " } // mCyl \n " )
formatted_output . append ( " }; \n " )
# Remove the compiler strip gate macro if it was present
if ' COMPILER_STRIP_GATE ' in lines [ end_index + 1 ] :
del lines [ end_index + 1 ]
# Replace the original section with the new formatted output
lines [ start_index : end_index + 1 ] = formatted_output
# Write the modified lines back to the file
with open ( filename , ' w ' ) as f :
f . writelines ( lines )
# Write the unpacked data back to the file
def write_sphere_shape_data ( filename , data , variable_name , section_rodata_present , start_index , end_index ) :
with open ( filename , ' r ' ) as f :
lines = f . readlines ( )
# Formatting the output
const_keyword = " const " if section_rodata_present else " "
formatted_output = [ ]
formatted_output . append ( f " { const_keyword } static dCcD_SrcSph { variable_name } = {{ \n " )
formatted_output . append ( " { \n " )
# cCcD_SrcObj members
formatted_output . append ( f " {{ { hex ( data [ ' mFlags ' ] ) } , {{ {{ { data [ ' mObjAt ' ] [ 0 ] } , { hex ( data [ ' mObjAt ' ] [ 1 ] ) } , { hex ( data [ ' mObjAt ' ] [ 2 ] ) } }} , {{ { hex ( data [ ' mObjTg ' ] [ 0 ] ) } , { hex ( data [ ' mObjTg ' ] [ 1 ] ) } }} , { hex ( data [ ' mObjCo ' ] ) } }} }} , // mObj \n " )
# dCcD_SrcGObjAt members
formatted_output . append ( f " {{ { data [ ' mGObjAt ' ] [ 0 ] } , { hex ( data [ ' mGObjAt ' ] [ 1 ] ) } , { hex ( data [ ' mGObjAt ' ] [ 2 ] ) } , { hex ( data [ ' mGObjAt ' ] [ 3 ] ) } , { hex ( data [ ' mGObjAt ' ] [ 4 ] ) } }} , // mGObjAt \n " )
# dCcD_SrcGObjTg members
formatted_output . append ( f " {{ { data [ ' mGObjTg ' ] [ 0 ] } , { hex ( data [ ' mGObjTg ' ] [ 1 ] ) } , { hex ( data [ ' mGObjTg ' ] [ 2 ] ) } , { hex ( data [ ' mGObjTg ' ] [ 3 ] ) } , { hex ( data [ ' mGObjTg ' ] [ 4 ] ) } }} , // mGObjTg \n " )
# dCcD_SrcGObjCo members
formatted_output . append ( f " {{ { hex ( data [ ' mGObjCo ' ] ) } }} , // mGObjCo \n " )
formatted_output . append ( " }, // mObjInf \n " )
# cM3dGCylS members
formatted_output . append ( " { \n " )
formatted_output . append ( f " {{ {{ { data [ ' mCenter ' ] [ 0 ] } f, { data [ ' mCenter ' ] [ 1 ] } f, { data [ ' mCenter ' ] [ 2 ] } f }} , { data [ ' mRadius ' ] } f }} // mSph \n " )
formatted_output . append ( " } // mSphAttr \n " )
formatted_output . append ( " }; \n " )
# Remove the compiler strip gate macro if it was present
if ' COMPILER_STRIP_GATE ' in lines [ end_index + 1 ] :
del lines [ end_index + 1 ]
# Replace the original section with the new formatted output
lines [ start_index : end_index + 1 ] = formatted_output
# Write the modified lines back to the file
with open ( filename , ' w ' ) as f :
f . writelines ( lines )
# Write the unpacked data back to the file
def write_triangle_shape_data ( filename , data , variable_name , section_rodata_present , start_index , end_index ) :
with open ( filename , ' r ' ) as f :
lines = f . readlines ( )
# Formatting the output
const_keyword = " const " if section_rodata_present else " "
formatted_output = [ ]
formatted_output . append ( f " { const_keyword } static dCcD_SrcTri { variable_name } = {{ \n " )
formatted_output . append ( " { \n " )
# cCcD_SrcObj members
formatted_output . append ( f " {{ { hex ( data [ ' mFlags ' ] ) } , {{ {{ { data [ ' mObjAt ' ] [ 0 ] } , { hex ( data [ ' mObjAt ' ] [ 1 ] ) } , { hex ( data [ ' mObjAt ' ] [ 2 ] ) } }} , {{ { hex ( data [ ' mObjTg ' ] [ 0 ] ) } , { hex ( data [ ' mObjTg ' ] [ 1 ] ) } }} , { hex ( data [ ' mObjCo ' ] ) } }} }} , // mObj \n " )
# dCcD_SrcGObjAt members
formatted_output . append ( f " {{ { data [ ' mGObjAt ' ] [ 0 ] } , { hex ( data [ ' mGObjAt ' ] [ 1 ] ) } , { hex ( data [ ' mGObjAt ' ] [ 2 ] ) } , { hex ( data [ ' mGObjAt ' ] [ 3 ] ) } , { hex ( data [ ' mGObjAt ' ] [ 4 ] ) } }} , // mGObjAt \n " )
# dCcD_SrcGObjTg members
formatted_output . append ( f " {{ { data [ ' mGObjTg ' ] [ 0 ] } , { hex ( data [ ' mGObjTg ' ] [ 1 ] ) } , { hex ( data [ ' mGObjTg ' ] [ 2 ] ) } , { hex ( data [ ' mGObjTg ' ] [ 3 ] ) } , { hex ( data [ ' mGObjTg ' ] [ 4 ] ) } }} , // mGObjTg \n " )
# dCcD_SrcGObjCo members
formatted_output . append ( f " {{ { hex ( data [ ' mGObjCo ' ] ) } }} , // mGObjCo \n " )
formatted_output . append ( " }, // mObjInf \n " )
# cM3dGCylS members
formatted_output . append ( " { \n " )
formatted_output . append ( f " {{ {{ { data [ ' a ' ] [ 0 ] } f, { data [ ' a ' ] [ 1 ] } f, { data [ ' a ' ] [ 2 ] } f }} , {{ { data [ ' b ' ] [ 0 ] } f, { data [ ' b ' ] [ 1 ] } f, { data [ ' b ' ] [ 2 ] } f }} , {{ { data [ ' c ' ] [ 0 ] } f, { data [ ' c ' ] [ 1 ] } f, { data [ ' c ' ] [ 2 ] } f }} }} // mTri \n " )
formatted_output . append ( " } // mTriAttr \n " )
formatted_output . append ( " }; \n " )
# Remove the compiler strip gate macro if it was present
if ' COMPILER_STRIP_GATE ' in lines [ end_index + 1 ] :
del lines [ end_index + 1 ]
# Replace the original section with the new formatted output
lines [ start_index : end_index + 1 ] = formatted_output
# Write the modified lines back to the file
with open ( filename , ' w ' ) as f :
f . writelines ( lines )
# Write the unpacked data back to the file
def write_capsule_shape_data ( filename , data , variable_name , section_rodata_present , start_index , end_index ) :
with open ( filename , ' r ' ) as f :
lines = f . readlines ( )
# Formatting the output
const_keyword = " const " if section_rodata_present else " "
formatted_output = [ ]
formatted_output . append ( f " { const_keyword } static dCcD_SrcCps { variable_name } = {{ \n " )
formatted_output . append ( " { \n " )
# cCcD_SrcObj members
formatted_output . append ( f " {{ { hex ( data [ ' mFlags ' ] ) } , {{ {{ { data [ ' mObjAt ' ] [ 0 ] } , { hex ( data [ ' mObjAt ' ] [ 1 ] ) } , { hex ( data [ ' mObjAt ' ] [ 2 ] ) } }} , {{ { hex ( data [ ' mObjTg ' ] [ 0 ] ) } , { hex ( data [ ' mObjTg ' ] [ 1 ] ) } }} , { hex ( data [ ' mObjCo ' ] ) } }} }} , // mObj \n " )
# dCcD_SrcGObjAt members
formatted_output . append ( f " {{ { data [ ' mGObjAt ' ] [ 0 ] } , { hex ( data [ ' mGObjAt ' ] [ 1 ] ) } , { hex ( data [ ' mGObjAt ' ] [ 2 ] ) } , { hex ( data [ ' mGObjAt ' ] [ 3 ] ) } , { hex ( data [ ' mGObjAt ' ] [ 4 ] ) } }} , // mGObjAt \n " )
# dCcD_SrcGObjTg members
formatted_output . append ( f " {{ { data [ ' mGObjTg ' ] [ 0 ] } , { hex ( data [ ' mGObjTg ' ] [ 1 ] ) } , { hex ( data [ ' mGObjTg ' ] [ 2 ] ) } , { hex ( data [ ' mGObjTg ' ] [ 3 ] ) } , { hex ( data [ ' mGObjTg ' ] [ 4 ] ) } }} , // mGObjTg \n " )
# dCcD_SrcGObjCo members
formatted_output . append ( f " {{ { hex ( data [ ' mGObjCo ' ] ) } }} , // mGObjCo \n " )
formatted_output . append ( " }, // mObjInf \n " )
# cM3dGCylS members
formatted_output . append ( " { \n " )
formatted_output . append ( f " {{ {{ { data [ ' mStart ' ] [ 0 ] } f, { data [ ' mStart ' ] [ 1 ] } f, { data [ ' mStart ' ] [ 2 ] } f }} , {{ { data [ ' mEnd ' ] [ 0 ] } f, { data [ ' mEnd ' ] [ 1 ] } f, { data [ ' mEnd ' ] [ 2 ] } f }} , { data [ ' mRadius ' ] } f }} , // mCps \n " )
formatted_output . append ( " } // mCpsAttr \n " )
formatted_output . append ( " }; \n " )
# Remove the compiler strip gate macro if it was present
if ' COMPILER_STRIP_GATE ' in lines [ end_index + 1 ] :
del lines [ end_index + 1 ]
# Replace the original section with the new formatted output
lines [ start_index : end_index + 1 ] = formatted_output
# Write the modified lines back to the file
with open ( filename , ' w ' ) as f :
f . writelines ( lines )
def post_process ( filename ) :
"""
Checks if we need to include d_cc_d header file, remove structs, remove functions and/or rename ctor/dtors.
"""
with open ( filename , ' r ' ) as f :
lines = f . readlines ( )
remove_structs = {
" struct Vec " ,
" struct cXyz " ,
" struct dCcD_Stts " ,
" struct dCcD_GStts " ,
" struct dCcD_GObjInf " ,
" struct dCcD_SrcSph "
" struct dCcD_Cyl " ,
" struct dCcD_Cyl " ,
" struct dCcD_Sph " ,
" struct dCcD_Cps " ,
" struct dCcD_Tri " ,
" struct dCcD_SrcSph " ,
" struct dCcD_SrcCyl " ,
" struct dCcD_SrcCps " ,
" struct dCcD_SrcTri " ,
" struct dCcD_GAtTgCoCommonBase " ,
" struct cCcD_Obj " ,
" struct cCcD_Stts " ,
" struct cCcD_GStts " ,
" struct cM3dGSph " ,
" struct cM3dGPla " ,
" struct cM3dGCyl " ,
" struct cM3dGCps " ,
" struct cM3dGAab " ,
" struct cM3dGLin " ,
" struct cM3dGTri " ,
" struct Z2SeMgr " ,
" struct JAISoundID " ,
" struct Z2AudioMgr " ,
" struct JGeometry "
}
remove_functions = {
" void PSVECAdd(); " ,
" void PSVECSubtract(); " ,
" void PSVECSquareMag(); " ,
" void PSVECScale(); " ,
" void PSVECSquareDistance(); " ,
" void PSVECDotProduct(); " ,
" void PSVECCrossProduct(); " ,
" void C_VECReflect(); " ,
" void abs(); " ,
" void sin(); " ,
" void acos(); " ,
" void tan(); " ,
" u32 __float_nan; " ,
" f32 G_CM3D_F_ABS_MIN " ,
" u32 __float_max "
}
extern_c_replacements = {
" asm cXyz::~cXyz() " ,
" asm cM3dGPla::~cM3dGPla() " ,
" cXyz::cXyz() " ,
" asm cM3dGPla::~cM3dGPla() " ,
" asm cM3dGCyl::~cM3dGCyl() " ,
" asm cM3dGSph::~cM3dGSph() " ,
" asm cM3dGAab::~cM3dGAab() " ,
" asm dCcD_Sph::~dCcD_Sph() " ,
" asm dCcD_Sph::dCcD_Sph() " ,
" asm dCcD_GStts::~dCcD_GStts() " ,
" asm cCcD_GStts::~cCcD_GStts() " ,
" asm cXyz::cXyz(cXyz const& param_0) " ,
" asm cXyz::cXyz(f32 param_0, f32 param_1, f32 param_2) " ,
" asm void cXyz::set(f32 param_0, f32 param_1, f32 param_2) " ,
" asm void cXyz::set(Vec const& param_0) " ,
" asm void cXyz::operator=(cXyz const& param_0) " ,
" asm void cXyz::operator+=(Vec const& param_0) " ,
" JAISoundID::JAISoundID(u32 param_0) " , # special case, needs manual replacement
" asm dCcD_Cyl::~dCcD_Cyl() " ,
" asm cM3dGLin::~cM3dGLin() " ,
" asm dCcD_Cyl::dCcD_Cyl() " ,
" asm dCcD_Stts::~dCcD_Stts() " ,
" asm dCcD_Stts::dCcD_Stts() " ,
" asm dCcD_Tri::~dCcD_Tri() " ,
" asm dCcD_Tri::dCcD_Tri() " ,
" asm cM3dGTri::~cM3dGTri() " ,
" asm void cCcD_ObjHitInf::OnCoSetBit() " ,
" asm void cXyz::abs() " ,
" asm dCcD_Cps::~dCcD_Cps() " ,
" asm dCcD_Cps::dCcD_Cps() "
}
include_line = False
i = len ( lines ) - 1
while i > = 0 :
line = lines [ i ]
if any ( struct in line for struct in remove_structs ) :
print ( f " Removing struct at line { i } : { line } " )
include_line = True
# Find the end of the struct data
end_index = None
brace_count = 0
for j in range ( i , len ( lines ) ) :
brace_count + = lines [ j ] . count ( ' { ' )
brace_count - = lines [ j ] . count ( ' } ' )
if brace_count == 0 :
end_index = j
break
# Remove the struct data
if end_index is not None :
del lines [ i : end_index + 2 ] # rm struct and trailing newline
i = end_index + 1 # Adjust i to skip over deleted lines
elif any ( function in line for function in remove_functions ) :
print ( f " Removing function at line { i } : { line } " )
include_line = True
del lines [ i ]
elif any ( function in line for function in extern_c_replacements ) and not line . startswith ( " // " ) :
print ( f " Replacing function at line { i } : { line } " )
include_line = True
# Grab the mangled name from 4 lines above
if " asm " in line :
symbol_line = lines [ i - 4 ]
else :
symbol_line = lines [ i - 1 ]
match = re . search ( r ' ( \ S+) \ s+ \ */$ ' , symbol_line )
if match :
symbol = match . group ( 1 )
print ( " SYMBOL: " , symbol )
else :
print ( " Symbol not found " )
# comment out original line
lines [ i ] = f " // { lines [ i ] } "
# Add new line after with extern "C" asm void
lines . insert ( i + 1 , f ' extern " C " asm void { symbol } () {{ \n ' )
i - = 1
if include_line :
# Find the first #include line
include_index = next ( ( i for i , line in enumerate ( lines ) if line . strip ( ) . startswith ( " #include " ) ) , None )
if include_index is not None :
# Insert the new header after the first #include line
lines . insert ( include_index + 1 , ' #include " d/cc/d_cc_d.h " \n ' )
else :
# If no #include line is found, insert at the beginning (or handle as you see fit)
lines . insert ( 0 , ' #include " d/cc/d_cc_d.h " \n ' )
# Write the modified lines back to the file
with open ( filename , ' w ' ) as f :
f . writelines ( lines )
2024-04-12 00:10:30 -06:00
def search_file ( target , pattern ) :
2023-08-20 22:10:09 -06:00
file_matches = [ ]
2024-04-12 00:10:30 -06:00
# if target is a file, search the file
if os . path . isfile ( target ) :
with open ( target , ' r ' ) as f :
contents = f . read ( )
matches = pattern . findall ( contents )
if matches :
for match in matches :
# Extract the desired part of the match and append it to the file_matches list
variable_name = match [ 2 ]
file_matches . append ( ( target , variable_name ) )
return file_matches
print ( " No matches found in the file. " )
return None
# if target is a directory, search all files in the directory
elif os . path . isdir ( target ) :
for root , dirs , files in os . walk ( target ) :
2023-08-20 22:10:09 -06:00
for file in files :
2024-04-12 00:10:30 -06:00
if file . endswith ( ' .cpp ' ) :
2023-08-20 22:10:09 -06:00
filepath = os . path . join ( root , file )
with open ( filepath , ' r ' ) as f :
contents = f . read ( )
matches = pattern . findall ( contents )
if matches :
for match in matches :
# Extract the desired part of the match and append it to the file_matches list
variable_name = match [ 2 ]
file_matches . append ( ( filepath , variable_name ) )
2024-04-12 00:10:30 -06:00
return file_matches
print ( " Target is neither a file nor a directory. " )
return None
2023-08-20 22:10:09 -06:00
2024-04-12 00:10:30 -06:00
def search_files ( shape , target , variable_name = None ) :
"""
Returns a pairing of filename and shape data lines that have not been setup.
If variable_name is not set, the function will attempt to find and setup all variables with the specified shape.
"""
if variable_name is not None :
search_shape = variable_name
else :
search_shape = shape
2023-08-20 22:10:09 -06:00
2024-04-12 00:10:30 -06:00
pattern = re . compile ( f ' (SECTION_DATA|SECTION_RODATA).*static \\ s+u8 \\ s+(const \\ s+)?( \\ w* { search_shape } \\ w*) \\ [ \\ d+ \\ ] ' , re . I )
return search_file ( target , pattern )
def run_make_command ( wibo_path = None , thread_num = 1 ) :
cmd = [ " make " , " all " , " rels " , f " -j { thread_num } " ]
if wibo_path :
cmd . append ( f " WINE= { wibo_path } " )
result = subprocess . run ( cmd )
2023-08-20 22:10:09 -06:00
if result . returncode != 0 :
print ( " Failed to run make command " )
sys . exit ( 1 )
@click.command ( )
2024-04-12 00:10:30 -06:00
@click.option ( ' --shape ' , required = True , help = ' Shape to unpack. Should be one of: triangle, sphere, cylinder or capsule. ' )
@click.option ( ' --variable-name ' , default = None , required = False , help = ' Name of the variable in the file to transform. If not set, the script will atempt to find and setup all variables with the specified shape. ' )
@click.option ( ' --target ' , required = True , type = str , help = ' Target filename or path where the file to be processed is located. ' )
@click.option ( ' --wibo-path ' , default = " ~/git/c/wibo/build/wibo " , help = ' Path to Wibo. ' )
@click.option ( ' --thread-num ' , default = 1 , type = int , help = ' Number of threads for compilation. ' )
def process_file ( shape , variable_name , target , wibo_path , thread_num ) :
"""
Process the specified file(s) and unpack the shape of the variable(s).
"""
2023-08-20 22:10:09 -06:00
match shape . lower ( ) :
case " cylinder " :
2024-04-12 00:10:30 -06:00
filenames = search_files ( " cyl " , target , variable_name )
2023-08-20 22:10:09 -06:00
2024-04-12 00:10:30 -06:00
if filenames is None :
sys . exit ( 0 )
for file in filenames :
2023-08-20 22:10:09 -06:00
cylinder_data = None
2024-04-12 00:10:30 -06:00
f_name = file [ 0 ]
var_name = file [ 1 ]
print ( f_name )
data = fetch_shape_data ( f_name , var_name )
2023-08-20 22:10:09 -06:00
if data is not None :
cylinder_data = unpack_cylinder_data ( data [ " data " ] )
if cylinder_data is not None :
print ( " Updating " , f_name )
write_cylinder_shape_data ( f_name , cylinder_data , variable_name , data [ " section_rodata_present " ] , data [ " start_index " ] , data [ " end_index " ] )
post_process ( f_name )
2024-04-12 00:10:30 -06:00
run_make_command ( wibo_path , thread_num )
2023-08-20 22:10:09 -06:00
case " sphere " :
2024-04-12 00:10:30 -06:00
filenames = search_files ( " sph " , target , variable_name )
if filenames is None :
sys . exit ( 0 )
2023-08-20 22:10:09 -06:00
for filename in filenames :
sphere_data = None
f_name = filename [ 0 ]
variable_name = filename [ 1 ]
data = fetch_shape_data ( f_name , variable_name )
if data is not None :
sphere_data = unpack_sphere_data ( data [ " data " ] )
if sphere_data is not None :
print ( " Updating " , f_name )
write_sphere_shape_data ( f_name , sphere_data , variable_name , data [ " section_rodata_present " ] , data [ " start_index " ] , data [ " end_index " ] )
post_process ( f_name )
run_make_command ( )
case " triangle " :
2024-04-12 00:10:30 -06:00
filenames = search_files ( " tri " , target , variable_name )
if filenames is None :
sys . exit ( 0 )
2023-08-20 22:10:09 -06:00
for filename in filenames :
triangle_data = None
f_name = filename [ 0 ]
variable_name = filename [ 1 ]
data = fetch_shape_data ( f_name , variable_name )
if data is not None :
triangle_data = unpack_triangle_data ( data [ " data " ] )
if triangle_data is not None :
print ( " Updating " , f_name )
write_triangle_shape_data ( f_name , triangle_data , variable_name , data [ " section_rodata_present " ] , data [ " start_index " ] , data [ " end_index " ] )
post_process ( f_name )
run_make_command ( )
case " capsule " :
2024-04-12 00:10:30 -06:00
filenames = search_files ( " cps " , target , variable_name )
if filenames is None :
sys . exit ( 0 )
2023-08-20 22:10:09 -06:00
for filename in filenames :
capsule_data = None
f_name = filename [ 0 ]
variable_name = filename [ 1 ]
data = fetch_shape_data ( f_name , variable_name )
if data is not None :
capsule_data = unpack_capsule_data ( data [ " data " ] )
if capsule_data is not None :
print ( " Updating " , f_name )
write_capsule_shape_data ( f_name , capsule_data , variable_name , data [ " section_rodata_present " ] , data [ " start_index " ] , data [ " end_index " ] )
post_process ( f_name )
run_make_command ( )
case _ :
raise ValueError ( f " Unknown shape: { shape } " )
if __name__ == ' __main__ ' :
process_file ( )