Alot of my best work happens when I'm distracted. None of them are real 'projects' per se, but I'll just post them here.
Heres one I wrote the day before yesterday, a .BLEND loader. This doesn't actually load model data, it just loads the SDNA and makes parsing it simple:
Code:
Here's an example of how to use it to list the names of all the Meshes in a file
Code:
One thing you should notice is that I'm not using the SDNA to get the name property of the Mesh structure. That what you're supposed to do, but since I didn't this code is specific to Blender 2.49 files
Heres one I wrote the day before yesterday, a .BLEND loader. This doesn't actually load model data, it just loads the SDNA and makes parsing it simple:
Code:
#ifndef BLENDER_IMPORT_H
#define BLENDER_IMPORT_H
#include <stdio.h>
#include <string.h>
/* ##############################################
* # Basic Blender Type Definitions
* ############################################## */
typedef struct { // Blender File Header
char id[7];
char psize;
char endian;
char version[3];
} BLEND_FileHeader;
typedef struct { // File-block header
char id[4];
unsigned int size;
unsigned long oldMemAddr;
unsigned int index;
unsigned int strc_count;
} BLEND_FileBlockHeader;
/* ##############################################
* # Blender SDNA Type Definitions
* ############################################## */
typedef char* BLEND_SDNA_NAME;
typedef char* BLEND_SDNA_TYPE;
typedef short BLEND_SDNA_TLEN;
typedef struct {
short type;
short name;
} BLEND_SDNA_STRC_FIELD;
typedef struct {
short type;
short field_count;
BLEND_SDNA_STRC_FIELD* field;
} BLEND_SDNA_STRC;
typedef struct {
char id[4];
char name_id[4];
unsigned int name_count;
BLEND_SDNA_NAME* name;
char type_id[4];
unsigned int type_count;
BLEND_SDNA_NAME* type;
char tlen_id[4];
BLEND_SDNA_TLEN* tlen;
char strc_id[4];
unsigned int strc_count;
BLEND_SDNA_STRC* strc;
} BLEND_SDNA;
// ##############################################
typedef struct {
FILE* handle;
BLEND_FileHeader* fh;
BLEND_FileBlockHeader* fbh;
BLEND_SDNA* sdna;
} BLEND_FILE;
// ##############################################
/* ##############################################
* # Function Definitions
* ############################################## */
short blend_readShort(BLEND_FILE*);
int blend_readInt(BLEND_FILE*);
long blend_readPointer(BLEND_FILE*);
char* blend_readString(BLEND_FILE*);
void blend_readFBH(BLEND_FILE*);
#define blend_skipFB(bf) fseek(bf->handle, bf->fbh->size, SEEK_CUR);
void blend_loadSDNA(BLEND_FILE*, FILE*);
void blend_clearSDNA(BLEND_SDNA* sdna);
BLEND_FILE* blend_open(char* filename);
void blend_close(BLEND_FILE*);
#define blend_rewind(bf) fseek(bf->handle, 0x0C, SEEK_SET);
int blend_getStructByType(BLEND_FILE*, unsigned int);
bool blend_seekFirstFBByType(BLEND_FILE*, char*);
bool blend_seekFBByType(BLEND_FILE*, char*);
/* ##############################################
* # Load Blender File
* ############################################## */
BLEND_FILE* blend_open(char* filename) {
FILE* fp = fopen(filename, "rb");
if (!fp) return NULL;
BLEND_FILE* bf = (BLEND_FILE*) malloc(sizeof(BLEND_FILE));
if (!bf) {
fclose(fp);
return NULL;
}
bf->handle = fp;
bf->fh = (BLEND_FileHeader*) malloc(sizeof(BLEND_FileHeader));
if (!bf->fh) {
free(bf);
fclose(fp);
return NULL;
}
bf->fbh = (BLEND_FileBlockHeader*) malloc(sizeof(BLEND_FileBlockHeader));
if (!bf->fbh) {
free(bf->fh);
free(bf);
fclose(fp);
return NULL;
}
bf->sdna = (BLEND_SDNA*) malloc(sizeof(BLEND_SDNA));
if (!bf->sdna) {
free(bf->fbh);
free(bf->fh);
free(bf);
fclose(fp);
return NULL;
}
fread(bf->fh, sizeof(BLEND_FileHeader), 1, bf->handle);
while (1) {
blend_readFBH(bf);
if (((unsigned int*)(void*)&bf->fbh->id)[0] == 0x31414E44) break;
else blend_skipFB(bf);
}
blend_loadSDNA(bf, bf->handle);
fseek(bf->handle, 0x0C, SEEK_SET);
return bf;
}
void blend_close(BLEND_FILE* bf) {
blend_clearSDNA(bf->sdna);
free(bf->fh);
free(bf->fbh);
free(bf->sdna);
fclose(bf->handle);
}
/* ##############################################
* # Blender File Reading Functions
* #
* # One of the great things about Blender is that it can
* # run on numerous machines due to the fact that it can
* # store files in big endian or little endian, 32-bit
* # or 64-bit. The routines below read various data types
* # based on that information in the file header.
* ############################################## */
short blend_readShort(BLEND_FILE* bf) {
short val;
fread(&val, 2, 1, bf->handle);
if (bf->fh->endian == 'V')
val = ((val & 0x00FF) << 8) | ((val & 0XFF00) >> 8);
return val;
}
int blend_readInt(BLEND_FILE* bf) {
int val;
fread(&val, 4, 1, bf->handle);
if (bf->fh->endian == 'V')
val = ((val & 0x000000FF) << 24) | ((val & 0xFF000000) >> 24) |
((val & 0x0000FF00) << 8) | ((val & 0x00FF0000) >> 8);
return val;
}
long blend_readPointer(BLEND_FILE* bf) {
long val;
if (bf->fh->psize == '-') {
fread(&val, 8, 1, bf->handle);
if (bf->fh->endian == 'V')
// meh...for some reason I couldn't get bitshifting to work for
// to work on a 64-bit number, so I converted the long value to
// 2 ints, xor-swapped them, and then flipped their endianness
((int*)(void*)&val)[0] ^= ((int*)(void*)&val)[1] ^= ((int*)(void*)&val)[0] ^= ((int*)(void*)&val)[1];
((int*)(void*)&val)[0] = ((((int*)(void*)&val)[0] & 0x000000FF) << 24) | ((((int*)(void*)&val)[0] & 0xFF000000) >> 24) |
((((int*)(void*)&val)[0] & 0x0000FF00) << 8) | ((((int*)(void*)&val)[0] & 0x00FF0000) >> 8);
((int*)(void*)&val)[1] = ((((int*)(void*)&val)[1] & 0x000000FF) << 24) | ((((int*)(void*)&val)[1] & 0xFF000000) >> 24) |
((((int*)(void*)&val)[1] & 0x0000FF00) << 8) | ((((int*)(void*)&val)[1] & 0x00FF0000) >> 8);
} else {
fread(&val, 4, 1, bf->handle);
if (bf->fh->endian == 'V')
val = ((val & 0x000000FF) << 24) | ((val & 0xFF000000) >> 24) |
((val & 0x0000FF00) << 8) | ((val & 0x00FF0000) >> 8);
}
return val;
}
char* blend_readString(BLEND_FILE* bf) {
unsigned int i = 0;
char* str = (char*) malloc(1);
while (1) {
str[i] = fgetc(bf->handle);
if (str[i] == 0) break;
else {
i++;
str = (char*) realloc(str, i+1);
}
}
return str;
}
/* ### Read a file-block header ### */
void blend_readFBH(BLEND_FILE* bf) {
fread(bf->fbh->id, 1, 4, bf->handle);
bf->fbh->size = blend_readInt(bf);
bf->fbh->oldMemAddr = blend_readPointer(bf);
bf->fbh->index = blend_readInt(bf);
bf->fbh->strc_count = blend_readInt(bf);
}
/* ##############################################
* # Load Blender SDNA
* #
* # This function loads an SDNA from a file at its current
* # position.
* ############################################## */
void blend_loadSDNA(BLEND_FILE* bf, FILE* fp) {
FILE* tmp = bf->handle;
bf->handle = fp;
unsigned int i, j;
/* Read in the NAME section */
fread(&bf->sdna->id, 1, 8, bf->handle);
bf->sdna->name_count = blend_readInt(bf);
bf->sdna->name = (BLEND_SDNA_NAME*) malloc(bf->sdna->name_count*sizeof(BLEND_SDNA_NAME));
for (i=0; i<bf->sdna->name_count; i++)
bf->sdna->name[i] = blend_readString(bf);
/* Read in the TYPE section */
fseek(bf->handle, (4-(ftell(bf->handle)%4))%4, SEEK_CUR); // 4-byte aligned
fread(&bf->sdna->type_id, 1, 4, bf->handle);
bf->sdna->type_count = blend_readInt(bf);
bf->sdna->type = (BLEND_SDNA_TYPE*) malloc(bf->sdna->type_count*sizeof(BLEND_SDNA_TYPE));
for (i=0; i<bf->sdna->type_count; i++)
bf->sdna->type[i] = blend_readString(bf);
/* Read in the TLEN section */
fseek(bf->handle, (4-(ftell(bf->handle)%4))%4, SEEK_CUR); // 4-byte aligned
fread(&bf->sdna->tlen_id, 1, 4, bf->handle);
bf->sdna->tlen = (BLEND_SDNA_TLEN*) malloc(bf->sdna->type_count*sizeof(BLEND_SDNA_TLEN));
for (i=0; i<bf->sdna->type_count; i++)
bf->sdna->tlen[i] = blend_readShort(bf);
/* Read in the STRC section */
fseek(bf->handle, (4-(ftell(bf->handle)%4))%4, SEEK_CUR); // 4-byte aligned
fread(&bf->sdna->strc_id, 1, 4, bf->handle);
bf->sdna->strc_count = blend_readInt(bf);
bf->sdna->strc = (BLEND_SDNA_STRC*) malloc(bf->sdna->strc_count*sizeof(BLEND_SDNA_STRC));
for (i=0; i<bf->sdna->strc_count; i++) {
bf->sdna->strc[i].type = blend_readShort(bf);
bf->sdna->strc[i].field_count = blend_readShort(bf);
bf->sdna->strc[i].field = (BLEND_SDNA_STRC_FIELD*) malloc(bf->sdna->strc[i].field_count*sizeof(BLEND_SDNA_STRC_FIELD));
for (j=0; j<(unsigned int)bf->sdna->strc[i].field_count; j++) {
bf->sdna->strc[i].field[j].type = blend_readShort(bf);
bf->sdna->strc[i].field[j].name = blend_readShort(bf);
}
}
bf->handle = tmp;
}
/* ##############################################
* # Change SDNA
* #
* # Sometimes you might wanna use a specific SDNA rather
* # than the one in the blender file. That's what this
* # routine's for.
* ############################################## */
void blend_changeSDNA(BLEND_FILE* bf, BLEND_SDNA* sdna) {
blend_clearSDNA(bf->sdna);
free(bf->sdna);
bf->sdna = sdna;
}
/* ##############################################
* # Clear SDNA
* #
* # Although it doesn't require much memory to do, flattening
* # an SDNA requires many allocations to be made. The function
* # below clears an SDNA, freeing all the memory and erasing
* # the data used by it. It does NOT free the actual SDNA
* # itself. This means that you could use this function to
* # clear and reuse the buffer for another SDNA. You should
* # always clear an SDNA with this function before freeing it.
* ############################################## */
void blend_clearSDNA(BLEND_SDNA* sdna) {
unsigned int i;
for (i=0; i<sdna->name_count; i++)
free(sdna->name[i]);
free(sdna->name);
for (i=0; i<sdna->type_count; i++)
free(sdna->type[i]);
free(sdna->type);
free(sdna->tlen);
for (i=0; i<sdna->strc_count; i++)
free(sdna->strc[i].field);
free(sdna->strc);
}
/* ##############################################
* # Get Structure By Type
* #
* # When searching through the SDNA, you follow a simple
* # process. First, you check whether or not the item is
* # in the list of structures. If it is, then you start
* # all over again with its fields. If it isn't, then
* # it's a plain type and can be read.
* # The function below searches through the list of
* # structures for a particular item type and returns its
* # index. It returns -1 if it isn't listed.
* ############################################## */
int blend_getStructByType(BLEND_FILE* bf, unsigned int type) {
unsigned int i;
for (i=0; i<bf->sdna->strc_count; i++)
if ((unsigned int)bf->sdna->strc[i].type == type) return i;
return -1;
}
bool blend_seekFirstFBByType(BLEND_FILE* bf, char* type) {
blend_rewind(bf);
return blend_seekFBByType(bf, type);
}
bool blend_seekFBByType(BLEND_FILE* bf, char* type) {
while (1) {
blend_readFBH(bf);
if (((unsigned int*)(void*)bf->fbh->id)[0] == 0x42444E45) return false;
if (!strcmp(type, bf->sdna->type[bf->sdna->strc[bf->fbh->index].type])) return true;
blend_skipFB(bf);
}
return true;
}
#endif
Here's an example of how to use it to list the names of all the Meshes in a file
Code:
#include <stdio.h>
#include <malloc.h>
#include "blend_import.h"
int main(int argc, char* argv[]) {
char* name;
BLEND_FILE* bf = blend_open((char*) "test.blend");
if (!bf) return null;
while (blend_seekFBByType(bf, (char*) "Mesh")) {
retpos = ftell(bf->handle)
fseek(bf->handle, 16, SEEK_CUR);
printf("%s\n", blend_readString(bf));
fseek(bf->handle, retpos, 0)
blend_skipFB(bf)
}
blend_close(bf);
return 0;
}
One thing you should notice is that I'm not using the SDNA to get the name property of the Mesh structure. That what you're supposed to do, but since I didn't this code is specific to Blender 2.49 files