/****************************************************************************** libx42 - skinned vertex animation library Copyright (C) 2007 HermitWorks Entertainment Corporation This program 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 2 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA. ******************************************************************************/ #include "local.h" #define swap_i16( l ) ((((l) << 8) & 0xFF00) | (((l) >> 8) & 0xFF)) #define swap_s16( l ) (s16)swap_i16( l ) #define swap_u16( l ) (u16)swap_i16( l ) #define swap_i32( l ) ((((l) << 24) & 0xFF000000) | (((l) << 8) & 0x00FF0000) | (((l) >> 8) & 0x0000FF00) | (((l) >> 24) & 0x000000FF)) #define swap_u32( l ) (u32)swap_i32( l ) ALWAYS_INLINE f32 swap_f32( f32 f ) { union { u32 i; f32 f; } u; u.f = f; u.i = swap_u32( u.i ); return u.f; } X42_EXPORT bool X42_CALL x42_ValidateIdent( const x42Header_ident_t *h ) { #ifndef LIBX42_NO_PARAM_VALIDATION demand_rf( h != NULL, X42_ERR_BADPTR, "Expected valid x42Header pointer, got NULL instead." ); #endif if( h->ident != X42_IDENT ) return false; switch( h->version ) { case X42_VER_V5: break; default: return false; } return true; } X42_EXPORT bool X42_CALL x42_ValidateHeader( const x42header_t *h ) { #ifndef LIBX42_NO_PARAM_VALIDATION demand_rf( h != NULL, X42_ERR_BADPTR, "Expected valid x42Header pointer, got NULL instead." ); #endif if( !x42_ValidateIdent( &h->ident ) ) return false; return true; } typedef struct bufStm_t { const byte *pBuf; size_t cbRead; } bufStm_t; static size_t X42_CALL bufStm_read( void *buffer, size_t cb, void *baton ) { bufStm_t *s = (bufStm_t*)baton; memcpy( buffer, s->pBuf, cb ); s->pBuf += cb; s->cbRead += cb; return cb; } static size_t read_n( void *buffer, size_t cb, x42inStream_t *file_stream ) { byte *buf = (byte*)buffer; size_t cbRead = 0; while( cb ) { size_t cr = file_stream->read_fn( buf, cb, file_stream->baton ); demand_rv( cr <= cb, X42_ERR_BADSTREAM, "stream returned more data than requested", cbRead ); demand_rv( cr != 0, X42_ERR_EOF, "The read stream unexpectadely ended.", cbRead ); cbRead += cr; buf += cr; cb -= cr; } return cbRead; } static bool align_stream( x42inStream_t *file_stream, size_t *pos, size_t a ) { if( a ) { size_t ofs = ((a - (*pos % a)) % a); #ifdef LIBX42_NO_ALLOCA byte tmp[128]; assert( a < sizeof( tmp ), X42_ERR_INTERNAL, "crazy huge alignment value" ); #else byte *tmp = ofs ? (byte*)_alloca( ofs ) : NULL; #endif if( ofs ) *pos += read_n( tmp, ofs, file_stream ); demand_rf( *pos % a == 0, X42_ERR_INTERNAL, "failed to align stream - EOF?" ); } return true; } static bool read_packed_floats( x42inStream_t *file_stream, size_t *inPos, const vec2_t *bounds, uint components, float *dest, uint elements, size_t elemStride ) { uint i; float scale[4], bias[4]; size_t cbElem; if( !elements ) return true; for( i = 0; i < components; i++ ) { bias[i] = bounds[i][0]; scale[i] = (bounds[i][1] - bounds[i][0]) / 65530.0F; } cbElem = sizeof( u16 ) * components; for( i = 0; i < elements; i++ ) { uint c; u16 buf[4]; if( read_n( buf, cbElem, file_stream ) != cbElem ) return false; for( c = 0; c < components; c++ ) dest[c] = buf[c] * scale[c] + bias[c]; dest = (float*)((byte*)dest + elemStride); } *inPos += cbElem * elements; return true; } static bool read_packed_floats_swp( x42inStream_t *file_stream, size_t *inPos, const vec2_t *bounds, uint components, float *dest, uint elements, size_t elemStride ) { uint i; float scale[4], bias[4]; size_t cbElem; if( !elements ) return true; for( i = 0; i < components; i++ ) { bias[i] = bounds[i][0]; scale[i] = (bounds[i][1] - bounds[i][0]) / 65530.0F; } cbElem = sizeof( u16 ) * components; for( i = 0; i < elements; i++ ) { uint c; u16 buf[4]; if( read_n( buf, cbElem, file_stream ) != cbElem ) return false; for( c = 0; c < components; c++ ) dest[c] = (float)(int)swap_u16( buf[c] ) * scale[c] + bias[c]; dest = (float*)((byte*)dest + elemStride); } *inPos += cbElem * elements; return true; } static bool read_packed_floats_s16n( x42inStream_t *file_stream, size_t *inPos, uint components, float *dest, uint elements, size_t elemStride ) { uint i; size_t cbElem; if( !elements ) return true; cbElem = sizeof( s16 ) * components; for( i = 0; i < elements; i++ ) { uint c; s16 buf[4]; if( read_n( buf, cbElem, file_stream ) != cbElem ) return false; for( c = 0; c < components; c++ ) dest[c] = X42_FLOAT_S16N_UNPACK( buf[c] ); dest = (float*)((byte*)dest + elemStride); } *inPos += cbElem * elements; return true; } static bool read_packed_floats_s16n_swp( x42inStream_t *file_stream, size_t *inPos, uint components, float *dest, uint elements, size_t elemStride ) { uint i; size_t cbElem; if( !elements ) return true; cbElem = sizeof( s16 ) * components; for( i = 0; i < elements; i++ ) { uint c; s16 buf[4]; if( read_n( buf, cbElem, file_stream ) != cbElem ) return false; for( c = 0; c < components; c++ ) dest[c] = X42_FLOAT_S16N_UNPACK( swap_s16( buf[c] ) ); dest = (float*)((byte*)dest + elemStride); } *inPos += cbElem * elements; return true; } static bool read_packed_floats_s8n( x42inStream_t *file_stream, size_t *inPos, uint components, float *dest, uint elements, size_t elemStride ) { uint i; size_t cbElem; if( !elements ) return true; cbElem = sizeof( s8 ) * components; for( i = 0; i < elements; i++ ) { uint c; s8 buf[8]; if( read_n( buf, cbElem, file_stream ) != cbElem ) return false; for( c = 0; c < components; c++ ) dest[c] = X42_FLOAT_S8N_UNPACK( buf[c] ); dest = (float*)((byte*)dest + elemStride); } *inPos += cbElem * elements; return true; } #define read_packed_floats_s8n_swp read_packed_floats_s8n static bool skip_block( x42inStream_t *file_stream, size_t cb ) { size_t len; byte tmp[2048]; len = cb; while( len ) { size_t cbRq, cbR; cbRq = len; if( len > sizeof( tmp ) ) len = sizeof( tmp ); cbR = read_n( tmp, cbRq, file_stream ); demand_rf( cbR != 0, X42_ERR_INTERNAL, "failed to skip stream bytes - EOF?" ); len -= cbR; } return true; } X42_EXPORT x42header_t* X42_CALL x42_LoadHeaderFromStream( void *out_buffer, x42inStream_t *file_stream ) { bool swap; x42Header_ident_t ident; x42header_t *ret; #ifndef LIBX42_NO_PARAM_VALIDATION demand_rn( out_buffer != NULL, X42_ERR_BADPTR, "out_buffer is NULL" ); demand_rn( file_stream != NULL, X42_ERR_BADPTR, "file_stream is NULL" ); #endif #define read_check() demand_rn( stat, X42_ERR_INTERNAL, "failed to read from stream" ) #define checked_read( buf, size ) demand_rn( read_n( buf, size, file_stream ) == (size_t)(size), X42_ERR_INTERNAL, "failed to read from stream" ) checked_read( &ident, sizeof( ident ) ); swap = ident.ident == swap_u32( X42_IDENT ); if( swap ) { ident.ident = swap_u32( ident.ident ); ident.version = swap_u32( ident.version ); } demand_rn( x42_ValidateIdent( &ident ), X42_ERR_BADDATA, "invalid x42 data" ); ret = (x42header_t*)out_buffer; memset( ret, 0, sizeof( x42header_t ) ); ret->ident = ident; ret->runFlags = swap ? X42_RF_LOAD_SWAPPED : 0; switch( ident.version ) { case X42_VER_V5: { x42Header_v5_t in; checked_read( &in, sizeof( in ) ); ret->modelFlags = in.modelFlags; ret->baseFrame = in.baseFrame; ret->numFrames = in.numFrames; ret->nameBlobLen = in.nameBlobLen; ret->numBones = in.numBones; ret->numAnimGroups = in.numAnimGroups; ret->numPosValues = in.numPosValues; ret->numScaleValues = in.numScaleValues; ret->numRotValues = in.numRotValues; ret->keyStreamLength = in.keyStreamLength; ret->numAnims = in.numAnims; ret->numTags = in.numTags; ret->numInfluences = in.numInfluences; ret->numLods = in.numLods; ret->numGroups = in.numGroups; ret->numVerts = in.numVerts; ret->numIndices = in.numIndices; ret->boundingSphere = in.boundingSphere; ret->boundingBox = in.boundingBox; } break; NO_DEFAULT; } if( swap ) { ret->modelFlags = swap_u16( ret->modelFlags ); ret->baseFrame = swap_s16( ret->baseFrame ); ret->numFrames = swap_u16( ret->numFrames ); ret->nameBlobLen = swap_u16( ret->nameBlobLen ); ret->numBones = swap_u16( ret->numBones ); ret->numAnimGroups = swap_u16( ret->numAnimGroups ); ret->numPosValues = swap_u16( ret->numPosValues ); ret->numScaleValues = swap_u16( ret->numScaleValues ); ret->numRotValues = swap_u16( ret->numRotValues ); ret->keyStreamLength = swap_u16( ret->keyStreamLength ); ret->numAnims = swap_u16( ret->numAnims ); ret->numTags = swap_u16( ret->numTags ); ret->numInfluences = swap_u16( ret->numInfluences ); ret->numLods = swap_u16( ret->numLods ); ret->numGroups = swap_u16( ret->numGroups ); ret->numVerts = swap_u32( ret->numVerts ); ret->numIndices = swap_u32( ret->numIndices ); ret->boundingBox.mins[0] = swap_f32( ret->boundingBox.mins[0] ); ret->boundingBox.mins[1] = swap_f32( ret->boundingBox.mins[1] ); ret->boundingBox.mins[2] = swap_f32( ret->boundingBox.mins[2] ); ret->boundingBox.maxs[0] = swap_f32( ret->boundingBox.maxs[0] ); ret->boundingBox.maxs[1] = swap_f32( ret->boundingBox.maxs[1] ); ret->boundingBox.maxs[2] = swap_f32( ret->boundingBox.maxs[2] ); ret->boundingSphere.center[0] = swap_f32( ret->boundingSphere.center[0] ); ret->boundingSphere.center[1] = swap_f32( ret->boundingSphere.center[1] ); ret->boundingSphere.center[2] = swap_f32( ret->boundingSphere.center[2] ); ret->boundingSphere.radius = swap_f32( ret->boundingSphere.radius ); } #undef checked_read #undef read_check return ret; } X42_EXPORT x42header_t* X42_CALL x42_LoadHeaderFromBuffer( void *out_buffer, const void **file_data ) { bufStm_t s; x42inStream_t stm; x42header_t *ret; #ifndef LIBX42_NO_PARAM_VALIDATION demand_rn( out_buffer != NULL, X42_ERR_BADPTR, "out_buffer is NULL" ); demand_rn( file_data != NULL, X42_ERR_BADPTR, "file_data is NULL" ); demand_rn( *file_data != NULL, X42_ERR_BADPTR, "*file_data is NULL" ); #endif s.pBuf = (byte*)*file_data; s.cbRead = 0; stm.read_fn = bufStm_read; stm.baton = &s; ret = x42_LoadHeaderFromStream( out_buffer, &stm ); *file_data = s.pBuf; return ret; } static void fixup_persist_flags( uint *pf, const x42header_t *h ) { if( !(h->modelFlags & X42_MF_HAS_NORMALS) ) *pf &= ~X42_PERSIST_NORMALS; if( !(h->modelFlags & X42_MF_HAS_TANGENT_BASIS) ) *pf &= ~X42_PERSIST_TANGENT_BASIS; if( !(h->modelFlags &X42_MF_HAS_TEXTURE_COORDINATES) ) *pf &= ~X42_PERSIST_TEXTURE_COORDINATES; if( !(h->modelFlags & X42_MF_HAS_COLORS) ) *pf &= ~X42_PERSIST_COLORS; } //get the size of buffer a given x42 file (represented by its header) will use in memory X42_EXPORT size_t X42_CALL x42_GetLoadedSize( const x42header_t *h, uint persist_flags ) { size_t ret; #ifndef LIBX42_NO_PARAM_VALIDATION demand_rz( h != NULL, X42_ERR_BADPTR, "Expected valid x42Header pointer, got NULL instead." ); demand_rz( x42_ValidateHeader( h ), X42_ERR_BADDATA, "Invalid x42 file header." ); #endif fixup_persist_flags( &persist_flags, h ); ret = sizeof( x42data_t ); /* The x42data_t struct always resides at the start of the buffer. Sneak in 16 bytes so that the buffer pointer can be pushed to a 16-byte boundary before we lay out the rest of the data. */ ret += sizeof( float[4] ); ret = align( ret, sizeof( void* ) ) + sizeof( x42keyStreamEntry_t ) * h->keyStreamLength; ret = align( ret, sizeof( void* ) ) + sizeof( x42animation_t ) * h->numAnims; ret = align( ret, sizeof( float ) ) + sizeof( vec3_t ) * h->numPosValues; ret = align( ret, sizeof( float ) ) + sizeof( vec3_t ) * h->numScaleValues; ret = align( ret, sizeof( float ) ) + sizeof( quat_t ) * h->numRotValues; ret = align( ret, sizeof( void* ) ) + sizeof( x42animGroup_t ) * h->numAnimGroups; ret = align( ret, sizeof( void* ) ) + sizeof( x42bone_t ) * h->numBones; ret = align( ret, sizeof( void* ) ) + sizeof( u8 ) * h->numBones; ret = align( ret, sizeof( void* ) ) + sizeof( x42influence_t ) * h->numInfluences; ret = align( ret, sizeof( void* ) ) + sizeof( x42tag_t ) * h->numTags; ret = align( ret, sizeof( void* ) ) + sizeof( x42lodRange_t ) * h->numLods; ret = align( ret, sizeof( void* ) ) + sizeof( x42group_t ) * h->numGroups; ret = align( ret, sizeof( float[4] ) ) + sizeof( x42vertAnim_t ) * h->numVerts; if( persist_flags & X42_PERSIST_NORMALS ) ret = align( ret, sizeof( float[4] ) ) + sizeof( x42vertNormal_t ) * h->numVerts; if( persist_flags & X42_PERSIST_TANGENT_BASIS ) ret = align( ret, sizeof( float[4] ) ) + sizeof( x42vertTangent_t ) * h->numVerts; if( persist_flags & X42_PERSIST_TEXTURE_COORDINATES ) ret = align( ret, sizeof( float ) ) + sizeof( vec2_t ) * h->numVerts; if( persist_flags & X42_PERSIST_COLORS ) ret = align( ret, sizeof( rgba_t ) ) + sizeof( rgba_t ) * h->numVerts; ret = align( ret, sizeof( x42index_t ) ) + sizeof( x42index_t ) * h->numIndices; ret += sizeof( char ) * h->nameBlobLen; return ret; } ALWAYS_INLINE void* advance_out_internal( byte **out, size_t a, size_t cb ) { void *ret; *out = align_ptr( *out, a ); ret = *out; *out += cb; return ret; } X42_EXPORT x42data_t* X42_CALL x42_SetupBufferPointers( void *out_buffer, const x42header_t *h, uint persist_flags ) { byte *out; x42data_t *ret; #ifndef LIBX42_NO_PARAM_VALIDATION demand_rn( out_buffer != NULL, X42_ERR_BADPTR, "out_buffer is NULL" ); demand_rn( h != NULL, X42_ERR_BADPTR, "Expected valid x42Header pointer, got NULL instead." ); demand_rn( x42_ValidateHeader( h ), X42_ERR_BADDATA, "Invalid x42 file header." ); #endif fixup_persist_flags( &persist_flags, h ); #define advance_out( a, cb ) advance_out_internal( &out, a, cb ) ret = (x42data_t*)out_buffer; memcpy( &ret->header, h, sizeof( x42header_t ) ); out = (byte*)(ret + 1); out = align_ptr( out, sizeof( float[4] ) ); ret->keyStream = (x42keyStreamEntry_t*)advance_out( sizeof( void* ), sizeof( x42keyStreamEntry_t )*h->keyStreamLength ); ret->animations = (x42animation_t*) advance_out( sizeof( void* ), sizeof( x42animation_t ) * h->numAnims ); ret->posValues = (vec3_t*) advance_out( sizeof( float ), sizeof( vec3_t ) * h->numPosValues ); ret->scaleValues = (vec3_t*) advance_out( sizeof( float ), sizeof( vec3_t ) * h->numScaleValues ); ret->rotValues = (quat_t*) advance_out( sizeof( float ), sizeof( quat_t ) * h->numRotValues ); ret->animGroups = (x42animGroup_t*) advance_out( sizeof( void* ), sizeof( x42animGroup_t ) * h->numAnimGroups ); ret->bones = (x42bone_t*) advance_out( sizeof( void* ), sizeof( x42bone_t ) * h->numBones ); ret->boneGroups = (u8*) advance_out( sizeof( void* ), sizeof( u8 ) * h->numBones ); ret->influences = (x42influence_t*) advance_out( sizeof( void* ), sizeof( x42influence_t ) * h->numInfluences ); ret->tags = (x42tag_t*) advance_out( sizeof( void* ), sizeof( x42tag_t ) * h->numTags ); ret->lods = (x42lodRange_t*) advance_out( sizeof( void* ), sizeof( x42lodRange_t ) * h->numLods ); ret->groups = (x42group_t*) advance_out( sizeof( void* ), sizeof( x42group_t ) * h->numGroups ); ret->vertPos = (x42vertAnim_t*) advance_out( sizeof( float[4] ),sizeof( x42vertAnim_t ) * h->numVerts ); if( persist_flags & X42_PERSIST_NORMALS ) ret->vertNorm = (x42vertNormal_t*) advance_out( sizeof( float[4] ),sizeof( x42vertNormal_t ) * h->numVerts ); else ret->vertNorm = NULL; if( persist_flags & X42_PERSIST_TANGENT_BASIS ) ret->vertTan = (x42vertTangent_t*) advance_out( sizeof( float[4] ),sizeof( x42vertTangent_t ) * h->numVerts ); else ret->vertTan = NULL; if( persist_flags & X42_PERSIST_TEXTURE_COORDINATES ) ret->vertTc = (vec2_t*) advance_out( sizeof( float ), sizeof( vec2_t ) * h->numVerts ); else ret->vertTc = NULL; if( persist_flags & X42_PERSIST_COLORS ) ret->vertCl = (rgba_t*) advance_out( sizeof( rgba_t ), sizeof( rgba_t ) * h->numVerts ); else ret->vertCl = NULL; ret->indices = (x42index_t*) advance_out( sizeof( x42index_t ), sizeof( x42index_t ) * h->numIndices ); ret->strings = (char*) advance_out( sizeof( char ), sizeof( char ) * h->nameBlobLen ); return ret; } X42_EXPORT x42data_t* x42_CopyRuntimeData( void *out_buffer, const x42data_t *src ) { uint persist_flags; const x42header_t *h; x42data_t *ret; x42data_t tmp; #ifndef LIBX42_NO_PARAM_VALIDATION demand_rn( out_buffer != NULL, X42_ERR_BADPTR, "out_buffer is NULL" ); demand_rn( src != NULL, X42_ERR_BADPTR, "Expected valid x42data_t pointer, got NULL instead." ); #endif //src may be unaligned - copy it to stack before reading anything from it memcpy( &tmp, src, sizeof( x42data_t ) ); src = &tmp; #ifndef LIBX42_NO_PARAM_VALIDATION demand_rn( x42_ValidateHeader( &src->header ), X42_ERR_BADDATA, "Invalid x42 file header." ); #endif h = &src->header; persist_flags = 0; if( src->vertNorm ) persist_flags |= X42_PERSIST_NORMALS; if( src->vertTan ) persist_flags |= X42_PERSIST_TANGENT_BASIS; if( src->vertTc ) persist_flags |= X42_PERSIST_TEXTURE_COORDINATES; if( src->vertCl ) persist_flags |= X42_PERSIST_COLORS; ret = x42_SetupBufferPointers( out_buffer, h, persist_flags ); memcpy( ret->keyStream, src->keyStream, sizeof( x42keyStreamEntry_t ) * h->keyStreamLength ); memcpy( ret->animations, src->animations, sizeof( x42animation_t ) * h->numAnims ); memcpy( ret->posValues, src->posValues, sizeof( vec3_t ) * h->numPosValues ); memcpy( ret->scaleValues, src->scaleValues, sizeof( vec3_t ) * h->numScaleValues ); memcpy( ret->rotValues, src->rotValues, sizeof( quat_t ) * h->numRotValues ); memcpy( ret->animGroups, src->animGroups, sizeof( x42animGroup_t ) * h->numAnimGroups ); memcpy( ret->bones, src->bones, sizeof( x42bone_t ) * h->numBones ); memcpy( ret->boneGroups, src->boneGroups, sizeof( u8 ) * h->numBones ); memcpy( ret->influences, src->influences, sizeof( x42influence_t ) * h->numInfluences ); memcpy( ret->tags, src->tags, sizeof( x42tag_t ) * h->numTags ); memcpy( ret->lods, src->lods, sizeof( x42lodRange_t ) * h->numLods ); memcpy( ret->groups, src->groups, sizeof( x42group_t ) * h->numGroups ); memcpy( ret->vertPos, src->vertPos, sizeof( x42vertAnim_t ) * h->numVerts ); if( src->vertNorm ) memcpy( ret->vertNorm, src->vertNorm, sizeof( x42vertNormal_t ) * h->numVerts ); if( src->vertTan ) memcpy( ret->vertTan, src->vertTan, sizeof( x42vertTangent_t ) * h->numVerts ); if( src->vertTc ) memcpy( ret->vertTc, src->vertTc, sizeof( vec2_t ) * h->numVerts ); if( src->vertCl ) memcpy( ret->vertCl, src->vertCl, sizeof( rgba_t ) * h->numVerts ); memcpy( ret->indices, src->indices, sizeof( x42index_t ) * h->numIndices ); memcpy( X42_CONST_CAST( char, ret->strings ), src->strings, sizeof( char ) * h->nameBlobLen ); return ret; } static bool load_direct( x42data_t *ret, const x42header_t *h, x42inStream_t *file_stream, size_t inPos ) { uint i; bool stat; x42PackHeader_v5_t pack; /* Stream read helper macros begin here... */ #define read_check() demand_rf( stat, X42_ERR_INTERNAL, "failed to read from stream" ) #define checked_read( buf, type, count ) \ if( !const_cond_false ) \ { \ size_t cb = sizeof( type ) * count; \ stat = read_n( buf, cb, file_stream ) == cb; \ \ read_check(); \ inPos += cb; \ } \ else \ (void)0 #define checked_align( a ) \ if( !const_cond_false ) \ { \ stat = align_stream( file_stream, &inPos, a ); \ demand_rf( stat, X42_ERR_INTERNAL, "failed to align stream" ); \ } \ else \ (void)0 #define checked_read_a( buf, type, count, a ) \ if( !const_cond_false ) \ { \ checked_align( a ); \ checked_read( buf, type, count ); \ } \ else \ (void)0 #define checked_read_packed_floats( buf, pack, comps, count, stride ) \ if( !const_cond_false ) \ { \ checked_align( 2 ); \ stat = read_packed_floats( file_stream, &inPos, pack, \ comps, (float*)buf, count, stride ); \ read_check(); \ } \ else \ (void)0 #define checked_read_packed_floats_s16n( buf, comps, count, stride ) \ if( !const_cond_false ) \ { \ checked_align( 2 ); \ stat = read_packed_floats_s16n( file_stream, &inPos, \ comps, (float*)buf, count, stride ); \ read_check(); \ } \ else \ (void)0 #define checked_read_packed_floats_s8n( buf, comps, count, stride ) \ if( !const_cond_false ) \ { \ checked_align( 1 ); \ stat = read_packed_floats_s8n( file_stream, &inPos, \ comps, (float*)buf, count, stride ); \ read_check(); \ } \ else \ (void)0 #define checked_skip( size, a ) \ if( !const_cond_false ) \ { \ size_t cb = (size); \ \ checked_align( a ); \ stat = skip_block( file_stream, cb ); \ \ read_check(); \ inPos += cb; \ } \ else \ (void)0 /* ...and end here. */ checked_read( &pack, x42PackHeader_v5_t, 1 ); checked_read_a( ret->bones, x42Bone_v5_t, h->numBones, 8 ); checked_read_a( ret->animGroups, x42AnimGroup_v5_t, h->numAnimGroups, 8 ); for( i = 0; i < h->numAnimGroups; i++ ) { uint j; for( j = ret->animGroups[i].beginBone; j < ret->animGroups[i].endBone; j++ ) ret->boneGroups[j] = (u8)i; } checked_read_packed_floats( ret->posValues, pack.animPosPack, 3, h->numPosValues, sizeof( vec3_t ) ); if( h->modelFlags & X42_MF_UNIFORM_SCALE ) { checked_read_packed_floats( ret->scaleValues, &pack.animScalePack, 1, h->numScaleValues, sizeof( vec3_t ) ); for( i = 0; i < h->numScaleValues; i++ ) { ret->scaleValues[i][1] = ret->scaleValues[i][0]; ret->scaleValues[i][2] = ret->scaleValues[i][0]; } } else { vec2_t bdxyz[3]; bdxyz[0][0] = bdxyz[1][0] = bdxyz[2][0] = pack.animScalePack[0]; bdxyz[0][1] = bdxyz[1][1] = bdxyz[2][1] = pack.animScalePack[1]; checked_read_packed_floats( ret->scaleValues, bdxyz, 3, h->numScaleValues, sizeof( vec3_t ) ); } checked_read_packed_floats_s16n( ret->rotValues, 4, h->numRotValues, sizeof( quat_t ) ); checked_read_a( ret->keyStream, x42KeyStreamEntry_v5_t, h->keyStreamLength, 8 ); checked_read_a( ret->animations, x42Animation_v5_t, h->numAnims, 8 ); checked_read_a( ret->tags, x42Tag_v5_t, h->numTags, 8 ); checked_read_a( ret->influences, x42Influence_v5_t, h->numInfluences, 8 ); checked_read_a( ret->lods, x42LodRange_v5_t, h->numLods, 8 ); checked_read_a( ret->groups, x42Group_v5_t, h->numGroups, 8 ); checked_read_packed_floats( &ret->vertPos[0].pos, pack.vertPosPack, 3, h->numVerts, sizeof( x42vertAnim_t ) ); for( i = 0; i < h->numGroups; i++ ) { uint j; const x42group_t *g = ret->groups + i; size_t cbElem; if( !g->maxVertInfluences ) { for( j = 0; j < g->numVerts; j++ ) { uint k; for( k = 0; k < X42_WEIGHTS_PER_VERT; k++ ) { ret->vertPos[g->firstVert + j].idx[k] = 0; ret->vertPos[g->firstVert + j].wt[k] = 0; } } continue; } cbElem = sizeof( byte ) * ((g->maxVertInfluences - 1) * 2 + 1); for( j = 0; j < g->numVerts; j++ ) { uint k; u8 in[7]; float sum; x42vertAnim_t *v; checked_read( in, u8, cbElem ); v = ret->vertPos + g->firstVert + j; sum = 0; for( k = 0; k < g->maxVertInfluences - 1U; k++ ) { v->idx[k] = in[k * 2 + 0]; v->wt[k] = X42_FLOAT_U8N_UNPACK( in[k * 2 + 1] ); sum += v->wt[k]; } v->idx[k] = in[k * 2 + 0]; v->wt[k] = 1.0F - sum; for( k++; k < X42_WEIGHTS_PER_VERT; k++ ) { v->idx[k] = 0; v->wt[k] = 0; } } } if( ret->vertNorm ) { checked_read_packed_floats_s8n( &ret->vertNorm[0].norm, 3, h->numVerts, sizeof( x42vertNormal_t ) ); } else if( h->modelFlags & X42_MF_HAS_NORMALS ) checked_skip( sizeof( s8[3] ) * h->numVerts, 0 ); if( ret->vertTan ) { checked_read_packed_floats_s8n( &ret->vertTan[0].tan, 7, h->numVerts, sizeof( x42vertTangent_t ) ); for( i = 0; i < h->numVerts; i++ ) ret->vertTan[i].nfac1 = ret->vertTan[i].nfac0; } else if( h->modelFlags & X42_MF_HAS_TANGENT_BASIS ) checked_skip( sizeof( s8[7] ) * h->numVerts, 0 ); if( ret->vertTc ) { checked_read_packed_floats( ret->vertTc, pack.vertTcPack, 2, h->numVerts, sizeof( vec2_t ) ); } else if( h->modelFlags & X42_MF_HAS_TEXTURE_COORDINATES ) checked_skip( sizeof( u16[2] ) * h->numVerts, 2 ); if( ret->vertCl ) { checked_read_a( ret->vertCl, rgba_t, h->numVerts, 4 ); } else if( h->modelFlags & X42_MF_HAS_COLORS ) checked_skip( sizeof( rgba_t ) * h->numVerts, 4 ); checked_read_a( ret->indices, x42Index_t, h->numIndices, 4 ); checked_read( (char*)ret->strings, u8, h->nameBlobLen ); #undef checked_skip #undef checked_read_packed_floats_s8n #undef checked_read_packed_floats_s16n #undef checked_read_packed_floats #undef checked_read_a #undef checked_read #undef checked_align #undef read_check return true; } #define swap_rgba_t( cl ) (void)sizeof( cl ) #define swap_u8( i ) (void)sizeof( i ) #define swap_x42Index_t( idx ) *(idx) = swap_u16( *(idx) ) static void swap_x42PackHeader_v5_t( x42PackHeader_v5_t *pack ) { pack->animPosPack[0][0] = swap_f32( pack->animPosPack[0][0] ); pack->animPosPack[0][1] = swap_f32( pack->animPosPack[0][1] ); pack->animPosPack[1][0] = swap_f32( pack->animPosPack[1][0] ); pack->animPosPack[1][1] = swap_f32( pack->animPosPack[1][1] ); pack->animPosPack[2][0] = swap_f32( pack->animPosPack[2][0] ); pack->animPosPack[2][1] = swap_f32( pack->animPosPack[2][1] ); pack->animScalePack[0] = swap_f32( pack->animScalePack[0] ); pack->animScalePack[1] = swap_f32( pack->animScalePack[1] ); pack->vertPosPack[0][0] = swap_f32( pack->vertPosPack[0][0] ); pack->vertPosPack[0][1] = swap_f32( pack->vertPosPack[0][1] ); pack->vertPosPack[1][0] = swap_f32( pack->vertPosPack[1][0] ); pack->vertPosPack[1][1] = swap_f32( pack->vertPosPack[1][1] ); pack->vertPosPack[2][0] = swap_f32( pack->vertPosPack[2][0] ); pack->vertPosPack[2][1] = swap_f32( pack->vertPosPack[2][1] ); pack->vertTcPack[0][0] = swap_f32( pack->vertTcPack[0][0] ); pack->vertTcPack[0][1] = swap_f32( pack->vertTcPack[0][1] ); pack->vertTcPack[1][0] = swap_f32( pack->vertTcPack[1][0] ); pack->vertTcPack[1][1] = swap_f32( pack->vertTcPack[1][1] ); } static void swap_x42KeyStreamEntry_v5_t( x42KeyStreamEntry_v5_t *ks ) { ks->type = swap_u16( ks->type ); ks->bone = swap_u16( ks->bone ); ks->frame = swap_u16( ks->frame ); ks->value = swap_u16( ks->value ); } static void swap_x42Animation_v5_t( x42Animation_v5_t *anim ) { anim->name = swap_u16( anim->name ); anim->firstFrame = swap_u16( anim->firstFrame ); anim->lastFrame = swap_u16( anim->lastFrame ); anim->loopStart = swap_u16( anim->loopStart ); anim->loopEnd = swap_u16( anim->loopEnd ); anim->frameRate = swap_u16( anim->frameRate ); } static void swap_x42AnimGroup_v5_t( x42AnimGroup_v5_t *group ) { group->name = swap_u16( group->name ); group->beginBone = swap_u16( group->beginBone ); group->endBone = swap_u16( group->endBone ); } static void swap_x42Bone_v5_t( x42Bone_v5_t *bone ) { bone->name = swap_u16( bone->name ); bone->flags = swap_u16( bone->name ); bone->parentIdx = swap_u16( bone->parentIdx ); bone->extent = swap_f32( bone->extent ); } static void swap_affine_t( affine_t *a ) { a->c[0][0] = swap_f32( a->c[0][0] ); a->c[0][1] = swap_f32( a->c[0][1] ); a->c[0][2] = swap_f32( a->c[0][2] ); a->c[1][0] = swap_f32( a->c[1][0] ); a->c[1][1] = swap_f32( a->c[1][1] ); a->c[1][2] = swap_f32( a->c[1][2] ); a->c[2][0] = swap_f32( a->c[2][0] ); a->c[2][1] = swap_f32( a->c[2][1] ); a->c[2][2] = swap_f32( a->c[2][2] ); a->c[3][0] = swap_f32( a->c[3][0] ); a->c[3][1] = swap_f32( a->c[3][1] ); a->c[3][2] = swap_f32( a->c[3][2] ); } static void swap_x42Influence_v5_t( x42Influence_v5_t *inf ) { inf->bone = swap_u16( inf->bone ); swap_affine_t( &inf->meshToBone ); } static void swap_x42Tag_v5_t( x42Tag_v5_t *tag ) { tag->name = swap_u16( tag->name ); tag->bone = swap_u16( tag->bone ); swap_affine_t( &tag->tagMatrix ); } static void swap_x42Group_v5_t( x42Group_v5_t *g ) { uint i; g->material = swap_u16( g->material ); g->surfaceName = swap_u16( g->surfaceName ); g->maxVertInfluences = swap_u16( g->maxVertInfluences ); g->numInfluences = swap_u16( g->numInfluences ); for( i = 0; i < X42_MAX_INFLUENCES_PER_BATCH_V5; i++ ) g->influences[i] = swap_u16( g->influences[i] ); g->primType = swap_u16( g->primType ); g->firstVert = swap_u32( g->firstVert ); g->numVerts = swap_u32( g->numVerts ); g->firstIndex = swap_u32( g->firstIndex ); g->numElems = swap_u32( g->numElems ); } static void swap_x42LodRange_v5_t( x42LodRange_v5_t *lod ) { lod->lodName = swap_u16( lod->lodName ); lod->firstGroup = swap_u16( lod->firstGroup ); lod->numGroups = swap_u16( lod->numGroups ); } static bool load_swapped( x42data_t *ret, const x42header_t *h, x42inStream_t *file_stream, size_t inPos ) { uint i; bool stat; x42PackHeader_v5_t pack; /* Stream read helper macros begin here... */ #define read_check() demand_rf( stat, X42_ERR_INTERNAL, "failed to read from stream" ) #define checked_read( buf, type, count ) \ if( !const_cond_false ) \ { \ uint _i; \ size_t cb = sizeof( type ) * count; \ stat = read_n( buf, cb, file_stream ) == cb; \ \ read_check(); \ inPos += cb; \ \ for( _i = 0; _i < (count); _i++ ) \ swap_##type( buf + _i ); \ } \ else \ (void)0 #define checked_align( a ) \ if( !const_cond_false ) \ { \ stat = align_stream( file_stream, &inPos, a ); \ demand_rf( stat, X42_ERR_INTERNAL, "failed to align stream" ); \ } \ else \ (void)0 #define checked_read_a( buf, type, count, a ) \ if( !const_cond_false ) \ { \ checked_align( a ); \ checked_read( buf, type, count ); \ } \ else \ (void)0 #define checked_read_packed_floats( buf, pack, comps, count, stride ) \ if( !const_cond_false ) \ { \ checked_align( 2 ); \ stat = read_packed_floats_swp( file_stream, &inPos, pack, \ comps, (float*)buf, count, stride ); \ read_check(); \ } \ else \ (void)0 #define checked_read_packed_floats_s16n( buf, comps, count, stride ) \ if( !const_cond_false ) \ { \ checked_align( 2 ); \ stat = read_packed_floats_s16n_swp( file_stream, &inPos, \ comps, (float*)buf, count, stride ); \ read_check(); \ } \ else \ (void)0 #define checked_read_packed_floats_s8n( buf, comps, count, stride ) \ if( !const_cond_false ) \ { \ checked_align( 1 ); \ stat = read_packed_floats_s8n_swp( file_stream, &inPos, \ comps, (float*)buf, count, stride ); \ read_check(); \ } \ else \ (void)0 #define checked_skip( size, a ) \ if( !const_cond_false ) \ { \ size_t cb = (size); \ \ checked_align( a ); \ stat = skip_block( file_stream, cb ); \ \ read_check(); \ inPos += cb; \ } \ else \ (void)0 /* ...and end here. */ checked_read( &pack, x42PackHeader_v5_t, 1 ); checked_read_a( ret->bones, x42Bone_v5_t, h->numBones, 8 ); checked_read_a( ret->animGroups, x42AnimGroup_v5_t, h->numAnimGroups, 8 ); for( i = 0; i < h->numAnimGroups; i++ ) { uint j; for( j = ret->animGroups[i].beginBone; j < ret->animGroups[i].endBone; j++ ) ret->boneGroups[j] = (u8)i; } checked_read_packed_floats( ret->posValues, pack.animPosPack, 3, h->numPosValues, sizeof( vec3_t ) ); if( h->modelFlags & X42_MF_UNIFORM_SCALE ) { checked_read_packed_floats( ret->scaleValues, &pack.animScalePack, 1, h->numScaleValues, sizeof( vec3_t ) ); for( i = 0; i < h->numScaleValues; i++ ) { ret->scaleValues[i][1] = ret->scaleValues[i][0]; ret->scaleValues[i][2] = ret->scaleValues[i][0]; } } else { vec2_t bdxyz[3]; bdxyz[0][0] = bdxyz[1][0] = bdxyz[2][0] = pack.animScalePack[0]; bdxyz[0][1] = bdxyz[1][1] = bdxyz[2][1] = pack.animScalePack[1]; checked_read_packed_floats( ret->scaleValues, bdxyz, 3, h->numScaleValues, sizeof( vec3_t ) ); } checked_read_packed_floats_s16n( ret->rotValues, 4, h->numRotValues, sizeof( quat_t ) ); checked_read_a( ret->keyStream, x42KeyStreamEntry_v5_t, h->keyStreamLength, 8 ); checked_read_a( ret->animations, x42Animation_v5_t, h->numAnims, 8 ); checked_read_a( ret->tags, x42Tag_v5_t, h->numTags, 8 ); checked_read_a( ret->influences, x42Influence_v5_t, h->numInfluences, 8 ); checked_read_a( ret->lods, x42LodRange_v5_t, h->numLods, 8 ); checked_read_a( ret->groups, x42Group_v5_t, h->numGroups, 8 ); checked_read_packed_floats( &ret->vertPos[0].pos, pack.vertPosPack, 3, h->numVerts, sizeof( x42vertAnim_t ) ); for( i = 0; i < h->numGroups; i++ ) { uint j; const x42group_t *g = ret->groups + i; size_t cbElem; if( !g->maxVertInfluences ) { for( j = 0; j < g->numVerts; j++ ) { uint k; for( k = 0; k < X42_WEIGHTS_PER_VERT; k++ ) { ret->vertPos[g->firstVert + j].idx[k] = 0; ret->vertPos[g->firstVert + j].wt[k] = 0; } } continue; } cbElem = sizeof( byte ) * ((g->maxVertInfluences - 1) * 2 + 1); for( j = 0; j < g->numVerts; j++ ) { uint k; u8 in[7]; float sum; x42vertAnim_t *v; checked_read( in, u8, cbElem ); v = ret->vertPos + g->firstVert + j; sum = 0; for( k = 0; k < g->maxVertInfluences - 1U; k++ ) { v->idx[k] = in[k * 2 + 0]; v->wt[k] = X42_FLOAT_U8N_UNPACK( in[k * 2 + 1] ); sum += v->wt[k]; } v->idx[k] = in[k * 2 + 0]; v->wt[k] = 1.0F - sum; for( k++; k < X42_WEIGHTS_PER_VERT; k++ ) { v->idx[k] = 0; v->wt[k] = 0; } } } if( ret->vertNorm ) { checked_read_packed_floats_s8n( &ret->vertNorm[0].norm, 3, h->numVerts, sizeof( x42vertNormal_t ) ); } else if( h->modelFlags & X42_MF_HAS_NORMALS ) checked_skip( sizeof( s8[3] ) * h->numVerts, 0 ); if( ret->vertTan ) { checked_read_packed_floats_s8n( &ret->vertTan[0].tan, 7, h->numVerts, sizeof( x42vertTangent_t ) ); for( i = 0; i < h->numVerts; i++ ) ret->vertTan[i].nfac1 = ret->vertTan[i].nfac0; } else if( h->modelFlags & X42_MF_HAS_TANGENT_BASIS ) checked_skip( sizeof( s8[7] ) * h->numVerts, 0 ); if( ret->vertTc ) { checked_read_packed_floats( ret->vertTc, pack.vertTcPack, 2, h->numVerts, sizeof( vec2_t ) ); } else if( h->modelFlags & X42_MF_HAS_TEXTURE_COORDINATES ) checked_skip( sizeof( u16[2] ) * h->numVerts, 2 ); if( ret->vertCl ) { checked_read_a( ret->vertCl, rgba_t, h->numVerts, 4 ); } else if( h->modelFlags & X42_MF_HAS_COLORS ) checked_skip( sizeof( rgba_t ) * h->numVerts, 4 ); checked_read_a( ret->indices, x42Index_t, h->numIndices, 4 ); checked_read( (char*)ret->strings, u8, h->nameBlobLen ); #undef checked_skip #undef checked_read_packed_floats_s8n #undef checked_read_packed_floats_s16n #undef checked_read_packed_floats #undef checked_read_a #undef checked_read #undef checked_align #undef read_check return true; } /* The actual loader functions follow. The out_buffer parameter must point to a buffer that is at least x42_GetLoadedSize bytes long. The h parameter points to the x42 file's header. The last parameter is a mechanism to get all of the file data that follows the header. So if you're passing in a stream it should be seeked to sizeof( x42Header_t ) bytes into the file. Freeing the file is simple: just free the buffer you passed in and it's gone. All of the data associated with an in-memory x42 file is stored in that buffer. These functions will return NULL if they fail. */ X42_EXPORT x42data_t* X42_CALL x42_LoadDataFromStream( void *out_buffer, const x42header_t *h, uint persist_flags, x42inStream_t *file_stream ) { bool stat; x42data_t *ret; size_t inPos; #ifndef LIBX42_NO_PARAM_VALIDATION demand_rn( out_buffer != NULL, X42_ERR_BADPTR, "out_buffer is NULL" ); demand_rn( h != NULL, X42_ERR_BADPTR, "Expected valid x42Header pointer, got NULL instead." ); demand_rn( x42_ValidateHeader( h ), X42_ERR_BADDATA, "Invalid x42 file header." ); demand_rn( file_stream != NULL, X42_ERR_BADPTR, "file_stream is NULL" ); demand_rn( file_stream->read_fn != NULL, X42_ERR_BADSTREAM, "file_stream has a NULL read_fn" ); #endif ret = x42_SetupBufferPointers( out_buffer, h, persist_flags ); demand_rn( ret != NULL, X42_ERR_INTERNAL, "failed to set up buffer pointers" ); inPos = sizeof( x42Header_ident_t ); switch( h->ident.version ) { case X42_VER_V5: inPos += sizeof( x42Header_v5_t ); break; default: fail_rn( X42_ERR_INTERNAL, "invalid file version" ); } if( h->runFlags & X42_RF_LOAD_SWAPPED ) { stat = load_swapped( ret, h, file_stream, inPos ); } else { stat = load_direct( ret, h, file_stream, inPos ); } demand_rn( stat, X42_ERR_INTERNAL, "failed to load file data" ); return ret; } X42_EXPORT x42data_t* X42_CALL x42_LoadDataFromBuffer( void *out_buffer, const x42header_t *h, uint persist_flags, const void *file_data ) { bufStm_t s; x42inStream_t stm; #ifndef LIBX42_NO_PARAM_VALIDATION demand_rn( out_buffer != NULL, X42_ERR_BADPTR, "out_buffer is NULL" ); demand_rn( h != NULL, X42_ERR_BADPTR, "Expected valid x42Header pointer, got NULL instead." ); demand_rn( x42_ValidateHeader( h ), X42_ERR_BADDATA, "Invalid x42 file header." ); demand_rn( file_data != NULL, X42_ERR_BADPTR, "file_data is NULL" ); #endif s.pBuf = (byte*)file_data; s.cbRead = 0; stm.read_fn = bufStm_read; stm.baton = &s; return x42_LoadDataFromStream( out_buffer, h, persist_flags, &stm ); }