/* ================= R_LoadIQM Load an IQM model and compute the joint matrices for every frame. ================= */ qboolean R_LoadIQM(model_t* mod, void* buffer, int filesize, const char* mod_name) { iqmHeader_t* header; iqmVertexArray_t* vertexarray; iqmTriangle_t* triangle; iqmMesh_t* mesh; iqmJoint_t* joint; iqmPose_t* pose; iqmBounds_t* bounds; unsigned short* framedata; char* str; int i, j; float jointMats[IQM_MAX_JOINTS * 2 * 12]; float* mat; size_t size, joint_names; iqmData_t* iqmData; srfIQModel_t* surface; if (filesize < sizeof(iqmHeader_t)) { return qfalse; } header = (iqmHeader_t*)buffer; if (Q_strncmp(header->magic, IQM_MAGIC, sizeof(header->magic))) { return qfalse; } LL(header->version); if (header->version != IQM_VERSION) { ri.Printf(PRINT_WARNING, "R_LoadIQM: %s is a unsupported IQM version (%d), only version %d is supported.\n", mod_name, header->version, IQM_VERSION); return qfalse; } LL(header->filesize); if (header->filesize > filesize || header->filesize > 16 << 20) { return qfalse; } LL(header->flags); LL(header->num_text); LL(header->ofs_text); LL(header->num_meshes); LL(header->ofs_meshes); LL(header->num_vertexarrays); LL(header->num_vertexes); LL(header->ofs_vertexarrays); LL(header->num_triangles); LL(header->ofs_triangles); LL(header->ofs_adjacency); LL(header->num_joints); LL(header->ofs_joints); LL(header->num_poses); LL(header->ofs_poses); LL(header->num_anims); LL(header->ofs_anims); LL(header->num_frames); LL(header->num_framechannels); LL(header->ofs_frames); LL(header->ofs_bounds); LL(header->num_comment); LL(header->ofs_comment); LL(header->num_extensions); LL(header->ofs_extensions); // check ioq3 joint limit if (header->num_joints > IQM_MAX_JOINTS) { ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %d joints (%d).\n", mod_name, IQM_MAX_JOINTS, header->num_joints); return qfalse; } // check and swap vertex arrays if (IQM_CheckRange(header, header->ofs_vertexarrays, header->num_vertexarrays, sizeof(iqmVertexArray_t))) { return qfalse; } vertexarray = (iqmVertexArray_t*)((byte*)header + header->ofs_vertexarrays); for (i = 0; i < header->num_vertexarrays; i++, vertexarray++) { int j, n, *intPtr; if (vertexarray->size <= 0 || vertexarray->size > 4) { return qfalse; } // total number of values n = header->num_vertexes * vertexarray->size; switch (vertexarray->format) { case IQM_BYTE: case IQM_UBYTE: // 1 byte, no swapping necessary if (IQM_CheckRange(header, vertexarray->offset, n, sizeof(byte))) { return qfalse; } break; case IQM_INT: case IQM_UINT: case IQM_FLOAT: // 4-byte swap if (IQM_CheckRange(header, vertexarray->offset, n, sizeof(float))) { return qfalse; } intPtr = (int*)((byte*)header + vertexarray->offset); for (j = 0; j < n; j++, intPtr++) { LL(*intPtr); } break; default: // not supported return qfalse; break; } switch (vertexarray->type) { case IQM_POSITION: case IQM_NORMAL: if (vertexarray->format != IQM_FLOAT || vertexarray->size != 3) { return qfalse; } break; case IQM_TANGENT: if (vertexarray->format != IQM_FLOAT || vertexarray->size != 4) { return qfalse; } break; case IQM_TEXCOORD: if (vertexarray->format != IQM_FLOAT || vertexarray->size != 2) { return qfalse; } break; case IQM_BLENDINDEXES: case IQM_BLENDWEIGHTS: if (vertexarray->format != IQM_UBYTE || vertexarray->size != 4) { return qfalse; } break; case IQM_COLOR: if (vertexarray->format != IQM_UBYTE || vertexarray->size != 4) { return qfalse; } break; } } // check and swap triangles if (IQM_CheckRange(header, header->ofs_triangles, header->num_triangles, sizeof(iqmTriangle_t))) { return qfalse; } triangle = (iqmTriangle_t*)((byte*)header + header->ofs_triangles); for (i = 0; i < header->num_triangles; i++, triangle++) { LL(triangle->vertex[0]); LL(triangle->vertex[1]); LL(triangle->vertex[2]); if (triangle->vertex[0] > header->num_vertexes || triangle->vertex[1] > header->num_vertexes || triangle->vertex[2] > header->num_vertexes) { return qfalse; } } // check and swap meshes if (IQM_CheckRange(header, header->ofs_meshes, header->num_meshes, sizeof(iqmMesh_t))) { return qfalse; } mesh = (iqmMesh_t*)((byte*)header + header->ofs_meshes); for (i = 0; i < header->num_meshes; i++, mesh++) { LL(mesh->name); LL(mesh->material); LL(mesh->first_vertex); LL(mesh->num_vertexes); LL(mesh->first_triangle); LL(mesh->num_triangles); // check ioq3 limits if (mesh->num_vertexes > SHADER_MAX_VERTEXES) { ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i verts on a surface (%i).\n", mod_name, SHADER_MAX_VERTEXES, mesh->num_vertexes); return qfalse; } if (mesh->num_triangles * 3 > SHADER_MAX_INDEXES) { ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i triangles on a surface (%i).\n", mod_name, SHADER_MAX_INDEXES / 3, mesh->num_triangles); return qfalse; } if (mesh->first_vertex >= header->num_vertexes || mesh->first_vertex + mesh->num_vertexes > header->num_vertexes || mesh->first_triangle >= header->num_triangles || mesh->first_triangle + mesh->num_triangles > header->num_triangles || mesh->name >= header->num_text || mesh->material >= header->num_text) { return qfalse; } } // check and swap joints if (IQM_CheckRange(header, header->ofs_joints, header->num_joints, sizeof(iqmJoint_t))) { return qfalse; } joint = (iqmJoint_t*)((byte*)header + header->ofs_joints); joint_names = 0; for (i = 0; i < header->num_joints; i++, joint++) { LL(joint->name); LL(joint->parent); LL(joint->translate[0]); LL(joint->translate[1]); LL(joint->translate[2]); LL(joint->rotate[0]); LL(joint->rotate[1]); LL(joint->rotate[2]); LL(joint->rotate[3]); LL(joint->scale[0]); LL(joint->scale[1]); LL(joint->scale[2]); if (joint->parent < -1 || joint->parent >= (int)header->num_joints || joint->name >= (int)header->num_text) { return qfalse; } joint_names += strlen((char*)header + header->ofs_text + joint->name) + 1; } // check and swap poses if (header->num_poses != header->num_joints) { return qfalse; } if (IQM_CheckRange(header, header->ofs_poses, header->num_poses, sizeof(iqmPose_t))) { return qfalse; } pose = (iqmPose_t*)((byte*)header + header->ofs_poses); for (i = 0; i < header->num_poses; i++, pose++) { LL(pose->parent); LL(pose->mask); LL(pose->channeloffset[0]); LL(pose->channeloffset[1]); LL(pose->channeloffset[2]); LL(pose->channeloffset[3]); LL(pose->channeloffset[4]); LL(pose->channeloffset[5]); LL(pose->channeloffset[6]); LL(pose->channeloffset[7]); LL(pose->channeloffset[8]); LL(pose->channeloffset[9]); LL(pose->channelscale[0]); LL(pose->channelscale[1]); LL(pose->channelscale[2]); LL(pose->channelscale[3]); LL(pose->channelscale[4]); LL(pose->channelscale[5]); LL(pose->channelscale[6]); LL(pose->channelscale[7]); LL(pose->channelscale[8]); LL(pose->channelscale[9]); } if (header->ofs_bounds) { // check and swap model bounds if (IQM_CheckRange(header, header->ofs_bounds, header->num_frames, sizeof(*bounds))) { return qfalse; } bounds = (iqmBounds_t*)((byte*) header + header->ofs_bounds); for (i = 0; i < header->num_frames; i++) { LL(bounds->bbmin[0]); LL(bounds->bbmin[1]); LL(bounds->bbmin[2]); LL(bounds->bbmax[0]); LL(bounds->bbmax[1]); LL(bounds->bbmax[2]); bounds++; } } // allocate the model and copy the data size = sizeof(iqmData_t); size += header->num_meshes * sizeof(srfIQModel_t); size += header->num_joints * header->num_frames * 12 * sizeof(float); if (header->ofs_bounds) size += header->num_frames * 6 * sizeof(float); // model bounds size += header->num_vertexes * 3 * sizeof(float); // positions size += header->num_vertexes * 2 * sizeof(float); // texcoords size += header->num_vertexes * 3 * sizeof(float); // normals size += header->num_vertexes * 4 * sizeof(float); // tangents size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes size += header->num_vertexes * 4 * sizeof(byte); // blendWeights size += header->num_vertexes * 4 * sizeof(byte); // colors size += header->num_joints * sizeof(int); // parents size += header->num_triangles * 3 * sizeof(int); // triangles size += joint_names; // joint names mod->type = MOD_IQM; iqmData = (iqmData_t*)ri.Hunk_Alloc(size, h_low); mod->modelData = iqmData; // fill header iqmData->num_vertexes = header->num_vertexes; iqmData->num_triangles = header->num_triangles; iqmData->num_frames = header->num_frames; iqmData->num_surfaces = header->num_meshes; iqmData->num_joints = header->num_joints; iqmData->surfaces = (srfIQModel_t*)(iqmData + 1); iqmData->poseMats = (float*)(iqmData->surfaces + iqmData->num_surfaces); if (header->ofs_bounds) { iqmData->bounds = iqmData->poseMats + 12 * header->num_joints * header->num_frames; iqmData->positions = iqmData->bounds + 6 * header->num_frames; } else iqmData->positions = iqmData->poseMats + 12 * header->num_joints * header->num_frames; iqmData->texcoords = iqmData->positions + 3 * header->num_vertexes; iqmData->normals = iqmData->texcoords + 2 * header->num_vertexes; iqmData->tangents = iqmData->normals + 3 * header->num_vertexes; iqmData->blendIndexes = (byte*)(iqmData->tangents + 4 * header->num_vertexes); iqmData->blendWeights = iqmData->blendIndexes + 4 * header->num_vertexes; iqmData->colors = iqmData->blendWeights + 4 * header->num_vertexes; iqmData->jointParents = (int*)(iqmData->colors + 4 * header->num_vertexes); iqmData->triangles = iqmData->jointParents + header->num_joints; iqmData->names = (char*)(iqmData->triangles + 3 * header->num_triangles); // calculate joint matrices and their inverses // they are needed only until the pose matrices are calculated mat = jointMats; joint = (iqmJoint_t*)((byte*)header + header->ofs_joints); for (i = 0; i < header->num_joints; i++, joint++) { float baseFrame[12], invBaseFrame[12]; JointToMatrix(joint->rotate, joint->scale, joint->translate, baseFrame); Matrix34Invert(baseFrame, invBaseFrame); if (joint->parent >= 0) { Matrix34Multiply(jointMats + 2 * 12 * joint->parent, baseFrame, mat); mat += 12; Matrix34Multiply(invBaseFrame, jointMats + 2 * 12 * joint->parent + 12, mat); mat += 12; } else { Com_Memcpy(mat, baseFrame, sizeof(baseFrame)); mat += 12; Com_Memcpy(mat, invBaseFrame, sizeof(invBaseFrame)); mat += 12; } } // calculate pose matrices framedata = (unsigned short*)((byte*)header + header->ofs_frames); mat = iqmData->poseMats; for (i = 0; i < header->num_frames; i++) { pose = (iqmPose_t*)((byte*)header + header->ofs_poses); for (j = 0; j < header->num_poses; j++, pose++) { vec3_t translate; vec4_t rotate; vec3_t scale; float mat1[12], mat2[12]; translate[0] = pose->channeloffset[0]; if (pose->mask & 0x001) translate[0] += *framedata++ * pose->channelscale[0]; translate[1] = pose->channeloffset[1]; if (pose->mask & 0x002) translate[1] += *framedata++ * pose->channelscale[1]; translate[2] = pose->channeloffset[2]; if (pose->mask & 0x004) translate[2] += *framedata++ * pose->channelscale[2]; rotate[0] = pose->channeloffset[3]; if (pose->mask & 0x008) rotate[0] += *framedata++ * pose->channelscale[3]; rotate[1] = pose->channeloffset[4]; if (pose->mask & 0x010) rotate[1] += *framedata++ * pose->channelscale[4]; rotate[2] = pose->channeloffset[5]; if (pose->mask & 0x020) rotate[2] += *framedata++ * pose->channelscale[5]; rotate[3] = pose->channeloffset[6]; if (pose->mask & 0x040) rotate[3] += *framedata++ * pose->channelscale[6]; scale[0] = pose->channeloffset[7]; if (pose->mask & 0x080) scale[0] += *framedata++ * pose->channelscale[7]; scale[1] = pose->channeloffset[8]; if (pose->mask & 0x100) scale[1] += *framedata++ * pose->channelscale[8]; scale[2] = pose->channeloffset[9]; if (pose->mask & 0x200) scale[2] += *framedata++ * pose->channelscale[9]; // construct transformation matrix JointToMatrix(rotate, scale, translate, mat1); if (pose->parent >= 0) { Matrix34Multiply(jointMats + 12 * 2 * pose->parent, mat1, mat2); } else { Com_Memcpy(mat2, mat1, sizeof(mat1)); } Matrix34Multiply(mat2, jointMats + 12 * (2 * j + 1), mat); mat += 12; } } // register shaders // overwrite the material offset with the shader index mesh = (iqmMesh_t*)((byte*)header + header->ofs_meshes); surface = iqmData->surfaces; str = (char*)header + header->ofs_text; for (i = 0; i < header->num_meshes; i++, mesh++, surface++) { surface->surfaceType = SF_IQM; Q_strncpyz(surface->name, str + mesh->name, sizeof(surface->name)); Q_strlwr(surface->name); // lowercase the surface name so skin compares are faster surface->shader = R_FindShader(str + mesh->material, LIGHTMAP_NONE, qtrue); if (surface->shader->defaultShader) surface->shader = tr.defaultShader; surface->data = iqmData; surface->first_vertex = mesh->first_vertex; surface->num_vertexes = mesh->num_vertexes; surface->first_triangle = mesh->first_triangle; surface->num_triangles = mesh->num_triangles; } // copy vertexarrays and indexes vertexarray = (iqmVertexArray_t*)((byte*)header + header->ofs_vertexarrays); for (i = 0; i < header->num_vertexarrays; i++, vertexarray++) { int n; // total number of values n = header->num_vertexes * vertexarray->size; switch (vertexarray->type) { case IQM_POSITION: Com_Memcpy(iqmData->positions, (byte*)header + vertexarray->offset, n * sizeof(float)); break; case IQM_NORMAL: Com_Memcpy(iqmData->normals, (byte*)header + vertexarray->offset, n * sizeof(float)); break; case IQM_TANGENT: Com_Memcpy(iqmData->tangents, (byte*)header + vertexarray->offset, n * sizeof(float)); break; case IQM_TEXCOORD: Com_Memcpy(iqmData->texcoords, (byte*)header + vertexarray->offset, n * sizeof(float)); break; case IQM_BLENDINDEXES: Com_Memcpy(iqmData->blendIndexes, (byte*)header + vertexarray->offset, n * sizeof(byte)); break; case IQM_BLENDWEIGHTS: Com_Memcpy(iqmData->blendWeights, (byte*)header + vertexarray->offset, n * sizeof(byte)); break; case IQM_COLOR: Com_Memcpy(iqmData->colors, (byte*)header + vertexarray->offset, n * sizeof(byte)); break; } } // copy joint parents joint = (iqmJoint_t*)((byte*)header + header->ofs_joints); for (i = 0; i < header->num_joints; i++, joint++) { iqmData->jointParents[i] = joint->parent; } // copy triangles triangle = (iqmTriangle_t*)((byte*)header + header->ofs_triangles); for (i = 0; i < header->num_triangles; i++, triangle++) { iqmData->triangles[3 * i + 0] = triangle->vertex[0]; iqmData->triangles[3 * i + 1] = triangle->vertex[1]; iqmData->triangles[3 * i + 2] = triangle->vertex[2]; } // copy joint names str = iqmData->names; joint = (iqmJoint_t*)((byte*)header + header->ofs_joints); for (i = 0; i < header->num_joints; i++, joint++) { char* name = (char*)header + header->ofs_text + joint->name; int len = strlen(name) + 1; Com_Memcpy(str, name, len); str += len; } // copy model bounds if (header->ofs_bounds) { mat = iqmData->bounds; bounds = (iqmBounds_t*)((byte*) header + header->ofs_bounds); for (i = 0; i < header->num_frames; i++) { mat[0] = bounds->bbmin[0]; mat[1] = bounds->bbmin[1]; mat[2] = bounds->bbmin[2]; mat[3] = bounds->bbmax[0]; mat[4] = bounds->bbmax[1]; mat[5] = bounds->bbmax[2]; mat += 6; bounds++; } } return qtrue; }
static bool LoadIQMFile( void *buffer, unsigned filesize, const char *mod_name, size_t *len_names ) { iqmHeader_t *header; iqmVertexArray_t *vertexarray; iqmTriangle_t *triangle; iqmMesh_t *mesh; iqmJoint_t *joint; iqmPose_t *pose; iqmBounds_t *bounds; iqmAnim_t *anim; if( filesize < sizeof(iqmHeader_t) ) { Log::Warn("R_LoadIQModel: file size of %s is too small.", mod_name ); return false; } header = (iqmHeader_t *)buffer; if( Q_strncmp( header->magic, IQM_MAGIC, sizeof(header->magic) ) ) { Log::Warn("R_LoadIQModel: file %s doesn't contain an IQM header.", mod_name ); return false; } LL( header->version ); if( header->version != IQM_VERSION ) { Log::Warn("R_LoadIQM: %s is a unsupported IQM version (%d), only version %d is supported.", mod_name, header->version, IQM_VERSION); return false; } LL( header->filesize ); if( header->filesize > filesize || header->filesize > 1<<24 ) { Log::Warn("R_LoadIQM: %s has an invalid file size %d.", mod_name, header->filesize ); return false; } LL( header->flags ); LL( header->num_text ); LL( header->ofs_text ); LL( header->num_meshes ); LL( header->ofs_meshes ); LL( header->num_vertexarrays ); LL( header->num_vertexes ); LL( header->ofs_vertexarrays ); LL( header->num_triangles ); LL( header->ofs_triangles ); LL( header->ofs_adjacency ); LL( header->num_joints ); LL( header->ofs_joints ); LL( header->num_poses ); LL( header->ofs_poses ); LL( header->num_anims ); LL( header->ofs_anims ); LL( header->num_frames ); LL( header->num_framechannels ); LL( header->ofs_frames ); LL( header->ofs_bounds ); LL( header->num_comment ); LL( header->ofs_comment ); LL( header->num_extensions ); LL( header->ofs_extensions ); // check and swap vertex arrays if( IQM_CheckRange( header, header->ofs_vertexarrays, header->num_vertexarrays, sizeof(iqmVertexArray_t), mod_name, "vertexarray" ) ) { return false; } vertexarray = ( iqmVertexArray_t* )IQMPtr( header, header->ofs_vertexarrays ); for(unsigned i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) { int j, n, *intPtr; if( vertexarray->size <= 0 || vertexarray->size > 4 ) { Log::Warn("R_LoadIQM: %s contains an invalid vertexarray size.", mod_name ); return false; } // total number of values n = header->num_vertexes * vertexarray->size; switch( vertexarray->format ) { case IQM_BYTE: case IQM_UBYTE: // 1 byte, no swapping necessary if( IQM_CheckRange( header, vertexarray->offset, n, sizeof(byte), mod_name, "vertexarray" ) ) { return false; } break; case IQM_INT: case IQM_UINT: case IQM_FLOAT: // 4-byte swap if( IQM_CheckRange( header, vertexarray->offset, n, sizeof(float), mod_name, "vertexarray" ) ) { return false; } intPtr = ( int* )IQMPtr( header, vertexarray->offset ); for( j = 0; j < n; j++, intPtr++ ) { LL( *intPtr ); } break; default: // not supported Log::Warn("R_LoadIQM: file %s uses an unsupported vertex format.", mod_name ); return false; break; } switch( vertexarray->type ) { case IQM_POSITION: case IQM_NORMAL: if( vertexarray->format != IQM_FLOAT || vertexarray->size != 3 ) { Log::Warn("R_LoadIQM: file %s uses an unsupported vertex format.", mod_name ); return false; } break; case IQM_TANGENT: if( vertexarray->format != IQM_FLOAT || vertexarray->size != 4 ) { Log::Warn("R_LoadIQM: file %s uses an unsupported vertex format.", mod_name ); return false; } break; case IQM_TEXCOORD: if( vertexarray->format != IQM_FLOAT || vertexarray->size != 2 ) { Log::Warn("R_LoadIQM: file %s uses an unsupported vertex format.", mod_name ); return false; } break; case IQM_BLENDINDEXES: case IQM_BLENDWEIGHTS: if( vertexarray->format != IQM_UBYTE || vertexarray->size != 4 ) { Log::Warn("R_LoadIQM: file %s uses an unsupported vertex format.", mod_name ); return false; } break; case IQM_COLOR: if( vertexarray->format != IQM_UBYTE || vertexarray->size != 4 ) { Log::Warn("R_LoadIQM: file %s uses an unsupported vertex format.", mod_name ); return false; } break; } } // check and swap triangles if( IQM_CheckRange( header, header->ofs_triangles, header->num_triangles, sizeof(iqmTriangle_t), mod_name, "triangle" ) ) { return false; } triangle = ( iqmTriangle_t* )IQMPtr( header, header->ofs_triangles ); for(unsigned i = 0; i < header->num_triangles; i++, triangle++ ) { LL( triangle->vertex[0] ); LL( triangle->vertex[1] ); LL( triangle->vertex[2] ); if( triangle->vertex[0] > header->num_vertexes || triangle->vertex[1] > header->num_vertexes || triangle->vertex[2] > header->num_vertexes ) { return false; } } *len_names = 0; // check and swap meshes if( IQM_CheckRange( header, header->ofs_meshes, header->num_meshes, sizeof(iqmMesh_t), mod_name, "mesh" ) ) { return false; } mesh = ( iqmMesh_t* )IQMPtr( header, header->ofs_meshes ); for(unsigned i = 0; i < header->num_meshes; i++, mesh++) { LL( mesh->name ); LL( mesh->material ); LL( mesh->first_vertex ); LL( mesh->num_vertexes ); LL( mesh->first_triangle ); LL( mesh->num_triangles ); if( mesh->first_vertex >= header->num_vertexes || mesh->first_vertex + mesh->num_vertexes > header->num_vertexes || mesh->first_triangle >= header->num_triangles || mesh->first_triangle + mesh->num_triangles > header->num_triangles || mesh->name >= header->num_text || mesh->material >= header->num_text ) { Log::Warn("R_LoadIQM: file %s contains an invalid mesh.", mod_name ); return false; } *len_names += strlen( ( char* )IQMPtr( header, header->ofs_text + mesh->name ) ) + 1; } // check and swap joints if( header->num_joints != 0 && IQM_CheckRange( header, header->ofs_joints, header->num_joints, sizeof(iqmJoint_t), mod_name, "joint" ) ) { return false; } joint = ( iqmJoint_t* )IQMPtr( header, header->ofs_joints ); for(unsigned i = 0; i < header->num_joints; i++, joint++ ) { LL( joint->name ); LL( joint->parent ); LL( joint->translate[0] ); LL( joint->translate[1] ); LL( joint->translate[2] ); LL( joint->rotate[0] ); LL( joint->rotate[1] ); LL( joint->rotate[2] ); LL( joint->rotate[3] ); LL( joint->scale[0] ); LL( joint->scale[1] ); LL( joint->scale[2] ); if( joint->parent < -1 || joint->parent >= (int)header->num_joints || joint->name >= header->num_text ) { Log::Warn("R_LoadIQM: file %s contains an invalid joint.", mod_name ); return false; } if( joint->scale[0] < 0.0f || (int)( joint->scale[0] - joint->scale[1] ) || (int)( joint->scale[1] - joint->scale[2] ) ) { Log::Warn("R_LoadIQM: file %s contains an invalid scale: %f %f %f", mod_name, joint->scale[0], joint->scale[1], joint->scale[2] ); return false; } *len_names += strlen( ( char* )IQMPtr( header, header->ofs_text + joint->name ) ) + 1; } // check and swap poses if( header->ofs_poses > 0 ) { if( IQM_CheckRange( header, header->ofs_poses, header->num_poses, sizeof(iqmPose_t), mod_name, "pose" ) ) { return false; } pose = ( iqmPose_t* )IQMPtr( header, header->ofs_poses ); for(unsigned i = 0; i < header->num_poses; i++, pose++ ) { LL( pose->parent ); LL( pose->mask ); LL( pose->channeloffset[0] ); LL( pose->channeloffset[1] ); LL( pose->channeloffset[2] ); LL( pose->channeloffset[3] ); LL( pose->channeloffset[4] ); LL( pose->channeloffset[5] ); LL( pose->channeloffset[6] ); LL( pose->channeloffset[7] ); LL( pose->channeloffset[8] ); LL( pose->channeloffset[9] ); LL( pose->channelscale[0] ); LL( pose->channelscale[1] ); LL( pose->channelscale[2] ); LL( pose->channelscale[3] ); LL( pose->channelscale[4] ); LL( pose->channelscale[5] ); LL( pose->channelscale[6] ); LL( pose->channelscale[7] ); LL( pose->channelscale[8] ); LL( pose->channelscale[9] ); } } if( header->ofs_bounds ) { // check and swap model bounds if(IQM_CheckRange(header, header->ofs_bounds, header->num_frames, sizeof(*bounds), mod_name, "bounds" )) { return false; } bounds = ( iqmBounds_t* )IQMPtr( header, header->ofs_bounds ); for(unsigned i = 0; i < header->num_poses; i++) { LL(bounds->bbmin[0]); LL(bounds->bbmin[1]); LL(bounds->bbmin[2]); LL(bounds->bbmax[0]); LL(bounds->bbmax[1]); LL(bounds->bbmax[2]); bounds++; } } // check and swap animations if( header->ofs_anims ) { if( IQM_CheckRange( header, header->ofs_anims, header->num_anims, sizeof(iqmAnim_t), mod_name, "animation" ) ) { return false; } anim = ( iqmAnim_t* )IQMPtr( header, header->ofs_anims ); for(unsigned i = 0; i < header->num_anims; i++, anim++ ) { LL( anim->name ); LL( anim->first_frame ); LL( anim->num_frames ); LL( anim->framerate ); LL( anim->flags ); *len_names += strlen( ( char* )IQMPtr( header, header->ofs_text + anim->name ) ) + 1; } } return true; }