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