240 lines
5.5 KiB
C
240 lines
5.5 KiB
C
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#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;
|
|
}
|