]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/lwo/surface.c
* added MeshTex plugin src to project (can't compile)
[xonotic/netradiant.git] / libs / picomodel / lwo / surface.c
1 /*
2    ======================================================================
3    surface.c
4
5    Surface functions for an LWO2 reader.
6
7    Ernie Wright  17 Sep 00
8    ====================================================================== */
9
10 #include "../picointernal.h"
11 #include "lwo2.h"
12
13
14 /*
15    ======================================================================
16    lwFreePlugin()
17
18    Free the memory used by an lwPlugin.
19    ====================================================================== */
20
21 void lwFreePlugin( lwPlugin *p ){
22         if ( p ) {
23                 if ( p->ord ) {
24                         _pico_free( p->ord );
25                 }
26                 if ( p->name ) {
27                         _pico_free( p->name );
28                 }
29                 if ( p->data ) {
30                         _pico_free( p->data );
31                 }
32                 _pico_free( p );
33         }
34 }
35
36
37 /*
38    ======================================================================
39    lwFreeTexture()
40
41    Free the memory used by an lwTexture.
42    ====================================================================== */
43
44 void lwFreeTexture( lwTexture *t ){
45         if ( t ) {
46                 if ( t->ord ) {
47                         _pico_free( t->ord );
48                 }
49                 switch ( t->type ) {
50                 case ID_IMAP:
51                         if ( t->param.imap.vmap_name ) {
52                                 _pico_free( t->param.imap.vmap_name );
53                         }
54                         if ( t->tmap.ref_object ) {
55                                 _pico_free( t->tmap.ref_object );
56                         }
57                         break;
58                 case ID_PROC:
59                         if ( t->param.proc.name ) {
60                                 _pico_free( t->param.proc.name );
61                         }
62                         if ( t->param.proc.data ) {
63                                 _pico_free( t->param.proc.data );
64                         }
65                         break;
66                 case ID_GRAD:
67                         if ( t->param.grad.key ) {
68                                 _pico_free( t->param.grad.key );
69                         }
70                         if ( t->param.grad.ikey ) {
71                                 _pico_free( t->param.grad.ikey );
72                         }
73                         break;
74                 }
75                 _pico_free( t );
76         }
77 }
78
79
80 /*
81    ======================================================================
82    lwFreeSurface()
83
84    Free the memory used by an lwSurface.
85    ====================================================================== */
86
87 void lwFreeSurface( lwSurface *surf ){
88         if ( surf ) {
89                 if ( surf->name ) {
90                         _pico_free( surf->name );
91                 }
92                 if ( surf->srcname ) {
93                         _pico_free( surf->srcname );
94                 }
95
96                 lwListFree( surf->shader, (void *) lwFreePlugin );
97
98                 lwListFree( surf->color.tex, (void *) lwFreeTexture );
99                 lwListFree( surf->luminosity.tex, (void *) lwFreeTexture );
100                 lwListFree( surf->diffuse.tex, (void *) lwFreeTexture );
101                 lwListFree( surf->specularity.tex, (void *) lwFreeTexture );
102                 lwListFree( surf->glossiness.tex, (void *) lwFreeTexture );
103                 lwListFree( surf->reflection.val.tex, (void *) lwFreeTexture );
104                 lwListFree( surf->transparency.val.tex, (void *) lwFreeTexture );
105                 lwListFree( surf->eta.tex, (void *) lwFreeTexture );
106                 lwListFree( surf->translucency.tex, (void *) lwFreeTexture );
107                 lwListFree( surf->bump.tex, (void *) lwFreeTexture );
108
109                 _pico_free( surf );
110         }
111 }
112
113
114 /*
115    ======================================================================
116    lwGetTHeader()
117
118    Read a texture map header from a SURF.BLOK in an LWO2 file.  This is
119    the first subchunk in a BLOK, and its contents are common to all three
120    texture types.
121    ====================================================================== */
122
123 int lwGetTHeader( picoMemStream_t *fp, int hsz, lwTexture *tex ){
124         unsigned int id;
125         unsigned short sz;
126         int pos, rlen;
127
128
129         /* remember where we started */
130
131         set_flen( 0 );
132         pos = _pico_memstream_tell( fp );
133
134         /* ordinal string */
135
136         tex->ord = getS0( fp );
137
138         /* first subchunk header */
139
140         id = getU4( fp );
141         sz = getU2( fp );
142         if ( 0 > get_flen() ) {
143                 return 0;
144         }
145
146         /* process subchunks as they're encountered */
147
148         while ( 1 ) {
149                 sz += sz & 1;
150                 set_flen( 0 );
151
152                 switch ( id ) {
153                 case ID_CHAN:
154                         tex->chan = getU4( fp );
155                         break;
156
157                 case ID_OPAC:
158                         tex->opac_type = getU2( fp );
159                         tex->opacity.val = getF4( fp );
160                         tex->opacity.eindex = getVX( fp );
161                         break;
162
163                 case ID_ENAB:
164                         tex->enabled = getU2( fp );
165                         break;
166
167                 case ID_NEGA:
168                         tex->negative = getU2( fp );
169                         break;
170
171                 case ID_AXIS:
172                         tex->axis = getU2( fp );
173                         break;
174
175                 default:
176                         break;
177                 }
178
179                 /* error while reading current subchunk? */
180
181                 rlen = get_flen();
182                 if ( rlen < 0 || rlen > sz ) {
183                         return 0;
184                 }
185
186                 /* skip unread parts of the current subchunk */
187
188                 if ( rlen < sz ) {
189                         _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
190                 }
191
192                 /* end of the texture header subchunk? */
193
194                 if ( hsz <= _pico_memstream_tell( fp ) - pos ) {
195                         break;
196                 }
197
198                 /* get the next subchunk header */
199
200                 set_flen( 0 );
201                 id = getU4( fp );
202                 sz = getU2( fp );
203                 if ( 6 != get_flen() ) {
204                         return 0;
205                 }
206         }
207
208         set_flen( _pico_memstream_tell( fp ) - pos );
209         return 1;
210 }
211
212
213 /*
214    ======================================================================
215    lwGetTMap()
216
217    Read a texture map from a SURF.BLOK in an LWO2 file.  The TMAP
218    defines the mapping from texture to world or object coordinates.
219    ====================================================================== */
220
221 int lwGetTMap( picoMemStream_t *fp, int tmapsz, lwTMap *tmap ){
222         unsigned int id;
223         unsigned short sz;
224         int rlen, pos, i;
225
226         pos = _pico_memstream_tell( fp );
227         id = getU4( fp );
228         sz = getU2( fp );
229         if ( 0 > get_flen() ) {
230                 return 0;
231         }
232
233         while ( 1 ) {
234                 sz += sz & 1;
235                 set_flen( 0 );
236
237                 switch ( id ) {
238                 case ID_SIZE:
239                         for ( i = 0; i < 3; i++ )
240                                 tmap->size.val[ i ] = getF4( fp );
241                         tmap->size.eindex = getVX( fp );
242                         break;
243
244                 case ID_CNTR:
245                         for ( i = 0; i < 3; i++ )
246                                 tmap->center.val[ i ] = getF4( fp );
247                         tmap->center.eindex = getVX( fp );
248                         break;
249
250                 case ID_ROTA:
251                         for ( i = 0; i < 3; i++ )
252                                 tmap->rotate.val[ i ] = getF4( fp );
253                         tmap->rotate.eindex = getVX( fp );
254                         break;
255
256                 case ID_FALL:
257                         tmap->fall_type = getU2( fp );
258                         for ( i = 0; i < 3; i++ )
259                                 tmap->falloff.val[ i ] = getF4( fp );
260                         tmap->falloff.eindex = getVX( fp );
261                         break;
262
263                 case ID_OREF:
264                         tmap->ref_object = getS0( fp );
265                         break;
266
267                 case ID_CSYS:
268                         tmap->coord_sys = getU2( fp );
269                         break;
270
271                 default:
272                         break;
273                 }
274
275                 /* error while reading the current subchunk? */
276
277                 rlen = get_flen();
278                 if ( rlen < 0 || rlen > sz ) {
279                         return 0;
280                 }
281
282                 /* skip unread parts of the current subchunk */
283
284                 if ( rlen < sz ) {
285                         _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
286                 }
287
288                 /* end of the TMAP subchunk? */
289
290                 if ( tmapsz <= _pico_memstream_tell( fp ) - pos ) {
291                         break;
292                 }
293
294                 /* get the next subchunk header */
295
296                 set_flen( 0 );
297                 id = getU4( fp );
298                 sz = getU2( fp );
299                 if ( 6 != get_flen() ) {
300                         return 0;
301                 }
302         }
303
304         set_flen( _pico_memstream_tell( fp ) - pos );
305         return 1;
306 }
307
308
309 /*
310    ======================================================================
311    lwGetImageMap()
312
313    Read an lwImageMap from a SURF.BLOK in an LWO2 file.
314    ====================================================================== */
315
316 int lwGetImageMap( picoMemStream_t *fp, int rsz, lwTexture *tex ){
317         unsigned int id;
318         unsigned short sz;
319         int rlen, pos;
320
321         pos = _pico_memstream_tell( fp );
322         id = getU4( fp );
323         sz = getU2( fp );
324         if ( 0 > get_flen() ) {
325                 return 0;
326         }
327
328         while ( 1 ) {
329                 sz += sz & 1;
330                 set_flen( 0 );
331
332                 switch ( id ) {
333                 case ID_TMAP:
334                         if ( !lwGetTMap( fp, sz, &tex->tmap ) ) {
335                                 return 0;
336                         }
337                         break;
338
339                 case ID_PROJ:
340                         tex->param.imap.projection = getU2( fp );
341                         break;
342
343                 case ID_VMAP:
344                         tex->param.imap.vmap_name = getS0( fp );
345                         break;
346
347                 case ID_AXIS:
348                         tex->param.imap.axis = getU2( fp );
349                         break;
350
351                 case ID_IMAG:
352                         tex->param.imap.cindex = getVX( fp );
353                         break;
354
355                 case ID_WRAP:
356                         tex->param.imap.wrapw_type = getU2( fp );
357                         tex->param.imap.wraph_type = getU2( fp );
358                         break;
359
360                 case ID_WRPW:
361                         tex->param.imap.wrapw.val = getF4( fp );
362                         tex->param.imap.wrapw.eindex = getVX( fp );
363                         break;
364
365                 case ID_WRPH:
366                         tex->param.imap.wraph.val = getF4( fp );
367                         tex->param.imap.wraph.eindex = getVX( fp );
368                         break;
369
370                 case ID_AAST:
371                         tex->param.imap.aas_flags = getU2( fp );
372                         tex->param.imap.aa_strength = getF4( fp );
373                         break;
374
375                 case ID_PIXB:
376                         tex->param.imap.pblend = getU2( fp );
377                         break;
378
379                 case ID_STCK:
380                         tex->param.imap.stck.val = getF4( fp );
381                         tex->param.imap.stck.eindex = getVX( fp );
382                         break;
383
384                 case ID_TAMP:
385                         tex->param.imap.amplitude.val = getF4( fp );
386                         tex->param.imap.amplitude.eindex = getVX( fp );
387                         break;
388
389                 default:
390                         break;
391                 }
392
393                 /* error while reading the current subchunk? */
394
395                 rlen = get_flen();
396                 if ( rlen < 0 || rlen > sz ) {
397                         return 0;
398                 }
399
400                 /* skip unread parts of the current subchunk */
401
402                 if ( rlen < sz ) {
403                         _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
404                 }
405
406                 /* end of the image map? */
407
408                 if ( rsz <= _pico_memstream_tell( fp ) - pos ) {
409                         break;
410                 }
411
412                 /* get the next subchunk header */
413
414                 set_flen( 0 );
415                 id = getU4( fp );
416                 sz = getU2( fp );
417                 if ( 6 != get_flen() ) {
418                         return 0;
419                 }
420         }
421
422         set_flen( _pico_memstream_tell( fp ) - pos );
423         return 1;
424 }
425
426
427 /*
428    ======================================================================
429    lwGetProcedural()
430
431    Read an lwProcedural from a SURF.BLOK in an LWO2 file.
432    ====================================================================== */
433
434 int lwGetProcedural( picoMemStream_t *fp, int rsz, lwTexture *tex ){
435         unsigned int id;
436         unsigned short sz;
437         int rlen, pos;
438
439         pos = _pico_memstream_tell( fp );
440         id = getU4( fp );
441         sz = getU2( fp );
442         if ( 0 > get_flen() ) {
443                 return 0;
444         }
445
446         while ( 1 ) {
447                 sz += sz & 1;
448                 set_flen( 0 );
449
450                 switch ( id ) {
451                 case ID_TMAP:
452                         if ( !lwGetTMap( fp, sz, &tex->tmap ) ) {
453                                 return 0;
454                         }
455                         break;
456
457                 case ID_AXIS:
458                         tex->param.proc.axis = getU2( fp );
459                         break;
460
461                 case ID_VALU:
462                         tex->param.proc.value[ 0 ] = getF4( fp );
463                         if ( sz >= 8 ) {
464                                 tex->param.proc.value[ 1 ] = getF4( fp );
465                         }
466                         if ( sz >= 12 ) {
467                                 tex->param.proc.value[ 2 ] = getF4( fp );
468                         }
469                         break;
470
471                 case ID_FUNC:
472                         tex->param.proc.name = getS0( fp );
473                         rlen = get_flen();
474                         tex->param.proc.data = getbytes( fp, sz - rlen );
475                         break;
476
477                 default:
478                         break;
479                 }
480
481                 /* error while reading the current subchunk? */
482
483                 rlen = get_flen();
484                 if ( rlen < 0 || rlen > sz ) {
485                         return 0;
486                 }
487
488                 /* skip unread parts of the current subchunk */
489
490                 if ( rlen < sz ) {
491                         _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
492                 }
493
494                 /* end of the procedural block? */
495
496                 if ( rsz <= _pico_memstream_tell( fp ) - pos ) {
497                         break;
498                 }
499
500                 /* get the next subchunk header */
501
502                 set_flen( 0 );
503                 id = getU4( fp );
504                 sz = getU2( fp );
505                 if ( 6 != get_flen() ) {
506                         return 0;
507                 }
508         }
509
510         set_flen( _pico_memstream_tell( fp ) - pos );
511         return 1;
512 }
513
514
515 /*
516    ======================================================================
517    lwGetGradient()
518
519    Read an lwGradient from a SURF.BLOK in an LWO2 file.
520    ====================================================================== */
521
522 int lwGetGradient( picoMemStream_t *fp, int rsz, lwTexture *tex ){
523         unsigned int id;
524         unsigned short sz;
525         int rlen, pos, i, j, nkeys;
526
527         pos = _pico_memstream_tell( fp );
528         id = getU4( fp );
529         sz = getU2( fp );
530         if ( 0 > get_flen() ) {
531                 return 0;
532         }
533
534         while ( 1 ) {
535                 sz += sz & 1;
536                 set_flen( 0 );
537
538                 switch ( id ) {
539                 case ID_TMAP:
540                         if ( !lwGetTMap( fp, sz, &tex->tmap ) ) {
541                                 return 0;
542                         }
543                         break;
544
545                 case ID_PNAM:
546                         tex->param.grad.paramname = getS0( fp );
547                         break;
548
549                 case ID_INAM:
550                         tex->param.grad.itemname = getS0( fp );
551                         break;
552
553                 case ID_GRST:
554                         tex->param.grad.start = getF4( fp );
555                         break;
556
557                 case ID_GREN:
558                         tex->param.grad.end = getF4( fp );
559                         break;
560
561                 case ID_GRPT:
562                         tex->param.grad.repeat = getU2( fp );
563                         break;
564
565                 case ID_FKEY:
566                         nkeys = sz / sizeof( lwGradKey );
567                         tex->param.grad.key = _pico_calloc( nkeys, sizeof( lwGradKey ) );
568                         if ( !tex->param.grad.key ) {
569                                 return 0;
570                         }
571                         for ( i = 0; i < nkeys; i++ ) {
572                                 tex->param.grad.key[ i ].value = getF4( fp );
573                                 for ( j = 0; j < 4; j++ )
574                                         tex->param.grad.key[ i ].rgba[ j ] = getF4( fp );
575                         }
576                         break;
577
578                 case ID_IKEY:
579                         nkeys = sz / 2;
580                         tex->param.grad.ikey = _pico_calloc( nkeys, sizeof( short ) );
581                         if ( !tex->param.grad.ikey ) {
582                                 return 0;
583                         }
584                         for ( i = 0; i < nkeys; i++ )
585                                 tex->param.grad.ikey[ i ] = getU2( fp );
586                         break;
587
588                 default:
589                         break;
590                 }
591
592                 /* error while reading the current subchunk? */
593
594                 rlen = get_flen();
595                 if ( rlen < 0 || rlen > sz ) {
596                         return 0;
597                 }
598
599                 /* skip unread parts of the current subchunk */
600
601                 if ( rlen < sz ) {
602                         _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
603                 }
604
605                 /* end of the gradient? */
606
607                 if ( rsz <= _pico_memstream_tell( fp ) - pos ) {
608                         break;
609                 }
610
611                 /* get the next subchunk header */
612
613                 set_flen( 0 );
614                 id = getU4( fp );
615                 sz = getU2( fp );
616                 if ( 6 != get_flen() ) {
617                         return 0;
618                 }
619         }
620
621         set_flen( _pico_memstream_tell( fp ) - pos );
622         return 1;
623 }
624
625
626 /*
627    ======================================================================
628    lwGetTexture()
629
630    Read an lwTexture from a SURF.BLOK in an LWO2 file.
631    ====================================================================== */
632
633 lwTexture *lwGetTexture( picoMemStream_t *fp, int bloksz, unsigned int type ){
634         lwTexture *tex;
635         unsigned short sz;
636         int ok;
637
638         tex = _pico_calloc( 1, sizeof( lwTexture ) );
639         if ( !tex ) {
640                 return NULL;
641         }
642
643         tex->type = type;
644         tex->tmap.size.val[ 0 ] =
645                 tex->tmap.size.val[ 1 ] =
646                         tex->tmap.size.val[ 2 ] = 1.0f;
647         tex->opacity.val = 1.0f;
648         tex->enabled = 1;
649
650         sz = getU2( fp );
651         if ( !lwGetTHeader( fp, sz, tex ) ) {
652                 _pico_free( tex );
653                 return NULL;
654         }
655
656         sz = bloksz - sz - 6;
657         switch ( type ) {
658         case ID_IMAP:  ok = lwGetImageMap( fp, sz, tex );  break;
659         case ID_PROC:  ok = lwGetProcedural( fp, sz, tex );  break;
660         case ID_GRAD:  ok = lwGetGradient( fp, sz, tex );  break;
661         default:
662                 ok = !_pico_memstream_seek( fp, sz, PICO_SEEK_CUR );
663         }
664
665         if ( !ok ) {
666                 lwFreeTexture( tex );
667                 return NULL;
668         }
669
670         set_flen( bloksz );
671         return tex;
672 }
673
674
675 /*
676    ======================================================================
677    lwGetShader()
678
679    Read a shader record from a SURF.BLOK in an LWO2 file.
680    ====================================================================== */
681
682 lwPlugin *lwGetShader( picoMemStream_t *fp, int bloksz ){
683         lwPlugin *shdr;
684         unsigned int id;
685         unsigned short sz;
686         int hsz, rlen, pos;
687
688         shdr = _pico_calloc( 1, sizeof( lwPlugin ) );
689         if ( !shdr ) {
690                 return NULL;
691         }
692
693         pos = _pico_memstream_tell( fp );
694         set_flen( 0 );
695         hsz = getU2( fp );
696         shdr->ord = getS0( fp );
697         id = getU4( fp );
698         sz = getU2( fp );
699         if ( 0 > get_flen() ) {
700                 goto Fail;
701         }
702
703         while ( hsz > 0 ) {
704                 sz += sz & 1;
705                 hsz -= sz;
706                 if ( id == ID_ENAB ) {
707                         shdr->flags = getU2( fp );
708                         break;
709                 }
710                 else {
711                         _pico_memstream_seek( fp, sz, PICO_SEEK_CUR );
712                         id = getU4( fp );
713                         sz = getU2( fp );
714                 }
715         }
716
717         id = getU4( fp );
718         sz = getU2( fp );
719         if ( 0 > get_flen() ) {
720                 goto Fail;
721         }
722
723         while ( 1 ) {
724                 sz += sz & 1;
725                 set_flen( 0 );
726
727                 switch ( id ) {
728                 case ID_FUNC:
729                         shdr->name = getS0( fp );
730                         rlen = get_flen();
731                         shdr->data = getbytes( fp, sz - rlen );
732                         break;
733
734                 default:
735                         break;
736                 }
737
738                 /* error while reading the current subchunk? */
739
740                 rlen = get_flen();
741                 if ( rlen < 0 || rlen > sz ) {
742                         goto Fail;
743                 }
744
745                 /* skip unread parts of the current subchunk */
746
747                 if ( rlen < sz ) {
748                         _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
749                 }
750
751                 /* end of the shader block? */
752
753                 if ( bloksz <= _pico_memstream_tell( fp ) - pos ) {
754                         break;
755                 }
756
757                 /* get the next subchunk header */
758
759                 set_flen( 0 );
760                 id = getU4( fp );
761                 sz = getU2( fp );
762                 if ( 6 != get_flen() ) {
763                         goto Fail;
764                 }
765         }
766
767         set_flen( _pico_memstream_tell( fp ) - pos );
768         return shdr;
769
770 Fail:
771         lwFreePlugin( shdr );
772         return NULL;
773 }
774
775
776 /*
777    ======================================================================
778    compare_textures()
779    compare_shaders()
780
781    Callbacks for the lwListInsert() function, which is called to add
782    textures to surface channels and shaders to surfaces.
783    ====================================================================== */
784
785 static int compare_textures( lwTexture *a, lwTexture *b ){
786         return strcmp( a->ord, b->ord );
787 }
788
789
790 static int compare_shaders( lwPlugin *a, lwPlugin *b ){
791         return strcmp( a->ord, b->ord );
792 }
793
794
795 /*
796    ======================================================================
797    add_texture()
798
799    Finds the surface channel (lwTParam or lwCParam) to which a texture is
800    applied, then calls lwListInsert().
801    ====================================================================== */
802
803 static int add_texture( lwSurface *surf, lwTexture *tex ){
804         lwTexture **list;
805
806         switch ( tex->chan ) {
807         case ID_COLR:  list = &surf->color.tex;             break;
808         case ID_LUMI:  list = &surf->luminosity.tex;        break;
809         case ID_DIFF:  list = &surf->diffuse.tex;           break;
810         case ID_SPEC:  list = &surf->specularity.tex;       break;
811         case ID_GLOS:  list = &surf->glossiness.tex;        break;
812         case ID_REFL:  list = &surf->reflection.val.tex;    break;
813         case ID_TRAN:  list = &surf->transparency.val.tex;  break;
814         case ID_RIND:  list = &surf->eta.tex;               break;
815         case ID_TRNL:  list = &surf->translucency.tex;      break;
816         case ID_BUMP:  list = &surf->bump.tex;              break;
817         default:  return 0;
818         }
819
820         lwListInsert( (void **) list, tex, ( void *) compare_textures );
821         return 1;
822 }
823
824
825 /*
826    ======================================================================
827    lwDefaultSurface()
828
829    Allocate and initialize a surface.
830    ====================================================================== */
831
832 lwSurface *lwDefaultSurface( void ){
833         lwSurface *surf;
834
835         surf = _pico_calloc( 1, sizeof( lwSurface ) );
836         if ( !surf ) {
837                 return NULL;
838         }
839
840         surf->color.rgb[ 0 ] = 0.78431f;
841         surf->color.rgb[ 1 ] = 0.78431f;
842         surf->color.rgb[ 2 ] = 0.78431f;
843         surf->diffuse.val    = 1.0f;
844         surf->glossiness.val = 0.4f;
845         surf->bump.val       = 1.0f;
846         surf->eta.val        = 1.0f;
847         surf->sideflags      = 1;
848
849         return surf;
850 }
851
852
853 /*
854    ======================================================================
855    lwGetSurface()
856
857    Read an lwSurface from an LWO2 file.
858    ====================================================================== */
859
860 lwSurface *lwGetSurface( picoMemStream_t *fp, int cksize ){
861         lwSurface *surf;
862         lwTexture *tex;
863         lwPlugin *shdr;
864         unsigned int id, type;
865         unsigned short sz;
866         int pos, rlen;
867
868
869         /* allocate the Surface structure */
870
871         surf = _pico_calloc( 1, sizeof( lwSurface ) );
872         if ( !surf ) {
873                 goto Fail;
874         }
875
876         /* non-zero defaults */
877
878         surf->color.rgb[ 0 ] = 0.78431f;
879         surf->color.rgb[ 1 ] = 0.78431f;
880         surf->color.rgb[ 2 ] = 0.78431f;
881         surf->diffuse.val    = 1.0f;
882         surf->glossiness.val = 0.4f;
883         surf->bump.val       = 1.0f;
884         surf->eta.val        = 1.0f;
885         surf->sideflags      = 1;
886
887         /* remember where we started */
888
889         set_flen( 0 );
890         pos = _pico_memstream_tell( fp );
891
892         /* names */
893
894         surf->name = getS0( fp );
895         surf->srcname = getS0( fp );
896
897         /* first subchunk header */
898
899         id = getU4( fp );
900         sz = getU2( fp );
901         if ( 0 > get_flen() ) {
902                 goto Fail;
903         }
904
905         /* process subchunks as they're encountered */
906
907         while ( 1 ) {
908                 sz += sz & 1;
909                 set_flen( 0 );
910
911                 switch ( id ) {
912                 case ID_COLR:
913                         surf->color.rgb[ 0 ] = getF4( fp );
914                         surf->color.rgb[ 1 ] = getF4( fp );
915                         surf->color.rgb[ 2 ] = getF4( fp );
916                         surf->color.eindex = getVX( fp );
917                         break;
918
919                 case ID_LUMI:
920                         surf->luminosity.val = getF4( fp );
921                         surf->luminosity.eindex = getVX( fp );
922                         break;
923
924                 case ID_DIFF:
925                         surf->diffuse.val = getF4( fp );
926                         surf->diffuse.eindex = getVX( fp );
927                         break;
928
929                 case ID_SPEC:
930                         surf->specularity.val = getF4( fp );
931                         surf->specularity.eindex = getVX( fp );
932                         break;
933
934                 case ID_GLOS:
935                         surf->glossiness.val = getF4( fp );
936                         surf->glossiness.eindex = getVX( fp );
937                         break;
938
939                 case ID_REFL:
940                         surf->reflection.val.val = getF4( fp );
941                         surf->reflection.val.eindex = getVX( fp );
942                         break;
943
944                 case ID_RFOP:
945                         surf->reflection.options = getU2( fp );
946                         break;
947
948                 case ID_RIMG:
949                         surf->reflection.cindex = getVX( fp );
950                         break;
951
952                 case ID_RSAN:
953                         surf->reflection.seam_angle = getF4( fp );
954                         break;
955
956                 case ID_TRAN:
957                         surf->transparency.val.val = getF4( fp );
958                         surf->transparency.val.eindex = getVX( fp );
959                         break;
960
961                 case ID_TROP:
962                         surf->transparency.options = getU2( fp );
963                         break;
964
965                 case ID_TIMG:
966                         surf->transparency.cindex = getVX( fp );
967                         break;
968
969                 case ID_RIND:
970                         surf->eta.val = getF4( fp );
971                         surf->eta.eindex = getVX( fp );
972                         break;
973
974                 case ID_TRNL:
975                         surf->translucency.val = getF4( fp );
976                         surf->translucency.eindex = getVX( fp );
977                         break;
978
979                 case ID_BUMP:
980                         surf->bump.val = getF4( fp );
981                         surf->bump.eindex = getVX( fp );
982                         break;
983
984                 case ID_SMAN:
985                         surf->smooth = getF4( fp );
986                         break;
987
988                 case ID_SIDE:
989                         surf->sideflags = getU2( fp );
990                         break;
991
992                 case ID_CLRH:
993                         surf->color_hilite.val = getF4( fp );
994                         surf->color_hilite.eindex = getVX( fp );
995                         break;
996
997                 case ID_CLRF:
998                         surf->color_filter.val = getF4( fp );
999                         surf->color_filter.eindex = getVX( fp );
1000                         break;
1001
1002                 case ID_ADTR:
1003                         surf->add_trans.val = getF4( fp );
1004                         surf->add_trans.eindex = getVX( fp );
1005                         break;
1006
1007                 case ID_SHRP:
1008                         surf->dif_sharp.val = getF4( fp );
1009                         surf->dif_sharp.eindex = getVX( fp );
1010                         break;
1011
1012                 case ID_GVAL:
1013                         surf->glow.val = getF4( fp );
1014                         surf->glow.eindex = getVX( fp );
1015                         break;
1016
1017                 case ID_LINE:
1018                         surf->line.enabled = 1;
1019                         if ( sz >= 2 ) {
1020                                 surf->line.flags = getU2( fp );
1021                         }
1022                         if ( sz >= 6 ) {
1023                                 surf->line.size.val = getF4( fp );
1024                         }
1025                         if ( sz >= 8 ) {
1026                                 surf->line.size.eindex = getVX( fp );
1027                         }
1028                         break;
1029
1030                 case ID_ALPH:
1031                         surf->alpha_mode = getU2( fp );
1032                         surf->alpha = getF4( fp );
1033                         break;
1034
1035                 case ID_AVAL:
1036                         surf->alpha = getF4( fp );
1037                         break;
1038
1039                 case ID_BLOK:
1040                         type = getU4( fp );
1041
1042                         switch ( type ) {
1043                         case ID_IMAP:
1044                         case ID_PROC:
1045                         case ID_GRAD:
1046                                 tex = lwGetTexture( fp, sz - 4, type );
1047                                 if ( !tex ) {
1048                                         goto Fail;
1049                                 }
1050                                 if ( !add_texture( surf, tex ) ) {
1051                                         lwFreeTexture( tex );
1052                                 }
1053                                 set_flen( 4 + get_flen() );
1054                                 break;
1055                         case ID_SHDR:
1056                                 shdr = lwGetShader( fp, sz - 4 );
1057                                 if ( !shdr ) {
1058                                         goto Fail;
1059                                 }
1060                                 lwListInsert( (void **) &surf->shader, shdr, (void *) compare_shaders );
1061                                 ++surf->nshaders;
1062                                 set_flen( 4 + get_flen() );
1063                                 break;
1064                         }
1065                         break;
1066
1067                 default:
1068                         break;
1069                 }
1070
1071                 /* error while reading current subchunk? */
1072
1073                 rlen = get_flen();
1074                 if ( rlen < 0 || rlen > sz ) {
1075                         goto Fail;
1076                 }
1077
1078                 /* skip unread parts of the current subchunk */
1079
1080                 if ( rlen < sz ) {
1081                         _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
1082                 }
1083
1084                 /* end of the SURF chunk? */
1085
1086                 if ( cksize <= _pico_memstream_tell( fp ) - pos ) {
1087                         break;
1088                 }
1089
1090                 /* get the next subchunk header */
1091
1092                 set_flen( 0 );
1093                 id = getU4( fp );
1094                 sz = getU2( fp );
1095                 if ( 6 != get_flen() ) {
1096                         goto Fail;
1097                 }
1098         }
1099
1100         return surf;
1101
1102 Fail:
1103         if ( surf ) {
1104                 lwFreeSurface( surf );
1105         }
1106         return NULL;
1107 }