/* This file is part of Iceball. Iceball is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Iceball is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Iceball. If not, see . */ #include "common.h" model_t *model_new(int bonemax) { model_t *pmf = malloc(sizeof(model_t)+sizeof(model_bone_t *)*bonemax); // TODO: check if NULL pmf->bonelen = 0; pmf->bonemax = bonemax; pmf->udtype = UD_PMF; return pmf; } model_t *model_extend(model_t *pmf, int bonemax) { pmf = realloc(pmf, sizeof(model_t)+sizeof(model_bone_t *)*bonemax); // TODO: check if NULL pmf->bonemax = bonemax; return pmf; } void model_free(model_t *pmf) { while(pmf->bonelen != 0) model_bone_free(pmf->bones[pmf->bonelen-1]); free(pmf); } model_bone_t *model_bone_new(model_t *pmf, int ptmax) { model_bone_t *bone = malloc(sizeof(model_bone_t)+sizeof(model_point_t)*ptmax); // TODO: check if NULL bone->ptlen = 0; bone->ptmax = ptmax; bone->parent = pmf; bone->parent_idx = pmf->bonelen++; pmf->bones[bone->parent_idx] = bone; return bone; } model_bone_t *model_bone_extend(model_bone_t *bone, int ptmax) { model_t *pmf = bone->parent; bone = realloc(bone, sizeof(model_bone_t)+sizeof(model_point_t)*ptmax); // TODO: check if NULL pmf->bones[bone->parent_idx] = bone; bone->ptmax = ptmax; return bone; } void model_bone_free(model_bone_t *bone) { int i = bone->parent_idx; bone->parent->bonelen--; for(i = 0; i < bone->parent->bonelen; i++) bone->parent->bones[i] = bone->parent->bones[i+1]; free(bone); } model_t *model_parse_pmf(int len, const char *data) { int i,j; const char *p = data; const char *dend = data + len; // and now we crawl through the spec. // start with the header of "PMF",0x1A,1,0,0,0 char head[8]; if(memcmp(p, "PMF\x1A\x01\x00\x00\x00", 8)) { fprintf(stderr, "model_load_pmf: not a valid PMF v1 file\n"); return NULL; } p += 8; // then there's a uint32_t denoting how many body parts there are uint32_t bone_count = *(uint32_t *)p; p += 4; if(bone_count > MODEL_BONE_MAX) { fprintf(stderr, "model_parse_pmf: too many bones (%i > %i)\n" , bone_count, MODEL_BONE_MAX); return NULL; } model_t *pmf = model_new(bone_count); if(pmf == NULL) { error_perror("model_parse_pmf"); return NULL; } pmf->udtype = UD_PMF; // then, for each body part, for(i = 0; i < (int)bone_count; i++) { // there's a null-terminated 16-byte string (max 15 chars) denoting the part const char *namebuf = p; p += 16; if(namebuf[15] != '\x00') { fprintf(stderr, "model_load_pmf: name not null terminated\n"); model_free(pmf); return NULL; } // then there's a uint32_t denoting how many points there are in this body part uint32_t pt_count = *(uint32_t *)p; p += 4; // (just allocating the bone here) model_bone_t *bone = model_bone_new(pmf, pt_count); pmf = bone->parent; memcpy(bone->name, namebuf, 16); if(bone == NULL) { error_perror("model_parse_pmf"); model_free(pmf); return NULL; } // then there's a whole bunch of this: // uint16_t radius; // int16_t x,y,z; // uint8_t b,g,r,reserved; int bonelen = sizeof(model_point_t)*pt_count; memcpy(bone->pts, p, bonelen); p += bonelen; bone->ptlen = pt_count; // "reserved" needs to be 0 or else you suck // NO SIDECHANNELING YOUR NAME IN THERE // i'm going to enforce this in the loader // and will outright reject files which don't have 0 in ALL of these slots for(j = 0; j < bone->ptmax; j++) if(bone->pts[j].resv1 != 0) { fprintf(stderr, "model_load_pmf: file corrupted or made by a smartass\n"); model_free(pmf); return NULL; } // rinse, lather, repeat // units are 8:8 fixed point in terms of the vxl grid by default } return pmf; } model_t *model_load_pmf(const char *fname) { int flen; char *buf = net_fetch_file(fname, &flen); if(buf == NULL) return NULL; model_t *ret = model_parse_pmf(flen, buf); free(buf); return ret; } int model_save_pmf(model_t *pmf, const char *fname) { int i,j; FILE *fp = fopen(fname, "wb"); // check for errors if(fp == NULL) return error_perror("model_save_pmf"); // and now we crawl through the spec. // start with the header of "PMF",0x1A,1,0,0,0 char head[8]; fwrite("PMF\x1A\x01\x00\x00\x00", 8, 1, fp); // then there's a uint32_t denoting how many body parts there are uint32_t bone_count = pmf->bonelen; fwrite(&bone_count, 4, 1, fp); // then, for each body part, for(i = 0; i < (int)bone_count; i++) { // there's a null-terminated 16-byte string (max 15 chars) denoting the part fwrite(pmf->bones[i]->name, 16, 1, fp); // then there's a uint32_t denoting how many points there are in this body part uint32_t pt_count = pmf->bones[i]->ptlen; fwrite(&pt_count, 4, 1, fp); // then there's a whole bunch of this: // uint16_t radius; // int16_t x,y,z; // uint8_t b,g,r,reserved; fwrite(pmf->bones[i]->pts, sizeof(model_point_t), pt_count, fp); // rinse, lather, repeat // units are 8:8 fixed point in terms of the vxl grid by default } fclose(fp); return 0; }