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