Skip to content

Commit 7791683

Browse files
committed
Add source code
1 parent 3bb8747 commit 7791683

File tree

5 files changed

+394
-2
lines changed

5 files changed

+394
-2
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
# c-fbx-parser
2-
Compact .FBX parser written in C language
1+
# Compact .FBX parser
2+
3+
Compact .FBX parser written in C language

a.fbx

25.8 KB
Binary file not shown.

fbx.c

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
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+
}

fbx.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#ifndef FBX_H
2+
#define FBX_H
3+
4+
#include <stdbool.h>
5+
#include <stdint.h>
6+
7+
#define FBX_NODE_NAME_MAX 0xFF
8+
9+
struct FBXNode;
10+
struct FBXNodeProperty;
11+
struct FBXNodeList;
12+
13+
typedef struct FBXNode FBXNode;
14+
typedef struct FBXNodeProperty FBXNodeProperty;
15+
typedef struct FBXNodeList FBXNodeList;
16+
17+
struct FBXNodeList {
18+
size_t length;
19+
size_t capacity;
20+
struct FBXNode *nodes;
21+
};
22+
23+
struct FBXNode {
24+
uint32_t end_offset;
25+
uint32_t properties_count;
26+
uint32_t properties_list_length;
27+
uint8_t name_length;
28+
char name[FBX_NODE_NAME_MAX];
29+
struct FBXNodeList *subnodes;
30+
};
31+
32+
struct FBXNodeProperty {
33+
uint8_t type;
34+
union {
35+
struct { int16_t value; } Y; // i16
36+
struct { bool value; } C; // u8
37+
struct { int32_t value; } I; // i32
38+
struct { float value; } F; // f32
39+
struct { double value; } D; // f64
40+
struct { int64_t value; } L; // i64
41+
struct { int length; int encoding; int compressed_length; float *value; } f; // f32[]
42+
struct { int length; int encoding; int compressed_length; double *value; } d; // f64[]
43+
struct { int length; int encoding; int compressed_length; int64_t *value; } l; // i64[]
44+
struct { int length; int encoding; int compressed_length; int32_t *value; } i; // i32[]
45+
struct { int length; int encoding; int compressed_length; bool *value; } b; // u8[]
46+
struct { int length; char *value; } S; // string
47+
struct { int length; uint8_t *value; } R; // raw
48+
void *value;
49+
};
50+
};
51+
52+
FBXNodeList *FBX_Parse(const char *path);
53+
54+
#endif

0 commit comments

Comments
 (0)