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:
#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
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement