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