1
+ #include "fbx.h"
2
+
3
+ #include <stdio.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+
7
+ FBXNodeList * FBXNodeList_Create (size_t initial_capacity )
8
+ {
9
+ FBXNodeList * list = malloc (sizeof (FBXNodeList ));
10
+ if (list == NULL ) {
11
+ return NULL ;
12
+ }
13
+
14
+ list -> length = 0 ;
15
+ list -> capacity = initial_capacity ;
16
+
17
+ if (initial_capacity > 0 ) {
18
+ list -> nodes = malloc (sizeof (FBXNode ) * initial_capacity );
19
+ if (list -> nodes == NULL ) {
20
+ free (list );
21
+ return NULL ;
22
+ }
23
+ } else {
24
+ list -> nodes = NULL ;
25
+ }
26
+
27
+ return list ;
28
+ }
29
+
30
+ bool FBXNodeList_Resize (FBXNodeList * list , size_t capacity )
31
+ {
32
+ void * memory_address ;
33
+ if (list -> nodes == NULL ) {
34
+ memory_address = malloc (sizeof (FBXNode ));
35
+ } else {
36
+ memory_address = realloc (list -> nodes , sizeof (FBXNode ) * capacity );
37
+ }
38
+ if (memory_address == NULL ) {
39
+ return false;
40
+ }
41
+ list -> nodes = memory_address ;
42
+ list -> capacity = capacity ;
43
+ return true;
44
+ }
45
+
46
+ bool FBXNodeList_Truncate (FBXNodeList * list )
47
+ {
48
+ if (list -> length == list -> capacity ) {
49
+ return true;
50
+ }
51
+ return FBXNodeList_Resize (list , list -> length );
52
+ }
53
+
54
+ bool FBXNodeList_Insert (FBXNodeList * list , FBXNode * node )
55
+ {
56
+ size_t new_length = list -> length + 1 ;
57
+ if (new_length > list -> capacity ) {
58
+
59
+ size_t new_capacity = list -> capacity + list -> capacity ; // grow capacity
60
+ if (new_capacity == 0 ) {
61
+ new_capacity = 1 ;
62
+ }
63
+
64
+ if (FBXNodeList_Resize (list , new_capacity ) == false) {
65
+ return false;
66
+ }
67
+ }
68
+
69
+ memcpy (& list -> nodes [list -> length ], node , sizeof (FBXNode ));
70
+ list -> length = new_length ;
71
+
72
+ return true;
73
+ }
74
+
75
+ bool FBX_ParseNodeProperty (FILE * file , FBXNodeProperty * property )
76
+ {
77
+ // Read property type
78
+ fread (& property -> type , sizeof (uint8_t ), 1 , file );
79
+
80
+ // Read property value
81
+ switch (property -> type ) {
82
+ case 'Y' : { /* i16 */
83
+ fread (& property -> Y .value , sizeof (int16_t ), 1 , file );
84
+ break ;
85
+ }
86
+ case 'C' : { /* u8 (bool) */
87
+ fread (& property -> C .value , sizeof (bool ), 1 , file );
88
+ break ;
89
+ }
90
+ case 'I' : { /* i32 */
91
+ fread (& property -> I .value , sizeof (int32_t ), 1 , file );
92
+ break ;
93
+ }
94
+ case 'F' : { /* f32 */
95
+ fread (& property -> F .value , sizeof (float ), 1 , file );
96
+ break ;
97
+ }
98
+ case 'D' : { /* f64 */
99
+ fread (& property -> D .value , sizeof (double ), 1 , file );
100
+ break ;
101
+ }
102
+ case 'L' : { /* i64 */
103
+ fread (& property -> L .value , sizeof (int64_t ), 1 , file );
104
+ break ;
105
+ }
106
+ case 'f' : { /* f32[] */
107
+ fread (& property -> f .length , sizeof (uint32_t ), 1 , file );
108
+ fread (& property -> f .encoding , sizeof (uint32_t ), 1 , file );
109
+ fread (& property -> f .compressed_length , sizeof (uint32_t ), 1 , file );
110
+ if (property -> f .encoding == 0 ) {
111
+ fseek (file , sizeof (float ) * property -> f .length , SEEK_CUR );
112
+ } else {
113
+ fseek (file , property -> f .compressed_length , SEEK_CUR );
114
+ }
115
+ break ;
116
+ }
117
+ case 'd' : { /* f64[] */
118
+ fread (& property -> d .length , sizeof (uint32_t ), 1 , file );
119
+ fread (& property -> d .encoding , sizeof (uint32_t ), 1 , file );
120
+ fread (& property -> d .compressed_length , sizeof (uint32_t ), 1 , file );
121
+ if (property -> d .encoding == 0 ) {
122
+ fseek (file , sizeof (double ) * property -> d .length , SEEK_CUR );
123
+ } else {
124
+ fseek (file , property -> d .compressed_length , SEEK_CUR );
125
+ }
126
+ break ;
127
+ }
128
+ case 'l' : { /* i64[] */
129
+ fread (& property -> l .length , sizeof (uint32_t ), 1 , file );
130
+ fread (& property -> l .encoding , sizeof (uint32_t ), 1 , file );
131
+ fread (& property -> l .compressed_length , sizeof (uint32_t ), 1 , file );
132
+ if (property -> l .encoding == 0 ) {
133
+ fseek (file , sizeof (int64_t ) * property -> l .length , SEEK_CUR );
134
+ } else {
135
+ fseek (file , property -> l .compressed_length , SEEK_CUR );
136
+ }
137
+ break ;
138
+ }
139
+ case 'i' : { /* i32[] */
140
+ fread (& property -> i .length , sizeof (uint32_t ), 1 , file );
141
+ fread (& property -> i .encoding , sizeof (uint32_t ), 1 , file );
142
+ fread (& property -> i .compressed_length , sizeof (uint32_t ), 1 , file );
143
+ if (property -> i .encoding == 0 ) {
144
+ fseek (file , sizeof (int32_t ) * property -> i .length , SEEK_CUR );
145
+ } else {
146
+ fseek (file , property -> i .compressed_length , SEEK_CUR );
147
+ }
148
+ break ;
149
+ }
150
+ case 'b' : { /* u8[] (bool[]) */
151
+ fread (& property -> b .length , sizeof (uint32_t ), 1 , file );
152
+ fread (& property -> b .encoding , sizeof (uint32_t ), 1 , file );
153
+ fread (& property -> b .compressed_length , sizeof (uint32_t ), 1 , file );
154
+ if (property -> b .encoding == 0 ) {
155
+ fseek (file , sizeof (bool ) * property -> b .length , SEEK_CUR );
156
+ } else {
157
+ fseek (file , property -> b .compressed_length , SEEK_CUR );
158
+ }
159
+ break ;
160
+ }
161
+ case 'S' : { /* raw string */
162
+ fread (& property -> S .length , sizeof (uint32_t ), 1 , file );
163
+ fseek (file , property -> S .length , SEEK_CUR );
164
+ break ;
165
+ }
166
+ case 'R' : { /* raw binary */
167
+ fread (& property -> R .length , sizeof (uint32_t ), 1 , file );
168
+ fseek (file , property -> R .length , SEEK_CUR );
169
+ break ;
170
+ }
171
+ default : {
172
+ return false; // invalid property type
173
+ }
174
+ }
175
+
176
+ return true;
177
+ }
178
+
179
+ bool FBX_ParseNode (FILE * file , FBXNode * node )
180
+ {
181
+ // Read node record
182
+ fread (& node -> end_offset , sizeof (uint32_t ), 1 , file );
183
+ if (ferror (file ) != 0 ) {
184
+ return false;
185
+ }
186
+ fread (& node -> properties_count , sizeof (uint32_t ), 1 , file );
187
+ if (ferror (file ) != 0 ) {
188
+ return false;
189
+ }
190
+ fread (& node -> properties_list_length , sizeof (uint32_t ), 1 , file );
191
+ if (ferror (file ) != 0 ) {
192
+ return false;
193
+ }
194
+ fread (& node -> name_length , sizeof (uint8_t ), 1 , file );
195
+ if (ferror (file ) != 0 ) {
196
+ return false; // failed to read a node
197
+ }
198
+
199
+ // Read node name
200
+ fread (node -> name , 1 , node -> name_length , file );
201
+ if (ferror (file ) != 0 ) {
202
+ return false; // failed to read a node name
203
+ }
204
+ node -> name [node -> name_length ] = '\0' ; // end string with the '\0' character
205
+ // fseek(file, node->name_length, SEEK_CUR);
206
+
207
+ // Read node properties
208
+ FBXNodeProperty property ;
209
+ for (int i = 0 ; i < node -> properties_count ; ++ i ) {
210
+ if (!FBX_ParseNodeProperty (file , & property )) {
211
+ return false;
212
+ }
213
+ }
214
+ // fseek(file, node->properties_list_length, SEEK_CUR);
215
+
216
+ return true;
217
+ }
218
+
219
+ FBXNodeList * FBX_ParseNodes (FILE * file )
220
+ {
221
+ FBXNodeList * list = FBXNodeList_Create (0 );
222
+ if (list == NULL ) {
223
+ return NULL ;
224
+ }
225
+
226
+ while (1 ) {
227
+ FBXNode node ;
228
+ memset (& node , 0 , sizeof (FBXNode ));
229
+ if (FBX_ParseNode (file , & node ) == false) {
230
+ break ;
231
+ }
232
+ if (node .end_offset == 0 ) {
233
+ break ; // no more list
234
+ }
235
+
236
+ if (ftell (file ) != node .end_offset ) {
237
+ // Recursive parse subnodes
238
+ node .subnodes = FBX_ParseNodes (file );
239
+
240
+ // Skip to the next node
241
+ if (fseek (file , node .end_offset , SEEK_SET ) != 0 ) {
242
+ break ;
243
+ }
244
+ }
245
+
246
+ if (FBXNodeList_Insert (list , & node ) == false) {
247
+ break ;
248
+ }
249
+ }
250
+
251
+ FBXNodeList_Truncate (list );
252
+
253
+ return list ;
254
+ }
255
+
256
+ FBXNodeList * FBX_Parse (const char * path )
257
+ {
258
+ FBXNodeList * nodes = NULL ;
259
+
260
+ // Open file handle
261
+ FILE * file = NULL ;
262
+ if (fopen_s (& file , path , "rb" ) != 0 ) {
263
+ goto FUNCTION_END ;
264
+ }
265
+
266
+ // Check file signature
267
+ char buffer [21 ];
268
+ fread (buffer , 21 , 1 , file );
269
+ if (ferror (file ) != 0 ) {
270
+ goto FUNCTION_END ;
271
+ }
272
+ const char * FBX_SIGNATURE = "Kaydara FBX Binary " ;
273
+ if (strncmp (buffer , FBX_SIGNATURE , 20 ) != 0 ) {
274
+ goto FUNCTION_END ;
275
+ }
276
+
277
+ // Skip to the beginning of the first node
278
+ if (fseek (file , 27 , SEEK_SET ) != 0 ) {
279
+ goto FUNCTION_END ;
280
+ }
281
+
282
+ // Read nodes
283
+ nodes = FBX_ParseNodes (file );
284
+
285
+ // Cleanup and exit
286
+ FUNCTION_END :
287
+ if (file != NULL ) {
288
+ fclose (file );
289
+ }
290
+
291
+ return nodes ;
292
+ }
0 commit comments