]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/picointernal.c
Merge branch 'NateEag-master-patch-12920' into 'master'
[xonotic/netradiant.git] / libs / picomodel / picointernal.c
1 /* -----------------------------------------------------------------------------
2
3    PicoModel Library
4
5    Copyright (c) 2002, Randy Reddig & seaw0lf
6    All rights reserved.
7
8    Redistribution and use in source and binary forms, with or without modification,
9    are permitted provided that the following conditions are met:
10
11    Redistributions of source code must retain the above copyright notice, this list
12    of conditions and the following disclaimer.
13
14    Redistributions in binary form must reproduce the above copyright notice, this
15    list of conditions and the following disclaimer in the documentation and/or
16    other materials provided with the distribution.
17
18    Neither the names of the copyright holders nor the names of its contributors may
19    be used to endorse or promote products derived from this software without
20    specific prior written permission.
21
22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26    ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33    ----------------------------------------------------------------------------- */
34
35 /* todo:
36  * - fix p->curLine for parser routines. increased twice
37  */
38
39 /* dependencies */
40 #include <string.h>
41 #include "picointernal.h"
42 #include "globaldefs.h"
43
44
45
46 /* function pointers */
47 void *( *_pico_ptr_malloc    )( size_t ) = malloc;
48 void ( *_pico_ptr_free      )( void* ) = free;
49 void ( *_pico_ptr_load_file )( const char*, unsigned char**, int* ) = NULL;
50 void ( *_pico_ptr_free_file )( void* ) = NULL;
51 void ( *_pico_ptr_print     )( int, const char* ) = NULL;
52
53 typedef union
54 {
55         float f;
56         char c[4];
57 }
58 floatSwapUnion;
59
60 /* _pico_alloc:
61  *  kludged memory allocation wrapper
62  */
63 void *_pico_alloc( size_t size ){
64         void *ptr;
65
66         /* some sanity checks */
67         if ( size == 0 ) {
68                 return NULL;
69         }
70         if ( _pico_ptr_malloc == NULL ) {
71                 return NULL;
72         }
73
74         /* allocate memory */
75         ptr = _pico_ptr_malloc( size );
76         if ( ptr == NULL ) {
77                 return NULL;
78         }
79
80         /* zero out allocated memory */
81         memset( ptr,0,size );
82
83         /* return pointer to allocated memory */
84         return ptr;
85 }
86
87 /* _pico_calloc:
88  *  _pico_calloc wrapper
89  */
90 void *_pico_calloc( size_t num, size_t size ){
91         void *ptr;
92
93         /* some sanity checks */
94         if ( num == 0 || size == 0 ) {
95                 return NULL;
96         }
97         if ( _pico_ptr_malloc == NULL ) {
98                 return NULL;
99         }
100
101         /* allocate memory */
102         ptr = _pico_ptr_malloc( num * size );
103         if ( ptr == NULL ) {
104                 return NULL;
105         }
106
107         /* zero out allocated memory */
108         memset( ptr,0,num * size );
109
110         /* return pointer to allocated memory */
111         return ptr;
112 }
113
114 /* _pico_realloc:
115  *  memory reallocation wrapper (note: only grows,
116  *  but never shrinks or frees)
117  */
118 void *_pico_realloc( void **ptr, size_t oldSize, size_t newSize ){
119         void *ptr2;
120
121         /* sanity checks */
122         if ( ptr == NULL ) {
123                 return NULL;
124         }
125         if ( newSize < oldSize ) {
126                 return *ptr;
127         }
128         if ( _pico_ptr_malloc == NULL ) {
129                 return NULL;
130         }
131
132         /* allocate new pointer */
133         ptr2 = _pico_alloc( newSize );
134         if ( ptr2 == NULL ) {
135                 return NULL;
136         }
137
138         /* copy */
139         if ( *ptr != NULL ) {
140                 memcpy( ptr2, *ptr, oldSize );
141                 _pico_free( *ptr );
142         }
143
144         /* fix up and return */
145         *ptr = ptr2;
146         return *ptr;
147 }
148
149 /* _pico_clone_alloc:
150  *  handy function for quick string allocation/copy. it clones
151  *  the given string and returns a pointer to the new allocated
152  *  clone (which must be freed by caller of course) or returns
153  *  NULL on memory alloc or param errors. if 'size' is -1 the
154  *  length of the input string is used, otherwise 'size' is used
155  *  as custom clone size (the string is cropped to fit into mem
156  *  if needed). -sea
157  */
158 char *_pico_clone_alloc( const char *str ){
159         char* cloned;
160
161         /* sanity check */
162         if ( str == NULL ) {
163                 return NULL;
164         }
165
166         /* allocate memory */
167         cloned = _pico_alloc( strlen( str ) + 1 );
168         if ( cloned == NULL ) {
169                 return NULL;
170         }
171
172         /* copy input string to cloned string */
173         strcpy( cloned, str );
174
175         /* return ptr to cloned string */
176         return cloned;
177 }
178
179 /* _pico_free:
180  * wrapper around the free function pointer
181  */
182 void _pico_free( void *ptr ){
183         /* sanity checks */
184         if ( ptr == NULL ) {
185                 return;
186         }
187         if ( _pico_ptr_free == NULL ) {
188                 return;
189         }
190
191         /* free the allocated memory */
192         _pico_ptr_free( ptr );
193 }
194
195 /* _pico_load_file:
196  * wrapper around the loadfile function pointer
197  */
198 void _pico_load_file( const char *name, unsigned char **buffer, int *bufSize ){
199         /* sanity checks */
200         if ( name == NULL ) {
201                 *bufSize = -1;
202                 return;
203         }
204         if ( _pico_ptr_load_file == NULL ) {
205                 *bufSize = -1;
206                 return;
207         }
208         /* do the actual call to read in the file; */
209         /* BUFFER IS ALLOCATED BY THE EXTERNAL LOADFILE FUNC */
210         _pico_ptr_load_file( name,buffer,bufSize );
211 }
212
213 /* _pico_free_file:
214  * wrapper around the file free function pointer
215  */
216 void _pico_free_file( void *buffer ){
217         /* sanity checks */
218         if ( buffer == NULL ) {
219                 return;
220         }
221
222         /* use default free */
223         if ( _pico_ptr_free_file == NULL ) {
224                 free( buffer );
225                 return;
226         }
227         /* free the allocated file */
228         _pico_ptr_free_file( buffer );
229 }
230
231 /* _pico_printf:
232  * wrapper around the print function pointer -sea
233  */
234 void _pico_printf( int level, const char *format, ... ){
235         char str[4096];
236         va_list argptr;
237
238         /* sanity checks */
239         if ( format == NULL ) {
240                 return;
241         }
242         if ( _pico_ptr_print == NULL ) {
243                 return;
244         }
245
246         /* format string */
247         va_start( argptr,format );
248         vsprintf( str,format,argptr );
249         va_end( argptr );
250
251         /* remove linefeeds */
252         if ( str[ strlen( str ) - 1 ] == '\n' ) {
253                 str[ strlen( str ) - 1 ] = '\0';
254         }
255
256         /* do the actual call */
257         _pico_ptr_print( level,str );
258 }
259
260 /* _pico_first_token:
261  * trims everything after the first whitespace-delimited token
262  */
263
264 void _pico_first_token( char *str ){
265         if ( !str || !*str ) {
266                 return;
267         }
268         while ( *str && !isspace( *str ) )
269                 str++;
270         *str = '\0';
271 }
272
273 /* _pico_strltrim:
274  * left trims the given string -sea
275  */
276 char *_pico_strltrim( char *str ){
277         char *str1 = str, *str2 = str;
278
279         while ( isspace( *str2 ) ) str2++;
280         if ( str2 != str ) {
281                 while ( *str2 != '\0' ) /* fix: ydnar */
282                         *str1++ = *str2++;
283         }
284         return str;
285 }
286
287 /* _pico_strrtrim:
288  * right trims the given string -sea
289  */
290 char *_pico_strrtrim( char *str ){
291         if ( str && *str ) {
292                 char *str1 = str;
293                 int allspace = 1;
294
295                 while ( *str1 )
296                 {
297                         if ( allspace && !isspace( *str1 ) ) {
298                                 allspace = 0;
299                         }
300                         str1++;
301                 }
302                 if ( allspace ) {
303                         *str = '\0';
304                 }
305                 else {
306                         str1--;
307                         while ( ( isspace( *str1 ) ) && ( str1 >= str ) )
308                                 *str1-- = '\0';
309                 }
310         }
311         return str;
312 }
313
314 /* _pico_strlwr:
315  *  pico internal string-to-lower routine.
316  */
317 char *_pico_strlwr( char *str ){
318         char *cp;
319         for ( cp = str; *cp; ++cp )
320         {
321                 if ( 'A' <= *cp && *cp <= 'Z' ) {
322                         *cp += ( 'a' - 'A' );
323                 }
324         }
325         return str;
326 }
327
328 /* _pico_strchcount:
329  *  counts how often the given char appears in str. -sea
330  */
331 int _pico_strchcount( char *str, int ch ){
332         int count = 0;
333         while ( *str++ ) if ( *str == ch ) {
334                         count++;
335                 }
336         return count;
337 }
338
339 void _pico_zero_bounds( picoVec3_t mins, picoVec3_t maxs ){
340         int i;
341         for ( i = 0; i < 3; i++ )
342         {
343                 mins[i] = +999999;
344                 maxs[i] = -999999;
345         }
346 }
347
348 void _pico_expand_bounds( picoVec3_t p, picoVec3_t mins, picoVec3_t maxs ){
349         int i;
350         for ( i = 0; i < 3; i++ )
351         {
352                 float value = p[i];
353                 if ( value < mins[i] ) {
354                         mins[i] = value;
355                 }
356                 if ( value > maxs[i] ) {
357                         maxs[i] = value;
358                 }
359         }
360 }
361
362 void _pico_zero_vec( picoVec3_t vec ){
363         vec[ 0 ] = vec[ 1 ] = vec[ 2 ] = 0;
364 }
365
366 void _pico_zero_vec2( picoVec2_t vec ){
367         vec[ 0 ] = vec[ 1 ] = 0;
368 }
369
370 void _pico_zero_vec4( picoVec4_t vec ){
371         vec[ 0 ] = vec[ 1 ] = vec[ 2 ] = vec[ 3 ] = 0;
372 }
373
374 void _pico_set_vec( picoVec3_t v, float a, float b, float c ){
375         v[ 0 ] = a;
376         v[ 1 ] = b;
377         v[ 2 ] = c;
378 }
379
380 void _pico_set_vec4( picoVec4_t v, float a, float b, float c, float d ){
381         v[ 0 ] = a;
382         v[ 1 ] = b;
383         v[ 2 ] = c;
384         v[ 3 ] = d;
385 }
386
387 void _pico_copy_vec( picoVec3_t src, picoVec3_t dest ){
388         dest[ 0 ] = src[ 0 ];
389         dest[ 1 ] = src[ 1 ];
390         dest[ 2 ] = src[ 2 ];
391 }
392
393 void _pico_copy_vec2( picoVec2_t src, picoVec2_t dest ){
394         dest[ 0 ] = src[ 0 ];
395         dest[ 1 ] = src[ 1 ];
396 }
397
398 void _pico_copy_vec4( picoVec4_t src, picoVec4_t dest ){
399         dest[ 0 ] = src[ 0 ];
400         dest[ 1 ] = src[ 1 ];
401         dest[ 2 ] = src[ 2 ];
402         dest[ 3 ] = src[ 3 ];
403 }
404
405 /* ydnar */
406 picoVec_t _pico_normalize_vec( picoVec3_t vec ){
407         double len, ilen;
408
409         len = sqrt( vec[ 0 ] * vec[ 0 ] + vec[ 1 ] * vec[ 1 ] + vec[ 2 ] * vec[ 2 ] );
410         if ( len == 0.0 ) {
411                 return 0.0;
412         }
413         ilen = 1.0 / len;
414         vec[ 0 ] *= (picoVec_t) ilen;
415         vec[ 1 ] *= (picoVec_t) ilen;
416         vec[ 2 ] *= (picoVec_t) ilen;
417         return (picoVec_t) len;
418 }
419
420 void _pico_add_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ){
421         dest[ 0 ] = a[ 0 ] + b[ 0 ];
422         dest[ 1 ] = a[ 1 ] + b[ 1 ];
423         dest[ 2 ] = a[ 2 ] + b[ 2 ];
424 }
425
426 void _pico_subtract_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ){
427         dest[ 0 ] = a[ 0 ] - b[ 0 ];
428         dest[ 1 ] = a[ 1 ] - b[ 1 ];
429         dest[ 2 ] = a[ 2 ] - b[ 2 ];
430 }
431
432 void _pico_scale_vec( picoVec3_t v, float scale, picoVec3_t dest ){
433         dest[ 0 ] = v[ 0 ] * scale;
434         dest[ 1 ] = v[ 1 ] * scale;
435         dest[ 2 ] = v[ 2 ] * scale;
436 }
437
438 void _pico_scale_vec4( picoVec4_t v, float scale, picoVec4_t dest ){
439         dest[ 0 ] = v[ 0 ] * scale;
440         dest[ 1 ] = v[ 1 ] * scale;
441         dest[ 2 ] = v[ 2 ] * scale;
442         dest[ 3 ] = v[ 3 ] * scale;
443 }
444
445 picoVec_t _pico_dot_vec( picoVec3_t a, picoVec3_t b ){
446         return a[ 0 ] * b[ 0 ] + a[ 1 ] * b[ 1 ] + a[ 2 ] * b[ 2 ];
447 }
448
449 void _pico_cross_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ){
450         dest[ 0 ] = a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ];
451         dest[ 1 ] = a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ];
452         dest[ 2 ] = a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ];
453 }
454
455 picoVec_t _pico_calc_plane( picoVec4_t plane, picoVec3_t a, picoVec3_t b, picoVec3_t c ){
456         picoVec3_t ba, ca;
457
458         _pico_subtract_vec( b, a, ba );
459         _pico_subtract_vec( c, a, ca );
460         _pico_cross_vec( ca, ba, plane );
461         plane[ 3 ] = _pico_dot_vec( a, plane );
462         return _pico_normalize_vec( plane );
463 }
464
465 const picoColor_t picoColor_white = { 255, 255, 255, 255 };
466
467 /* separate from _pico_set_vec4 */
468 void _pico_set_color( picoColor_t c, int r, int g, int b, int a ){
469         c[ 0 ] = r;
470         c[ 1 ] = g;
471         c[ 2 ] = b;
472         c[ 3 ] = a;
473 }
474
475 void _pico_copy_color( const picoColor_t src, picoColor_t dest ){
476         dest[ 0 ] = src[ 0 ];
477         dest[ 1 ] = src[ 1 ];
478         dest[ 2 ] = src[ 2 ];
479         dest[ 3 ] = src[ 3 ];
480 }
481
482 #if GDEF_ARCH_ENDIAN_BIG
483
484 int   _pico_big_long( int src ) { return src; }
485 short _pico_big_short( short src ) { return src; }
486 float _pico_big_float( float src ) { return src; }
487
488 int _pico_little_long( int src ){
489         return ( ( src & 0xFF000000 ) >> 24 ) |
490                    ( ( src & 0x00FF0000 ) >> 8 ) |
491                    ( ( src & 0x0000FF00 ) << 8 ) |
492                    ( ( src & 0x000000FF ) << 24 );
493 }
494
495 short _pico_little_short( short src ){
496         return ( ( src & 0xFF00 ) >> 8 ) |
497                    ( ( src & 0x00FF ) << 8 );
498 }
499
500 float _pico_little_float( float src ){
501         floatSwapUnion in,out;
502         in.f = src;
503         out.c[ 0 ] = in.c[ 3 ];
504         out.c[ 1 ] = in.c[ 2 ];
505         out.c[ 2 ] = in.c[ 1 ];
506         out.c[ 3 ] = in.c[ 0 ];
507         return out.f;
508 }
509 #else /*__BIG_ENDIAN__*/
510
511 int   _pico_little_long( int src ) { return src; }
512 short _pico_little_short( short src ) { return src; }
513 float _pico_little_float( float src ) { return src; }
514
515 int _pico_big_long( int src ){
516         return ( ( src & 0xFF000000 ) >> 24 ) |
517                    ( ( src & 0x00FF0000 ) >> 8 ) |
518                    ( ( src & 0x0000FF00 ) << 8 ) |
519                    ( ( src & 0x000000FF ) << 24 );
520 }
521
522 short _pico_big_short( short src ){
523         return ( ( src & 0xFF00 ) >> 8 ) |
524                    ( ( src & 0x00FF ) << 8 );
525 }
526
527 float _pico_big_float( float src ){
528         floatSwapUnion in,out;
529         in.f = src;
530         out.c[ 0 ] = in.c[ 3 ];
531         out.c[ 1 ] = in.c[ 2 ];
532         out.c[ 2 ] = in.c[ 1 ];
533         out.c[ 3 ] = in.c[ 0 ];
534         return out.f;
535 }
536 #endif /*__BIG_ENDIAN__*/
537
538 /* _pico_stristr:
539  *  case-insensitive strstr. -sea
540  */
541 const char *_pico_stristr( const char *str, const char *substr ){
542         const size_t sublen = strlen( substr );
543         while ( *str )
544         {
545                 if ( !_pico_strnicmp( str,substr,sublen ) ) {
546                         break;
547                 }
548                 str++;
549         }
550         if ( !( *str ) ) {
551                 str = NULL;
552         }
553         return str;
554 }
555
556 /*
557    _pico_unixify()
558    changes dos \ style path separators to /
559  */
560
561 void _pico_unixify( char *path ){
562         if ( path == NULL ) {
563                 return;
564         }
565         while ( *path )
566         {
567                 if ( *path == '\\' ) {
568                         *path = '/';
569                 }
570                 path++;
571         }
572 }
573
574 /* _pico_nofname:
575  *  removes file name portion from given file path and converts
576  *  the directory separators to un*x style. returns 1 on success
577  *  or 0 when 'destSize' was exceeded. -sea
578  */
579 int _pico_nofname( const char *path, char *dest, int destSize ){
580         int left  = destSize;
581         char *temp  = dest;
582
583         while ( ( *dest = *path ) != '\0' )
584         {
585                 if ( *dest == '/' || *dest == '\\' ) {
586                         temp = ( dest + 1 );
587                         *dest = '/';
588                 }
589                 dest++; path++;
590
591                 if ( --left < 1 ) {
592                         *temp = '\0';
593                         return 0;
594                 }
595         }
596         *temp = '\0';
597         return 1;
598 }
599
600 /* _pico_nopath:
601  *  returns ptr to filename portion in given path or an empty
602  *  string otherwise. given 'path' is not altered. -sea
603  */
604 const char *_pico_nopath( const char *path ){
605         if ( path == NULL ) {
606                 return "";
607         }
608         const char *src;
609         src = path + ( strlen( path ) - 1 );
610
611         if ( !strchr( path,'/' ) && !strchr( path,'\\' ) ) {
612                 return ( path );
613         }
614
615         while ( ( src-- ) != path )
616         {
617                 if ( *src == '/' || *src == '\\' ) {
618                         return ( ++src );
619                 }
620         }
621         return "";
622 }
623
624 /* _pico_setfext:
625  *  sets/changes the file extension for the given filename
626  *  or filepath's filename portion. the given 'path' *is*
627  *  altered. leave 'ext' empty to remove extension. -sea
628  */
629 char *_pico_setfext( char *path, const char *ext ){
630         char *src;
631         int remfext = 0;
632
633         src = path + ( strlen( path ) - 1 );
634
635         if ( ext == NULL ) {
636                 ext = "";
637         }
638         if ( strlen( ext ) < 1 ) {
639                 remfext = 1;
640         }
641         if ( strlen( path ) < 1 ) {
642                 return path;
643         }
644
645         while ( ( src-- ) != path )
646         {
647                 if ( *src == '/' || *src == '\\' ) {
648                         return path;
649                 }
650
651                 if ( *src == '.' ) {
652                         if ( remfext ) {
653                                 *src = '\0';
654                                 return path;
655                         }
656                         *( ++src ) = '\0';
657                         break;
658                 }
659         }
660         strcat( path,ext );
661         return path;
662 }
663
664 /* _pico_getline:
665  *  extracts one line from the given buffer and stores it in dest.
666  *  returns -1 on error or the length of the line on success. i've
667  *  removed string trimming here. this can be done manually by the
668  *  calling func.
669  */
670 int _pico_getline( char *buf, int bufsize, char *dest, int destsize ){
671         int pos;
672
673         /* check output */
674         if ( dest == NULL || destsize < 1 ) {
675                 return -1;
676         }
677         memset( dest,0,destsize );
678
679         /* check input */
680         if ( buf == NULL || bufsize < 1 ) {
681                 return -1;
682         }
683
684         /* get next line */
685         for ( pos = 0; pos < bufsize && pos < destsize; pos++ )
686         {
687                 if ( buf[pos] == '\n' ) {
688                         pos++; break;
689                 }
690                 dest[pos] = buf[pos];
691         }
692         /* terminate dest and return */
693         dest[pos] = '\0';
694         return pos;
695 }
696
697 /* expecting fileName to be relative vfs model path */
698 void _pico_deduce_shadername( const char* fileName, const char* srcName, picoShader_t* shader ){
699         if( srcName == NULL || fileName == NULL )
700                 return;
701         char name[strlen( srcName ) + 1];
702         strcpy( name, srcName );
703         _pico_unixify( name );
704         _pico_setfext( name, NULL );
705
706         char path[strlen( fileName ) + strlen( name ) + 1];
707         _pico_nofname( fileName, path, strlen( fileName ) + strlen( name ) + 1 );
708         _pico_unixify( path );
709
710         if( !strchr( name , '/' ) ){ /* texture is likely in the folder, where model is */
711                 strcat( path, name );
712         }
713         else if( name[0] == '/' || ( name[0] != '\0' && name[1] == ':' ) || strstr( name, ".." ) ){ /* absolute path or with .. */
714                 const char* p = name;
715                 for (; *p != '\0'; ++p )
716                         if ( _pico_strnicmp( p, "/models/", 8 ) == 0 || _pico_strnicmp( p, "/textures/", 10 ) == 0 )
717                                 break;
718                 if( *p != '\0' ){
719                         strcpy( path, p + 1 );
720                 }
721                 else{
722                         p = _pico_nopath( name );
723                         strcat( path, p );
724                 }
725         }
726         else{
727                 PicoSetShaderName( shader, name );
728                 return;
729         }
730
731         _pico_printf( PICO_NORMAL, "PICO: substituting shader name: %s -> %s", srcName, path );
732         PicoSetShaderName( shader, path );
733 }
734
735 /* deduce shadernames from bitmap or shadername paths */
736 void _pico_deduce_shadernames( picoModel_t *model ){
737         for ( int i = 0; i < model->numShaders; ++i ){
738                 /* skip null shaders */
739                 if ( model->shader[i] == NULL )
740                         continue;
741
742                 const char* mapname = model->shader[i]->mapName;
743                 const char* shadername = model->shader[i]->name;
744
745                 /* Detect intentional material name to not replace it with texture name.
746
747                 Reimplement commits by Garux:
748                 https://github.com/Garux/netradiant-custom/commit/1bd3e7ae186b55fb61e3738d2493432c0b1f5a7b
749                 https://github.com/Garux/netradiant-custom/commit/ea21eee2254fb2e667732d8f1b0f83c439a89bfa
750
751                 This attempts to restore proper material behaviour when the mapper knows what he is doing,
752                 also called Julius' case or correct case because Julius is always correctâ„¢
753                 while keeping the fallback for other situations, also called newbie's case
754                 which may be compatible with third-party tools not following Quake 3 conventions.
755
756                 See: https://gitlab.com/xonotic/netradiant/-/merge_requests/179#note_777324051 */
757                 if ( shadername && *shadername &&
758                         ( _pico_strnicmp( shadername, "models/", 7 ) == 0
759                         || _pico_strnicmp( shadername, "models\\", 7 ) == 0
760                         || _pico_strnicmp( shadername, "textures/", 9 ) == 0
761                         || _pico_strnicmp( shadername, "textures\\", 9 ) == 0 ) )
762                 {
763                         _pico_deduce_shadername( model->fileName, shadername, model->shader[i] );
764                 }
765                 else if( mapname && *mapname )
766                         _pico_deduce_shadername( model->fileName, mapname, model->shader[i] );
767                 else if( shadername && *shadername )
768                         _pico_deduce_shadername( model->fileName, shadername, model->shader[i] );
769         }
770 }
771
772 /* _pico_parse_skip_white:
773  *  skips white spaces in current pico parser, sets *hasLFs
774  *  to 1 if linefeeds were skipped, and either returns the
775  *  parser's cursor pointer or NULL on error. -sea
776  */
777 void _pico_parse_skip_white( picoParser_t *p, int *hasLFs ){
778         /* sanity checks */
779         if ( p == NULL || p->cursor == NULL ) {
780                 return;
781         }
782
783         /* skin white spaces */
784         while ( 1 )
785         {
786                 /* sanity checks */
787                 if ( p->cursor <  p->buffer ||
788                          p->cursor >= p->max ) {
789                         return;
790                 }
791                 /* break for chars other than white spaces */
792                 if ( *p->cursor >  0x20 ) {
793                         break;
794                 }
795                 if ( *p->cursor == 0x00 ) {
796                         return;
797                 }
798
799                 /* a bit of linefeed handling */
800                 if ( *p->cursor == '\n' ) {
801                         *hasLFs = 1;
802                         p->curLine++;
803                 }
804                 /* go to next character */
805                 p->cursor++;
806         }
807 }
808
809 /* _pico_new_parser:
810  *  allocates a new ascii parser object.
811  */
812 picoParser_t *_pico_new_parser( const picoByte_t *buffer, int bufSize ){
813         picoParser_t *p;
814
815         /* sanity check */
816         if ( buffer == NULL || bufSize <= 0 ) {
817                 return NULL;
818         }
819
820         /* allocate reader */
821         p = _pico_alloc( sizeof( picoParser_t ) );
822         if ( p == NULL ) {
823                 return NULL;
824         }
825         memset( p,0,sizeof( picoParser_t ) );
826
827         /* allocate token space */
828         p->tokenSize = 0;
829         p->tokenMax = 1024;
830         p->token = _pico_alloc( p->tokenMax );
831         if ( p->token == NULL ) {
832                 _pico_free( p );
833                 return NULL;
834         }
835         /* setup */
836         p->buffer   = (const char *) buffer;
837         p->cursor   = p->buffer;
838         p->bufSize  = bufSize;
839         p->max      = p->buffer + bufSize;
840         p->curLine = 1; /* sea: new */
841
842         /* return ptr to parser */
843         return p;
844 }
845
846 /* _pico_free_parser:
847  *  frees an existing pico parser object.
848  */
849 void _pico_free_parser( picoParser_t *p ){
850         /* sanity check */
851         if ( p == NULL ) {
852                 return;
853         }
854
855         /* free the parser */
856         if ( p->token != NULL ) {
857                 _pico_free( p->token );
858         }
859         _pico_free( p );
860 }
861
862 /* _pico_parse_ex:
863  *  reads the next token from given pico parser object. if param
864  * 'allowLFs' is 1 it will read beyond linefeeds and return 0 when
865  *  the EOF is reached. if 'allowLFs' is 0 it will return 0 when
866  *  the EOL is reached. if 'handleQuoted' is 1 the parser function
867  *  will handle "quoted" strings and return the data between the
868  *  quotes as token. returns 0 on end/error or 1 on success. -sea
869  */
870 int _pico_parse_ex( picoParser_t *p, int allowLFs, int handleQuoted ){
871         int hasLFs = 0;
872         const char *old;
873
874         /* sanity checks */
875         if ( p == NULL || p->buffer == NULL ||
876                  p->cursor <  p->buffer ||
877                  p->cursor >= p->max ) {
878                 return 0;
879         }
880         /* clear parser token */
881         p->tokenSize = 0;
882         p->token[ 0 ] = '\0';
883         old = p->cursor;
884
885         /* skip whitespaces */
886         while ( p->cursor < p->max && *p->cursor <= 32 )
887         {
888                 if ( *p->cursor == '\n' ) {
889                         p->curLine++;
890                         hasLFs++;
891                 }
892                 p->cursor++;
893         }
894         /* return if we're not allowed to go beyond lfs */
895         if ( ( hasLFs > 0 ) && !allowLFs ) {
896                 p->cursor = old;
897                 return 0;
898         }
899         /* get next quoted string */
900         if ( *p->cursor == '\"' && handleQuoted ) {
901                 p->cursor++;
902                 while ( p->cursor < p->max && *p->cursor )
903                 {
904                         if ( *p->cursor == '\\' ) {
905                                 if ( *( p->cursor + 1 ) == '"' ) {
906                                         p->cursor++;
907                                 }
908                                 p->token[ p->tokenSize++ ] = *p->cursor++;
909                                 continue;
910                         }
911                         else if ( *p->cursor == '\"' ) {
912                                 p->cursor++;
913                                 break;
914                         }
915                         else if ( *p->cursor == '\n' ) {
916                                 p->curLine++;
917                         }
918                         p->token[ p->tokenSize++ ] = *p->cursor++;
919                 }
920                 /* terminate token */
921                 p->token[ p->tokenSize ] = '\0';
922                 return 1;
923         }
924         /* otherwise get next word */
925         while ( p->cursor < p->max && *p->cursor > 32 )
926         {
927                 if ( *p->cursor == '\n' ) {
928                         p->curLine++;
929                 }
930                 p->token[ p->tokenSize++ ] = *p->cursor++;
931         }
932         /* terminate token */
933         p->token[ p->tokenSize ] = '\0';
934         return 1;
935 }
936
937 /* _pico_parse_first:
938  *  reads the first token from the next line and returns
939  *  a pointer to it. returns NULL on EOL or EOF. -sea
940  */
941 char *_pico_parse_first( picoParser_t *p ){
942         /* sanity check */
943         if ( p == NULL ) {
944                 return NULL;
945         }
946
947         /* try to read next token (with lfs & quots) */
948         if ( !_pico_parse_ex( p,1,1 ) ) {
949                 return NULL;
950         }
951
952         /* return ptr to the token string */
953         return p->token;
954 }
955
956 /* _pico_parse:
957  *  reads the next token from the parser and returns a pointer
958  *  to it. quoted strings are handled as usual. returns NULL
959  *  on EOL or EOF. -sea
960  */
961 char *_pico_parse( picoParser_t *p, int allowLFs ){
962         /* sanity check */
963         if ( p == NULL ) {
964                 return NULL;
965         }
966
967         /* try to read next token (with quots) */
968         if ( !_pico_parse_ex( p,allowLFs,1 ) ) {
969                 return NULL;
970         }
971
972         /* return ptr to the token string */
973         return p->token;
974 }
975
976 /* _pico_parse_skip_rest:
977  *  skips the rest of the current line in parser.
978  */
979 void _pico_parse_skip_rest( picoParser_t *p ){
980         while ( _pico_parse_ex( p,0,0 ) ) ;
981 }
982
983 /* _pico_parse_skip_braced:
984  *  parses/skips over a braced section. returns 1 on success
985  *  or 0 on error (when there was no closing bracket and the
986  *  end of buffer was reached or when the opening bracket was
987  *  missing).
988  */
989 int _pico_parse_skip_braced( picoParser_t *p ){
990         int firstToken = 1;
991         int level;
992
993         /* sanity check */
994         if ( p == NULL ) {
995                 return 0;
996         }
997
998         /* set the initial level for parsing */
999         level = 0;
1000
1001         /* skip braced section */
1002         while ( 1 )
1003         {
1004                 /* read next token (lfs allowed) */
1005                 if ( !_pico_parse_ex( p,1,1 ) ) {
1006                         /* end of parser buffer reached */
1007                         return 0;
1008                 }
1009                 /* first token must be an opening bracket */
1010                 if ( firstToken && p->token[0] != '{' ) {
1011                         /* opening bracket missing */
1012                         return 0;
1013                 }
1014                 /* we only check this once */
1015                 firstToken = 0;
1016
1017                 /* update level */
1018                 if ( p->token[1] == '\0' ) {
1019                         if ( p->token[0] == '{' ) {
1020                                 level++;
1021                         }
1022                         if ( p->token[0] == '}' ) {
1023                                 level--;
1024                         }
1025                 }
1026                 /* break if we're back at our starting level */
1027                 if ( level == 0 ) {
1028                         break;
1029                 }
1030         }
1031         /* successfully skipped braced section */
1032         return 1;
1033 }
1034
1035 int _pico_parse_check( picoParser_t *p, int allowLFs, char *str ){
1036         if ( !_pico_parse_ex( p,allowLFs,1 ) ) {
1037                 return 0;
1038         }
1039         if ( !strcmp( p->token,str ) ) {
1040                 return 1;
1041         }
1042         return 0;
1043 }
1044
1045 int _pico_parse_checki( picoParser_t *p, int allowLFs, char *str ){
1046         if ( !_pico_parse_ex( p,allowLFs,1 ) ) {
1047                 return 0;
1048         }
1049         if ( !_pico_stricmp( p->token,str ) ) {
1050                 return 1;
1051         }
1052         return 0;
1053 }
1054
1055 int _pico_parse_int( picoParser_t *p, int *out ){
1056         char *token;
1057
1058         /* sanity checks */
1059         if ( p == NULL || out == NULL ) {
1060                 return 0;
1061         }
1062
1063         /* get token and turn it into an integer */
1064         *out = 0;
1065         token = _pico_parse( p,0 );
1066         if ( token == NULL ) {
1067                 return 0;
1068         }
1069         *out = atoi( token );
1070
1071         /* success */
1072         return 1;
1073 }
1074
1075 int _pico_parse_int_def( picoParser_t *p, int *out, int def ){
1076         char *token;
1077
1078         /* sanity checks */
1079         if ( p == NULL || out == NULL ) {
1080                 return 0;
1081         }
1082
1083         /* get token and turn it into an integer */
1084         *out = def;
1085         token = _pico_parse( p,0 );
1086         if ( token == NULL ) {
1087                 return 0;
1088         }
1089         *out = atoi( token );
1090
1091         /* success */
1092         return 1;
1093 }
1094
1095 int _pico_parse_float( picoParser_t *p, float *out ){
1096         char *token;
1097
1098         /* sanity checks */
1099         if ( p == NULL || out == NULL ) {
1100                 return 0;
1101         }
1102
1103         /* get token and turn it into a float */
1104         *out = 0.0f;
1105         token = _pico_parse( p,0 );
1106         if ( token == NULL ) {
1107                 return 0;
1108         }
1109         *out = (float) atof( token );
1110
1111         /* success */
1112         return 1;
1113 }
1114
1115 int _pico_parse_float_def( picoParser_t *p, float *out, float def ){
1116         char *token;
1117
1118         /* sanity checks */
1119         if ( p == NULL || out == NULL ) {
1120                 return 0;
1121         }
1122
1123         /* get token and turn it into a float */
1124         *out = def;
1125         token = _pico_parse( p,0 );
1126         if ( token == NULL ) {
1127                 return 0;
1128         }
1129         *out = (float) atof( token );
1130
1131         /* success */
1132         return 1;
1133 }
1134
1135 int _pico_parse_vec( picoParser_t *p, picoVec3_t out ){
1136         char *token;
1137         int i;
1138
1139         /* sanity checks */
1140         if ( p == NULL || out == NULL ) {
1141                 return 0;
1142         }
1143
1144         /* zero out outination vector */
1145         _pico_zero_vec( out );
1146
1147         /* parse three vector components */
1148         for ( i = 0; i < 3; i++ )
1149         {
1150                 token = _pico_parse( p,0 );
1151                 if ( token == NULL ) {
1152                         _pico_zero_vec( out );
1153                         return 0;
1154                 }
1155                 out[ i ] = (float) atof( token );
1156         }
1157         /* success */
1158         return 1;
1159 }
1160
1161 int _pico_parse_vec_def( picoParser_t *p, picoVec3_t out, picoVec3_t def ){
1162         char *token;
1163         int i;
1164
1165         /* sanity checks */
1166         if ( p == NULL || out == NULL ) {
1167                 return 0;
1168         }
1169
1170         /* assign default vector value */
1171         _pico_copy_vec( def,out );
1172
1173         /* parse three vector components */
1174         for ( i = 0; i < 3; i++ )
1175         {
1176                 token = _pico_parse( p,0 );
1177                 if ( token == NULL ) {
1178                         _pico_copy_vec( def,out );
1179                         return 0;
1180                 }
1181                 out[ i ] = (float) atof( token );
1182         }
1183         /* success */
1184         return 1;
1185 }
1186
1187 int _pico_parse_vec2( picoParser_t *p, picoVec2_t out ){
1188         char *token;
1189         int i;
1190
1191         /* sanity checks */
1192         if ( p == NULL || out == NULL ) {
1193                 return 0;
1194         }
1195
1196         /* zero out outination vector */
1197         _pico_zero_vec2( out );
1198
1199         /* parse two vector components */
1200         for ( i = 0; i < 2; i++ )
1201         {
1202                 token = _pico_parse( p,0 );
1203                 if ( token == NULL ) {
1204                         _pico_zero_vec2( out );
1205                         return 0;
1206                 }
1207                 out[ i ] = (float) atof( token );
1208         }
1209         /* success */
1210         return 1;
1211 }
1212
1213 int _pico_parse_vec2_def( picoParser_t *p, picoVec2_t out, picoVec2_t def ){
1214         char *token;
1215         int i;
1216
1217         /* sanity checks */
1218         if ( p == NULL || out == NULL ) {
1219                 return 0;
1220         }
1221
1222         /* assign default vector value */
1223         _pico_copy_vec2( def,out );
1224
1225         /* parse two vector components */
1226         for ( i = 0; i < 2; i++ )
1227         {
1228                 token = _pico_parse( p,0 );
1229                 if ( token == NULL ) {
1230                         _pico_copy_vec2( def,out );
1231                         return 0;
1232                 }
1233                 out[ i ] = (float) atof( token );
1234         }
1235         /* success */
1236         return 1;
1237 }
1238
1239 int _pico_parse_vec4( picoParser_t *p, picoVec4_t out ){
1240         char *token;
1241         int i;
1242
1243         /* sanity checks */
1244         if ( p == NULL || out == NULL ) {
1245                 return 0;
1246         }
1247
1248         /* zero out outination vector */
1249         _pico_zero_vec4( out );
1250
1251         /* parse four vector components */
1252         for ( i = 0; i < 4; i++ )
1253         {
1254                 token = _pico_parse( p,0 );
1255                 if ( token == NULL ) {
1256                         _pico_zero_vec4( out );
1257                         return 0;
1258                 }
1259                 out[ i ] = (float) atof( token );
1260         }
1261         /* success */
1262         return 1;
1263 }
1264
1265 int _pico_parse_vec4_def( picoParser_t *p, picoVec4_t out, picoVec4_t def ){
1266         char *token;
1267         int i;
1268
1269         /* sanity checks */
1270         if ( p == NULL || out == NULL ) {
1271                 return 0;
1272         }
1273
1274         /* assign default vector value */
1275         _pico_copy_vec4( def,out );
1276
1277         /* parse four vector components */
1278         for ( i = 0; i < 4; i++ )
1279         {
1280                 token = _pico_parse( p,0 );
1281                 if ( token == NULL ) {
1282                         _pico_copy_vec4( def,out );
1283                         return 0;
1284                 }
1285                 out[ i ] = (float) atof( token );
1286         }
1287         /* success */
1288         return 1;
1289 }
1290
1291 /* _pico_new_memstream:
1292  *  allocates a new memorystream object.
1293  */
1294 picoMemStream_t *_pico_new_memstream( const picoByte_t *buffer, int bufSize ){
1295         picoMemStream_t *s;
1296
1297         /* sanity check */
1298         if ( buffer == NULL || bufSize <= 0 ) {
1299                 return NULL;
1300         }
1301
1302         /* allocate stream */
1303         s = _pico_alloc( sizeof( picoMemStream_t ) );
1304         if ( s == NULL ) {
1305                 return NULL;
1306         }
1307         memset( s,0,sizeof( picoMemStream_t ) );
1308
1309         /* setup */
1310         s->buffer   = buffer;
1311         s->curPos   = buffer;
1312         s->bufSize  = bufSize;
1313         s->flag     = 0;
1314
1315         /* return ptr to stream */
1316         return s;
1317 }
1318
1319 /* _pico_free_memstream:
1320  *  frees an existing pico memorystream object.
1321  */
1322 void _pico_free_memstream( picoMemStream_t *s ){
1323         /* sanity check */
1324         if ( s == NULL ) {
1325                 return;
1326         }
1327
1328         /* free the stream */
1329         _pico_free( s );
1330 }
1331
1332 /* _pico_memstream_read:
1333  *  reads data from a pico memorystream into a buffer.
1334  */
1335 int _pico_memstream_read( picoMemStream_t *s, void *buffer, int len ){
1336         int ret = 1;
1337
1338         /* sanity checks */
1339         if ( s == NULL || buffer == NULL ) {
1340                 return 0;
1341         }
1342
1343         if ( s->curPos + len > s->buffer + s->bufSize ) {
1344                 s->flag |= PICO_IOEOF;
1345                 len = s->buffer + s->bufSize - s->curPos;
1346                 ret = 0;
1347         }
1348
1349         /* read the data */
1350         memcpy( buffer, s->curPos, len );
1351         s->curPos += len;
1352         return ret;
1353 }
1354
1355 /* _pico_memstream_read:
1356  *  reads a character from a pico memorystream
1357  */
1358 int _pico_memstream_getc( picoMemStream_t *s ){
1359         int c = 0;
1360
1361         /* sanity check */
1362         if ( s == NULL ) {
1363                 return -1;
1364         }
1365
1366         /* read the character */
1367         if ( _pico_memstream_read( s, &c, 1 ) == 0 ) {
1368                 return -1;
1369         }
1370
1371         return c;
1372 }
1373
1374 /* _pico_memstream_seek:
1375  *  sets the current read position to a different location
1376  */
1377 int _pico_memstream_seek( picoMemStream_t *s, long offset, int origin ){
1378         int overflow;
1379
1380         /* sanity check */
1381         if ( s == NULL ) {
1382                 return -1;
1383         }
1384
1385         if ( origin == PICO_SEEK_SET ) {
1386                 s->curPos = s->buffer + offset;
1387                 overflow = s->curPos - ( s->buffer + s->bufSize );
1388                 if ( overflow > 0 ) {
1389                         s->curPos = s->buffer + s->bufSize;
1390                         return offset - overflow;
1391                 }
1392                 return 0;
1393         }
1394         else if ( origin == PICO_SEEK_CUR ) {
1395                 s->curPos += offset;
1396                 overflow = s->curPos - ( s->buffer + s->bufSize );
1397                 if ( overflow > 0 ) {
1398                         s->curPos = s->buffer + s->bufSize;
1399                         return offset - overflow;
1400                 }
1401                 return 0;
1402         }
1403         else if ( origin == PICO_SEEK_END ) {
1404                 s->curPos = ( s->buffer + s->bufSize ) - offset;
1405                 overflow = s->buffer - s->curPos;
1406                 if ( overflow > 0 ) {
1407                         s->curPos = s->buffer;
1408                         return offset - overflow;
1409                 }
1410                 return 0;
1411         }
1412
1413         return -1;
1414 }
1415
1416 /* _pico_memstream_tell:
1417  *  returns the current read position in the pico memorystream
1418  */
1419 long _pico_memstream_tell( picoMemStream_t *s ){
1420         /* sanity check */
1421         if ( s == NULL ) {
1422                 return -1;
1423         }
1424
1425         return s->curPos - s->buffer;
1426 }