]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/lwo/lwo2.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / libs / picomodel / lwo / lwo2.c
1 /*\r
2 ======================================================================\r
3 lwo2.c\r
4 \r
5 The entry point for loading LightWave object files.\r
6 \r
7 Ernie Wright  17 Sep 00\r
8 ====================================================================== */\r
9 \r
10 #include "../picointernal.h"\r
11 #include "lwo2.h"\r
12 \r
13 /* disable warnings */\r
14 #ifdef _WIN32\r
15 #pragma warning( disable:4018 )         /* signed/unsigned mismatch */\r
16 #endif\r
17 \r
18 \r
19 /*\r
20 ======================================================================\r
21 lwFreeLayer()\r
22 \r
23 Free memory used by an lwLayer.\r
24 ====================================================================== */\r
25 \r
26 void lwFreeLayer( lwLayer *layer )\r
27 {\r
28    if ( layer ) {\r
29       if ( layer->name ) _pico_free( layer->name );\r
30       lwFreePoints( &layer->point );\r
31       lwFreePolygons( &layer->polygon );\r
32       lwListFree( layer->vmap, lwFreeVMap );\r
33       _pico_free( layer );\r
34    }\r
35 }\r
36 \r
37 \r
38 /*\r
39 ======================================================================\r
40 lwFreeObject()\r
41 \r
42 Free memory used by an lwObject.\r
43 ====================================================================== */\r
44 \r
45 void lwFreeObject( lwObject *object )\r
46 {\r
47    if ( object ) {\r
48       lwListFree( object->layer, lwFreeLayer );\r
49       lwListFree( object->env, lwFreeEnvelope );\r
50       lwListFree( object->clip, lwFreeClip );\r
51       lwListFree( object->surf, lwFreeSurface );\r
52       lwFreeTags( &object->taglist );\r
53       _pico_free( object );\r
54    }\r
55 }\r
56 \r
57 \r
58 /*\r
59 ======================================================================\r
60 lwGetObject()\r
61 \r
62 Returns the contents of a LightWave object, given its filename, or\r
63 NULL if the file couldn't be loaded.  On failure, failID and failpos\r
64 can be used to diagnose the cause.\r
65 \r
66 1.  If the file isn't an LWO2 or an LWOB, failpos will contain 12 and\r
67     failID will be unchanged.\r
68 \r
69 2.  If an error occurs while reading, failID will contain the most\r
70     recently read IFF chunk ID, and failpos will contain the value\r
71     returned by _pico_memstream_tell() at the time of the failure.\r
72 \r
73 3.  If the file couldn't be opened, or an error occurs while reading\r
74     the first 12 bytes, both failID and failpos will be unchanged.\r
75 \r
76 If you don't need this information, failID and failpos can be NULL.\r
77 ====================================================================== */\r
78 \r
79 lwObject *lwGetObject( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos )\r
80 {\r
81    lwObject *object;\r
82    lwLayer *layer;\r
83    lwNode *node;\r
84    unsigned int id, formsize, type, cksize;\r
85    int i, rlen;\r
86 \r
87    /* open the file */\r
88 \r
89    if ( !fp ) return NULL;\r
90 \r
91    /* read the first 12 bytes */\r
92 \r
93    set_flen( 0 );\r
94    id       = getU4( fp );\r
95    formsize = getU4( fp );\r
96    type     = getU4( fp );\r
97    if ( 12 != get_flen() ) {\r
98       return NULL;\r
99    }\r
100 \r
101    /* is this a LW object? */\r
102 \r
103    if ( id != ID_FORM ) {\r
104       if ( failpos ) *failpos = 12;\r
105       return NULL;\r
106    }\r
107 \r
108    if ( type != ID_LWO2 ) {\r
109       if ( type == ID_LWOB )\r
110          return lwGetObject5( filename, fp, failID, failpos );\r
111       else {\r
112          if ( failpos ) *failpos = 12;\r
113          return NULL;\r
114       }\r
115    }\r
116 \r
117    /* allocate an object and a default layer */\r
118 \r
119    object = _pico_calloc( 1, sizeof( lwObject ));\r
120    if ( !object ) goto Fail;\r
121 \r
122    layer = _pico_calloc( 1, sizeof( lwLayer ));\r
123    if ( !layer ) goto Fail;\r
124    object->layer = layer;\r
125 \r
126    /* get the first chunk header */\r
127 \r
128    id = getU4( fp );\r
129    cksize = getU4( fp );\r
130    if ( 0 > get_flen() ) goto Fail;\r
131 \r
132    /* process chunks as they're encountered */\r
133 \r
134    while ( 1 ) {\r
135       cksize += cksize & 1;\r
136 \r
137       switch ( id )\r
138       {\r
139          case ID_LAYR:\r
140             if ( object->nlayers > 0 ) {\r
141                layer = _pico_calloc( 1, sizeof( lwLayer ));\r
142                if ( !layer ) goto Fail;\r
143                lwListAdd( &object->layer, layer );\r
144             }\r
145             object->nlayers++;\r
146 \r
147             set_flen( 0 );\r
148             layer->index = getU2( fp );\r
149             layer->flags = getU2( fp );\r
150             layer->pivot[ 0 ] = getF4( fp );\r
151             layer->pivot[ 1 ] = getF4( fp );\r
152             layer->pivot[ 2 ] = getF4( fp );\r
153             layer->name = getS0( fp );\r
154 \r
155             rlen = get_flen();\r
156             if ( rlen < 0 || rlen > cksize ) goto Fail;\r
157             if ( rlen <= cksize - 2 )\r
158                layer->parent = getU2( fp );\r
159             rlen = get_flen();\r
160             if ( rlen < cksize )\r
161                _pico_memstream_seek( fp, cksize - rlen, PICO_SEEK_CUR );\r
162             break;\r
163 \r
164          case ID_PNTS:\r
165             if ( !lwGetPoints( fp, cksize, &layer->point ))\r
166                goto Fail;\r
167             break;\r
168 \r
169          case ID_POLS:\r
170             if ( !lwGetPolygons( fp, cksize, &layer->polygon,\r
171                layer->point.offset ))\r
172                goto Fail;\r
173             break;\r
174 \r
175          case ID_VMAP:\r
176          case ID_VMAD:\r
177             node = ( lwNode * ) lwGetVMap( fp, cksize, layer->point.offset,\r
178                layer->polygon.offset, id == ID_VMAD );\r
179             if ( !node ) goto Fail;\r
180             lwListAdd( &layer->vmap, node );\r
181             layer->nvmaps++;\r
182             break;\r
183 \r
184          case ID_PTAG:\r
185             if ( !lwGetPolygonTags( fp, cksize, &object->taglist,\r
186                &layer->polygon ))\r
187                goto Fail;\r
188             break;\r
189 \r
190          case ID_BBOX:\r
191             set_flen( 0 );\r
192             for ( i = 0; i < 6; i++ )\r
193                layer->bbox[ i ] = getF4( fp );\r
194             rlen = get_flen();\r
195             if ( rlen < 0 || rlen > cksize ) goto Fail;\r
196             if ( rlen < cksize )\r
197                _pico_memstream_seek( fp, cksize - rlen, PICO_SEEK_CUR );\r
198             break;\r
199 \r
200          case ID_TAGS:\r
201             if ( !lwGetTags( fp, cksize, &object->taglist ))\r
202                goto Fail;\r
203             break;\r
204 \r
205          case ID_ENVL:\r
206             node = ( lwNode * ) lwGetEnvelope( fp, cksize );\r
207             if ( !node ) goto Fail;\r
208             lwListAdd( &object->env, node );\r
209             object->nenvs++;\r
210             break;\r
211 \r
212          case ID_CLIP:\r
213             node = ( lwNode * ) lwGetClip( fp, cksize );\r
214             if ( !node ) goto Fail;\r
215             lwListAdd( &object->clip, node );\r
216             object->nclips++;\r
217             break;\r
218 \r
219          case ID_SURF:\r
220             node = ( lwNode * ) lwGetSurface( fp, cksize );\r
221             if ( !node ) goto Fail;\r
222             lwListAdd( &object->surf, node );\r
223             object->nsurfs++;\r
224             break;\r
225 \r
226          case ID_DESC:\r
227          case ID_TEXT:\r
228          case ID_ICON:\r
229          default:\r
230             _pico_memstream_seek( fp, cksize, PICO_SEEK_CUR );\r
231             break;\r
232       }\r
233 \r
234       /* end of the file? */\r
235 \r
236       if ( formsize <= _pico_memstream_tell( fp ) - 8 ) break;\r
237 \r
238       /* get the next chunk header */\r
239 \r
240       set_flen( 0 );\r
241       id = getU4( fp );\r
242       cksize = getU4( fp );\r
243       if ( 8 != get_flen() ) goto Fail;\r
244    }\r
245 \r
246    if ( object->nlayers == 0 )\r
247       object->nlayers = 1;\r
248 \r
249    layer = object->layer;\r
250    while ( layer ) {\r
251       lwGetBoundingBox( &layer->point, layer->bbox );\r
252       lwGetPolyNormals( &layer->point, &layer->polygon );\r
253       if ( !lwGetPointPolygons( &layer->point, &layer->polygon )) goto Fail;\r
254       if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist,\r
255          &object->surf, &object->nsurfs )) goto Fail;\r
256       lwGetVertNormals( &layer->point, &layer->polygon );\r
257       if ( !lwGetPointVMaps( &layer->point, layer->vmap )) goto Fail;\r
258       if ( !lwGetPolyVMaps( &layer->polygon, layer->vmap )) goto Fail;\r
259       layer = layer->next;\r
260    }\r
261 \r
262    return object;\r
263 \r
264 Fail:\r
265   if ( failID ) *failID = id;\r
266    if ( fp ) {\r
267       if ( failpos ) *failpos = _pico_memstream_tell( fp );\r
268    }\r
269    lwFreeObject( object );\r
270    return NULL;\r
271 }\r
272 \r
273 int lwValidateObject( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos )\r
274 {\r
275    unsigned int id, formsize, type;\r
276 \r
277    /* open the file */\r
278 \r
279    if ( !fp ) return PICO_PMV_ERROR_MEMORY;\r
280 \r
281    /* read the first 12 bytes */\r
282 \r
283    set_flen( 0 );\r
284    id       = getU4( fp );\r
285    formsize = getU4( fp );\r
286    type     = getU4( fp );\r
287    if ( 12 != get_flen() ) {\r
288       return PICO_PMV_ERROR_SIZE;\r
289    }\r
290 \r
291    /* is this a LW object? */\r
292 \r
293    if ( id != ID_FORM ) {\r
294       if ( failpos ) *failpos = 12;\r
295       return PICO_PMV_ERROR_SIZE;\r
296    }\r
297 \r
298    if ( type != ID_LWO2 ) {\r
299       if ( type == ID_LWOB )\r
300          return lwValidateObject5( filename, fp, failID, failpos );\r
301       else {\r
302          if ( failpos ) *failpos = 12;\r
303          return PICO_PMV_ERROR_IDENT;\r
304       }\r
305    }\r
306 \r
307    return PICO_PMV_OK;\r
308 }\r