This file format needs more research. Researches on headers / bodies structures are partially achieved. |
Gotcha Force Models
Gotcha Force models work as in the game Super Smash Bros Melee (SSBM) investigated by its community.
Un modèle est un format de fichier organisant des objets et des ressources sérialisés pour être utilisés par la librairie Hal SysDolphin (HSD), une librairie utilisée dans plusieurs jeux par Capcom. Ce sont des ressources graphiques structurées de manière hiérarchique. La présence de certaines images dans les modèles montrent à priori l'utilisation de la technique matcap. Par ailleurs, la librairie prendrait ses sources sur le SDK Dolphin, tel que GX ou encore MTX.
Fichiers concernés
L'ensemble des fichiers ayant dans leur titre _mdl sont des fichiers modèles correspondant au format décrit dans cette page. On notera les fichiers .arz qui sont des modèles compressés. Le fichier collision.arc et les fichiers tdc00.arc à tdc09.arc sont aussi des modèles.
Les fichiers modèles se retrouvent aussi dans les pzz des borgs (plxxxx.pzz) en position 004 à 009.
HSDRaw
HSDRaw, un éditeur 3D dotnet de ce format est instable sur mon Windows (algoflash). Il est cependant prouvé qu'un edit des _mdl pour commencer à l'offset 0x100 - après le préambule - permet de visualiser le fichier sur HSDRaw et d'avoir le rendu 3D du Borg (click sur le premier dossier JOBJ - puis le JOBJ dedans et double click sur le nœud en violet). Cela devrait aussi permettre d'éditer certaines propriétés ?
Structure générale
Le format des modèles se découpe en plusieurs blocks. Le préambule fait 0x100 octets au tout début du fichier. Il est souvent absent. Toutes les valeurs sont en unsigned big endian, et le pad est en Nulls bytes (\x00). Les offsets trouvés après le début du Data Block (DB) sont relatifs au début du DB. La table de relocs est un tableau d'offsets de 4 octets (relatif au DB) permettant de traduire en adressage absolu les offsets des structures lors du chargement mémoire. La table de reloc permet par exemple de délimiter les structures du fait qu'il est peu probable qu'un offset pointe au milieu d'une structure.
L'ensemble des fichiers répondant à ce format on un seul root_node de type SObj (Scene data).
Préambule : (0x100 octets / 0 octets)
- 4 octets - hsd_header_offset=0x100 (toujours)
- 4 octets - bones_flags_table_offset=0x20 (toujours) - offset de la liste des flags des armatures
- 4 octets - bones_table_offset=0xC0 (toujours) - offset de la liste des armatures
- 20 octets - Pad - (toujours)
- 4 octets [32] - Inconnu (flags des armatures ?) 1 octet par flag ?
- 32 octets - Pad - (toujours)
- 32 octets - armatures ?
- 32 octets - Pad - (toujours)
HSD Header - (0x20 octets)
- 4 octets - Taille totale du fichier
- 4 octets - Taille du Data block
- 4 octets - Nombre d'entrée dans la Table de relocs
- 4 octets - root0_count
- 4 octets - root1_count
- 12 octets - Pad - (toujours)
Data block :
- JOBJ, etc. -> Ces structures sont détaillée plus loin ci-dessous.
Relocation Table :
- Tableau d'offsets de 4 octets - Offsets des structures dans le DB.
Root Nodes (2) :
- Tableau de taille 8 * root0_count
- 4 octets - root_offset - relatif au DB
- 4 octets - string_table_offset - relatif à la StringTable
- Tableau de taille 8 * root1_count
- 4 octets - root_offset - relatif au DB
- 4 octets - string_table_offset - relatif à la StringTable
String Table :
- Suite de strings terminées par Null au nombre de root0_count + root1_count
Structures rencontrées dans le DB
Note : l'arborescence est cumulative sur les transformations pour les relations parents->enfants
Les Noeuds Racine qui ont pour nom "scene_data" pointent sur un SObj :
SObj : (0x10 octets)
- 4 octets - JObjDescs_offsets_list_offset
- 4 octets - Cameras_list_offset
- 4 octets - Lights_list_offset
- 4 octets - Fog
"scene_data" == nom du nœud racine JObjDescs_offsets_list_offset, cameras.. pointent sur une liste d'offsets terminée par 4 octets à 0x00. lights_list_offset pointe sur des couples (4 octets - LObj_offset ; 4 octets - light_anim_pointer_offset)
JObjDesc : (0x10 octets)
- 4 octets - root_joint_offset - JObj
- 4 octets - joint_animations_offset - ?
- 4 octets - material_animations_offset - ?
- 4 octets - shape_animations_offset - ?
CObj : (0x38 octets)
- 4 octets - flags
- 4 octets - projection_type - ProjectionTypeEnum
- 2 octets - viewport_left
- 2 octets - viewport_right
- 2 octets - viewport_top
- 2 octets - viewport_bottom
- 4 octets - proj_width
- 4 octets - proj_height
- 4 octets - eye_offset - WObj
- 4 octets - target_offset - WObj
- 4 octets - roll
- 4 octets - Inconnu
- 4 octets - float - near_clip
- 4 octets - float - far_clip
- 4 octets - float - field_of_view
- 4 octets - float - aspect
Camera object. Note : pour frustrum ou ortho, la structure est différente.
- ProjectionTypeEnum:
- PERSPECTIVE = 1
- FRUSTRUM = 2
- ORTHO = 3
WObj: (0x14 octets)
- 4 octets - Inconnu
- 4 octets - float - v1
- 4 octets - float - v2
- 4 octets - float - v3
- 4 octets - Inconnu
eye / target
LObj : (0x1c octets)
- 4 octets - class_name
- 4 octets - next_offset
- 2 octets - flags - LObjFlagsEnum
- 2 octets - attenuation_flags - LObjAttenuationFlagsEnum
- 1 octet - color_r
- 1 octet - color_g
- 1 octet - color_b
- 1 octet - color_alpha
- 4 octets - lobj_point_offset - position
- 4 octets - infinite_data
- 4 octets - point_spot_data
light
- LObjFlagsEnum :
- LOBJ_AMBIANT = 00 00
- LOBJ_INFINITE = 00 01
- LOBJ_POINT = 00 02
- LOBJ_SPOT = 00 03
- LOBJ_DIFFUSE = 00 04
- LOBJ_SPECULAR = 00 08
- LOBJ_ALPHA = 00 10
- LOBJ_HIDDEN = 00 20
- LOBJ_RAW_PARAM = 00 40
- LOBJ_DIFF_DIRTY = 00 80
- LOBJ_SPEC_DIRTY = 01 00
- LObjAttenuationFlagsEnum :
- LOBJ_LIGHT_ATTN_NONE = 00 00
- LOBJ_LIGHT_ATTN = 00 01
LObjPoint : (0x14 octets)
- 4 octets - class_name
- 4 octets - float - x
- 4 octets - float - y
- 4 octets - float - z
- 4 octets - Inconnu
LightAnimPointer : (0x10 octets)
- 4 octets - next_offset - (LightAnimPointer)
- 4 octets - light_anim - ?
- 4 octets - position_anim - ?
- 4 octets - interst_anim - ?
Bugs dans HSDRaw - à tester
JObj : (0x40 octets)
- 4 octets - Inconnu
- 4 octets - flags
- 4 octets - child_offset (JObj)
- 4 octets - next_offset (JObj)
- 4 octets - dobj_offset (DObj)
- 4 octets [3] - float3 - rotation_xyz
- 4 octets [3] - float3 - scale_xyz
- 4 octets [3] - float3 - translation_xyz
- 4 octets - inverse_world_transform_offset (inverse world transform = Matrice de 4x3)
- 4 octets - robj_offset
Pointé par les listes dans Root Node scene_data.
- JObjFlagsEnum : (peuvent se cumuler)
- NULL = 00 00 00 00
- SKELETON = 00 00 00 01
- SKELETON_ROOT = 00 00 00 02
- ENVELOPE_MODEL = 00 00 00 04
- CLASSICAL_SCALING = 00 00 00 08
- HIDDEN = 00 00 00 10
- PTCL = 00 00 00 20
- MTX_DIRTY = 00 00 00 40
- LIGHTING = 00 00 00 80
- TEXGEN = 00 00 01 00
- BILLBOARD = 00 00 02 00
- VBILLBOARD = 00 00 04 00
- HBILLBOARD = 00 00 06 00
- RBILLBOARD = 00 00 08 00
- INSTANCE = 00 00 10 00
- PBILLBOARD = 00 00 20 00
- SPLINE = 00 00 40 00
- FLIP_IK = 00 00 80 00
- SPECULAR = 00 01 00 00
- USE_QUATERNION = 00 02 00 00
- OPA = 00 04 00 00
- XLU = 00 08 00 00
- TEXEDGE = 00 10 00 00
- JOINT1 = 00 20 00 00
- JOINT2 = 00 40 00 00
- EFFECTOR = 00 60 00 00
- USER_DEFINED_MTX = 00 80 00 00
- MTX_INDEPEND_PARENT = 01 00 00 00
- MTX_INDEPEND_SRT = 02 00 00 00
- MTX_SCALE_COMPENSATE = 04 00 00 00
- ROOT_OPA = 10 00 00 00
- ROOT_XLU = 20 00 00 00
- ROOT_TEXEDGE = 40 00 00 00
InverseWorldTransform : (0x30 octets)
- 4 octets - float - M11
- 4 octets - float - M12
- 4 octets - float - M13
- 4 octets - float - M14
- 4 octets - float - M21
- 4 octets - float - M22
- 4 octets - float - M23
- 4 octets - float - M24
- 4 octets - float - M31
- 4 octets - float - M32
- 4 octets - float - M33
- 4 octets - float - M34
DObj : (0x10 octets)
- 4 octets - Inconnu
- 4 octets - next_offset - prochain DOBJ
- 4 octets - mobj_offset - material offset
- 4 octets - pobj_offset - mesh offset
Liste chaînée qui permet de récupérer l'ensemble des materials & mesh pour le JObj auquel il est lié.
MObj : (0x18 octets)
- 4 octets - Inconnu
- 4 octets - render_flags - RenderFlagsEnum
- 4 octets - tobj_offset - offset de texture - peut être invalide s'il n'y a pas de texture pour le material
- 4 octets - material_offset - couleurs des materials ?
- 8 octets - PEDesc ?
Contient les textures & informations sur les couleurs des Materials.
- RenderFlagsEnum : (se cumulent)
- USER = 80 00 00 00
- CONSTANT = 00 00 00 01
- VERTEX = 00 00 00 02
- BOTH = 00 00 00 03
- DIFFUSE = 00 00 00 04
- SPECULAR = 00 00 00 08
- TEX0 = 00 00 00 10
- TEX1 = 00 00 00 20
- TEX2 = 00 00 00 40
- TEX3 = 00 00 00 80
- TEX4 = 00 00 01 00
- TEX5 = 00 00 02 00
- TEX6 = 00 00 04 00
- TEX7 = 00 00 08 00
- TOON = 00 00 10 00
- ALPHA_MAT = 00 00 20 00
- ALPHA_VTX = 00 00 40 00
- ALPHA_BOTH = 00 00 60 00
- ZOFST = 01 00 00 00
- EFFECT = 02 00 00 00
- SHADOW = 04 00 00 00
- ZMODE_ALWAYS = 08 00 00 00
- DF_ALL = 10 00 00 00
- NO_ZUPDATE = 20 00 00 00
- XLU = 40 00 00 00
TObj : (0x5c octets)
- 4 octets - ?
- 4 octets - ?
- 4 octets - tex_map_id - TexMapIdEnum
- 4 octets - gx_tex_gen_src - GXTexGenSrcEnum
- 4 octets - float - rx
- 4 octets - float - ry
- 4 octets - float - rz
- 4 octets - float - sx
- 4 octets - float - sy
- 4 octets - float - sz
- 4 octets - float - tx
- 4 octets - float - ty
- 4 octets - float - tz
- 4 octets - wrap_s - WrapEnum
- 4 octets - wrap_t - WrapEnum
- 1 octet - w_scale
- 1 octet - h_scale
- 2 octets - ?
- 1 octets - bump_map - (False = 00 ; True = 01)
- 4 bits - alpha_operation - AlphaOperationEnum
- 4 bits - color_operation - ColorOperationEnum
- 1 octet - shadow_lightmap - (False = 00 ; True = 01) (2 octets avec une Enum lightmap limite)
- 1 bit - ext_lightmap - (False = 0 ; True = 1)
- 1 bit - ambiant_lightmap - (False = 0 ; True = 1)
- 1 bit - specular_lightmap - (False = 0 ; True = 1)
- 1 bit - diffuse_lightmap (False = 0 ; True = 1)
- 4 bits - coord_type - CoordTypeEnum
- 4 octets - float - blending - utilisé quand color_operation ou alpha_operation == BLEND
- 4 octets - mag_filter - MagFilterEnum
- 4 octets - ?
- 4 octets - ?
- 4 octets - ?
- 4 octets - ?
Informations de Textures. Le TObj contient des informations sur les paramètres d'environnement de textures utilisés pour le rendu graphique ainsi que les offset de l'image et des données de la palette utilisés pour cette texture. Le plus important ici, c'est l'offset d'image et la palette/tlut - si l'image n'est pas indexée (RGBA, CMPR, etc.) alors la structure de description palette/tlut n'est pas utilisée.
- TexMapIdEnum :
- GX_TEXMAP0 = 00 00 00 00
- GX_TEXMAP1 = 00 00 00 01
- GX_TEXMAP2 = 00 00 00 02
- GX_TEXMAP3 = 00 00 00 03
- GX_TEXMAP4 = 00 00 00 04
- GX_TEXMAP5 = 00 00 00 05
- GX_TEXMAP6 = 00 00 00 06
- GX_TEXMAP7 = 00 00 00 07
- GX_MAX_TEXMAP = 00 00 00 08
- GX_TEXMAP_NULL = 00 00 00 09
- GX_TEXMAP_DISABLE = 00 00 00 0A
- GXTexGenSrcEnum :
- GX_TG_POS = 00 00 00 00
- GX_TG_NRM = 00 00 00 01
- GX_TG_BINRM = 00 00 00 02
- GX_TG_TANGENT = 00 00 00 03
- GX_TG_TEX0 = 00 00 00 04
- GX_TG_TEX1 = 00 00 00 05
- GX_TG_TEX2 = 00 00 00 06
- GX_TG_TEX3 = 00 00 00 07
- GX_TG_TEX4 = 00 00 00 08
- GX_TG_TEX5 = 00 00 00 09
- GX_TG_TEX6 = 00 00 00 0A
- GX_TG_TEX7 = 00 00 00 0B
- GX_TG_TEXCOORD0 = 00 00 00 0C
- GX_TG_TEXCOORD1 = 00 00 00 0D
- GX_TG_TEXCOORD2 = 00 00 00 0E
- GX_TG_TEXCOORD3 = 00 00 00 0F
- GX_TG_TEXCOORD4 = 00 00 00 10
- GX_TG_TEXCOORD5 = 00 00 00 11
- GX_TG_TEXCOORD6 = 00 00 00 12
- GX_TG_COLOR0 = 00 00 00 13
- GX_TG_COLOR1 = 00 00 00 14
- AlphaOperationEnum:
- NONE = 0X
- ALPHAMASK = 1X
- BLEND = 2X
- MODULATE = 3X
- REPLACE = 4X
- PASS = 5X
- ADD = 6X
- SUB = 7X
- ColorOperationEnum:
- NONE = X0
- ALPHA_MASK = X1
- RGB_MASK = X2
- BLEND = X3
- MODULATE = X4
- REPLACE = X5
- PASS = X6
- ADD = X7
- SUB = X8
- CoordTypeEnum:
- UV = X0
- REFLECTION = X1
- HILIGHT = X2
- SHADOW = X3
- TOON = X4
- GRADATION = X5
- MagFilterEnum:
- GX_NEAR = 00 00 00 00
- GX_LINEAR = 00 00 00 01
- GX_NEAR_MIP_NEAR = 00 00 00 02
- GX_LIN_MIP_NEAR = 00 00 00 03
- GX_NEAR_MIP_LIN = 00 00 00 04
- GX_LIN_MIP_LIN = 00 00 00 05
- WrapEnum:
- CLAMP = 00 00 00 00
- REPEAT = 00 00 00 01
- MIRROR = 00 00 00 02
Material : (0x14 octets)
- 1 octets - AMB_R - (ambient)
- 1 octets - AMB_G
- 1 octets - AMB_B
- 1 octets - AMB_A
- 1 octets - DIF_R - (diffuse)
- 1 octets - DIF_G
- 1 octets - DIF_B
- 1 octets - DIF_A
- 1 octets - SPC_R - (specular)
- 1 octets - SPC_G
- 1 octets - SPC_B
- 1 octets - SPC_A
- 4 octets - float - alpha
- 4 octets - float - shininess
PObj : (0x18 octets)
- 4 octets - Inconnu
- 4 octets - next_offset - pobj
- 4 octets - vertex_attr_list_offset
- 2 octets - Flags - PObjFlagsEnum
- 2 octets - display_list_size - number of 0x20 (32) byte blocks occupied by display list data
- 4 octets - display_list_offset
- 4 octets - weight_list_offset - ? verifier qu'on retrouve bien le tableau d'envelope_weights à cet offset
Comme l'indique next_offset, il s'agit d'une liste de meshs à afficher pour un material donné référencé par un dobj. PObj contient les offsets de vertex attributes / display list / joint weight list ce qui suffit et permet d’interpréter et traiter les coordonnées de vertices, normals & textures.
Les attributs vertex sont les plus important - les parametres specifiés contrôlent tout : du format, de la taille des données de chaque vertex, normal et coordonnées de textures et comment ces valeurs sont dimensionnées (scaled) par rapport à la présence et la taille de chaque valeur d'index qui apparaît dans la display list information.
- PObjFlagsEnum :
- Inconnu0 = 00 01
- Inconnu1 = 00 02
- ANIM = 00 08
- SHAPE_ANIM = 10 00
- ENVELOPE = 20 00
- CULLBACK = 40 00
- CULLFRONT = 80 00
EnvelopeWeights : (0x? octets)
- 4 octets - envelope_count
- [envelope_count]:
- 4 octets - float - weight
- 4 octets - pad ?
- 4 octets - jobj_list_offset
not verified yet
- IMAGE_HEADER :
- 4 octets - image_offset - image data
- 2 octets - width
- 2 octets - height
- 4 octets - image_format
- PALETTE_HEADER : (0x10)
- 4 octets - palette_offset - palette data
- 4 octets - palette_format
- 4 octets - Inconnu
- 2 octets - color_count
- 2 octets - Inconnu
image_offset et palette_offset sont souvent partagés par plusieurs textures - savoir ça permet de retrouver toutes les informations sur les images des textures (largeur, hauteur, format), ainsi que le format et le nombre de couleurs de la palette/tlut.
Le format d'image ne détermine pas le nombre total de couleurs de la palette qu'elle utilisent. Par exemple, une image indexée sur 8 bits aurait un maximum de 256 couleurs mais seulement 136 ou 221 couleurs sont actuellement utilisées. De la sorte, la palette n'utilisera pas la taille totale qu'elle devrait utiliser.
Les images suivent la structure généralement utilisée par la GameCube :
IMAGE FORMATS :
- case 0: //i4
- case 1: //i8
- case 2: //i4a4
- case 3: //i8a8
- case 4: //r5g6b5
- case 5: //rgb5a3
- case 6: //r8g8b8a8
- case 8: //index4
- case 9: //index8
- case 0xa: //index14x2
- case 0xe: //s3tc1
Les formats indexés utilisent aussi les informations de palettes/tlut et les données peuvent apparaître en plusieurs formats :
PALETTE FORMATS :
- case 0: //ia8
- case 1: //r5g6b5
- case 2: //rgb5a3
---> "Joint Data - Accessing Geometry, Mesh, and Vertex Attributes" https://smashboards.com/threads/melee-dat-format.292603/
trad a revoir à partir de là, il faut investiguer le rendu 3D sur GameCube pour mieux comprendre
// déclaration d'un vertex et attribute information // attr, type, cnt, data_type, flags?, file_offset
- ATTR_DATA
- 4 octets - GXAttr vtx_attr_offset - attr
- 4 octets - GXAttrType vtx_attr_type - index_type
- 4 octets - GXCompCnt comp_cnt - cnt
- 4 octets - GXCompType comp_type - data_type
- 1 octets - scale
- 1 octets - Inconnu
- 2 octets - vtx_stride
- 2 octets - data_offset
vertex_attr_offset pointe sur une liste d'attributs de vertex. Leur nombre n'est pas donné. Les données dans cette structure suivent les informations et les types conçus par l'architecture GameCube/WII et son API. La liste est terminée par une valeur spécifique de vtx_attr qui signale la fin de cette liste. Avec un accès à la doku SDK, il serait fortement conseillé de lire la section à propos des "vertex attributes and other specifications".
La valeur de vtx_attr determine le type de donnée que cet attribut désigne et peut indiquer un nombre de types natifs utilisés utilisés pour passer les données entre les interfaces logicielles et matérielles.
Les valeures possibles sont les suivantes :
- enum GXAttr (4 octets)
- GX_VA_PNMTXIDX = 0, // position/normal matrix index
- GX_VA_TEX0MTXIDX, // texture 0 matrix index
- GX_VA_TEX1MTXIDX, // texture 1 matrix index
- GX_VA_TEX2MTXIDX, // texture 2 matrix index
- GX_VA_TEX3MTXIDX, // texture 3 matrix index
- GX_VA_TEX4MTXIDX, // texture 4 matrix index
- GX_VA_TEX5MTXIDX, // texture 5 matrix index
- GX_VA_TEX6MTXIDX, // texture 6 matrix index
- GX_VA_TEX7MTXIDX, // texture 7 matrix index
- GX_VA_POS = 9, // position
- GX_VA_NRM, // normal
- GX_VA_CLR0, // color 0
- GX_VA_CLR1, // color 1
- GX_VA_TEX0, // input texture coordinate 0
- GX_VA_TEX1, // input texture coordinate 1
- GX_VA_TEX2, // input texture coordinate 2
- GX_VA_TEX3, // input texture coordinate 3
- GX_VA_TEX4, // input texture coordinate 4
- GX_VA_TEX5, // input texture coordinate 5
- GX_VA_TEX6, // input texture coordinate 6
- GX_VA_TEX7, // input texture coordinate 7
- GX_POS_MTX_ARRAY, // position matrix array pointer
- GX_NRM_MTX_ARRAY, // normal matrix array pointer
- GX_TEX_MTX_ARRAY, // texture matrix array pointer
- GX_LIGHT_ARRAY, // light parameter array pointer
- GX_VA_NBT, // normal, bi-normal, tangent
- GX_VA_MAX_ATTR, // maximum number of vertex attributes
- GX_VA_NULL = 0xff // NULL attribute (to mark end of lists)
Toutes les données nécessaires pour représenter un mesh peuvent être spécifiées et indexées d'une manière ou d'une autre. De même, GX_VA_TEX0MTXIDX, GX_VA_CLR0, et autres sont aussi possibles - les valeurs les plus fréquentes dans les données SSBM sont : GX_VA_PNMTXIDX, GX_VA_POS, GX_VA_NRM, et GX_VA_TEX0. Which is to be expected as joint matrices, vertex positions/normals, and texture coordinates respectively are probably the most basic of elements needed to display a texture mesh, whether static or animated.
When the value of vtx_attr == GX_VA_NULL (0xFF), the end of the vertex attribute array has been reached.
The vtx_attr_type values is actually associated with how the value is indexed, and can thus also determine the size of the index value within the display list data.
- 4 octets - enum GXAttrType
- GX_NONE = 0,
- GX_DIRECT,
- GX_INDEX8,
- GX_INDEX16
- Structures COLL_DATA : (données de collisions)
- 4 octets - vertex_offset (2 float par entrée correspondant à des données 2D probablement)
- 4 octets - vertex_count
- 4 octets - index_offset
- 4 octets - index_count
- 4 octets [5] - tableau inconnu
- 2 octets - index_start
- 2 octets - index_count
- 4 octets - Inconnu
- 4 octets - Inconnu