]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/picointernal.c
Merge branch 'master' into Mario/winfix
[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 /* separate from _pico_set_vec4 */
466 void _pico_set_color( picoColor_t c, int r, int g, int b, int a ){
467         c[ 0 ] = r;
468         c[ 1 ] = g;
469         c[ 2 ] = b;
470         c[ 3 ] = a;
471 }
472
473 void _pico_copy_color( picoColor_t src, picoColor_t dest ){
474         dest[ 0 ] = src[ 0 ];
475         dest[ 1 ] = src[ 1 ];
476         dest[ 2 ] = src[ 2 ];
477         dest[ 3 ] = src[ 3 ];
478 }
479
480 #if GDEF_ARCH_ENDIAN_BIG
481
482 int   _pico_big_long( int src ) { return src; }
483 short _pico_big_short( short src ) { return src; }
484 float _pico_big_float( float src ) { return src; }
485
486 int _pico_little_long( int src ){
487         return ( ( src & 0xFF000000 ) >> 24 ) |
488                    ( ( src & 0x00FF0000 ) >> 8 ) |
489                    ( ( src & 0x0000FF00 ) << 8 ) |
490                    ( ( src & 0x000000FF ) << 24 );
491 }
492
493 short _pico_little_short( short src ){
494         return ( ( src & 0xFF00 ) >> 8 ) |
495                    ( ( src & 0x00FF ) << 8 );
496 }
497
498 float _pico_little_float( float src ){
499         floatSwapUnion in,out;
500         in.f = src;
501         out.c[ 0 ] = in.c[ 3 ];
502         out.c[ 1 ] = in.c[ 2 ];
503         out.c[ 2 ] = in.c[ 1 ];
504         out.c[ 3 ] = in.c[ 0 ];
505         return out.f;
506 }
507 #else /*__BIG_ENDIAN__*/
508
509 int   _pico_little_long( int src ) { return src; }
510 short _pico_little_short( short src ) { return src; }
511 float _pico_little_float( float src ) { return src; }
512
513 int _pico_big_long( int src ){
514         return ( ( src & 0xFF000000 ) >> 24 ) |
515                    ( ( src & 0x00FF0000 ) >> 8 ) |
516                    ( ( src & 0x0000FF00 ) << 8 ) |
517                    ( ( src & 0x000000FF ) << 24 );
518 }
519
520 short _pico_big_short( short src ){
521         return ( ( src & 0xFF00 ) >> 8 ) |
522                    ( ( src & 0x00FF ) << 8 );
523 }
524
525 float _pico_big_float( float src ){
526         floatSwapUnion in,out;
527         in.f = src;
528         out.c[ 0 ] = in.c[ 3 ];
529         out.c[ 1 ] = in.c[ 2 ];
530         out.c[ 2 ] = in.c[ 1 ];
531         out.c[ 3 ] = in.c[ 0 ];
532         return out.f;
533 }
534 #endif /*__BIG_ENDIAN__*/
535
536 /* _pico_stristr:
537  *  case-insensitive strstr. -sea
538  */
539 const char *_pico_stristr( const char *str, const char *substr ){
540         const size_t sublen = strlen( substr );
541         while ( *str )
542         {
543                 if ( !_pico_strnicmp( str,substr,sublen ) ) {
544                         break;
545                 }
546                 str++;
547         }
548         if ( !( *str ) ) {
549                 str = NULL;
550         }
551         return str;
552 }
553
554 /*
555    _pico_unixify()
556    changes dos \ style path separators to /
557  */
558
559 void _pico_unixify( char *path ){
560         if ( path == NULL ) {
561                 return;
562         }
563         while ( *path )
564         {
565                 if ( *path == '\\' ) {
566                         *path = '/';
567                 }
568                 path++;
569         }
570 }
571
572 /* _pico_nofname:
573  *  removes file name portion from given file path and converts
574  *  the directory separators to un*x style. returns 1 on success
575  *  or 0 when 'destSize' was exceeded. -sea
576  */
577 int _pico_nofname( const char *path, char *dest, int destSize ){
578         int left  = destSize;
579         char *temp  = dest;
580
581         while ( ( *dest = *path ) != '\0' )
582         {
583                 if ( *dest == '/' || *dest == '\\' ) {
584                         temp = ( dest + 1 );
585                         *dest = '/';
586                 }
587                 dest++; path++;
588
589                 if ( --left < 1 ) {
590                         *temp = '\0';
591                         return 0;
592                 }
593         }
594         *temp = '\0';
595         return 1;
596 }
597
598 /* _pico_nopath:
599  *  returns ptr to filename portion in given path or an empty
600  *  string otherwise. given 'path' is not altered. -sea
601  */
602 const char *_pico_nopath( const char *path ){
603         const char *src;
604         src = path + ( strlen( path ) - 1 );
605
606         if ( path == NULL ) {
607                 return "";
608         }
609         if ( !strchr( path,'/' ) && !strchr( path,'\\' ) ) {
610                 return ( path );
611         }
612
613         while ( ( src-- ) != path )
614         {
615                 if ( *src == '/' || *src == '\\' ) {
616                         return ( ++src );
617                 }
618         }
619         return "";
620 }
621
622 /* _pico_setfext:
623  *  sets/changes the file extension for the given filename
624  *  or filepath's filename portion. the given 'path' *is*
625  *  altered. leave 'ext' empty to remove extension. -sea
626  */
627 char *_pico_setfext( char *path, const char *ext ){
628         char *src;
629         int remfext = 0;
630
631         src = path + ( strlen( path ) - 1 );
632
633         if ( ext == NULL ) {
634                 ext = "";
635         }
636         if ( strlen( ext ) < 1 ) {
637                 remfext = 1;
638         }
639         if ( strlen( path ) < 1 ) {
640                 return path;
641         }
642
643         while ( ( src-- ) != path )
644         {
645                 if ( *src == '/' || *src == '\\' ) {
646                         return path;
647                 }
648
649                 if ( *src == '.' ) {
650                         if ( remfext ) {
651                                 *src = '\0';
652                                 return path;
653                         }
654                         *( ++src ) = '\0';
655                         break;
656                 }
657         }
658         strcat( path,ext );
659         return path;
660 }
661
662 /* _pico_getline:
663  *  extracts one line from the given buffer and stores it in dest.
664  *  returns -1 on error or the length of the line on success. i've
665  *  removed string trimming here. this can be done manually by the
666  *  calling func.
667  */
668 int _pico_getline( char *buf, int bufsize, char *dest, int destsize ){
669         int pos;
670
671         /* check output */
672         if ( dest == NULL || destsize < 1 ) {
673                 return -1;
674         }
675         memset( dest,0,destsize );
676
677         /* check input */
678         if ( buf == NULL || bufsize < 1 ) {
679                 return -1;
680         }
681
682         /* get next line */
683         for ( pos = 0; pos < bufsize && pos < destsize; pos++ )
684         {
685                 if ( buf[pos] == '\n' ) {
686                         pos++; break;
687                 }
688                 dest[pos] = buf[pos];
689         }
690         /* terminate dest and return */
691         dest[pos] = '\0';
692         return pos;
693 }
694
695 /* _pico_parse_skip_white:
696  *  skips white spaces in current pico parser, sets *hasLFs
697  *  to 1 if linefeeds were skipped, and either returns the
698  *  parser's cursor pointer or NULL on error. -sea
699  */
700 void _pico_parse_skip_white( picoParser_t *p, int *hasLFs ){
701         /* sanity checks */
702         if ( p == NULL || p->cursor == NULL ) {
703                 return;
704         }
705
706         /* skin white spaces */
707         while ( 1 )
708         {
709                 /* sanity checks */
710                 if ( p->cursor <  p->buffer ||
711                          p->cursor >= p->max ) {
712                         return;
713                 }
714                 /* break for chars other than white spaces */
715                 if ( *p->cursor >  0x20 ) {
716                         break;
717                 }
718                 if ( *p->cursor == 0x00 ) {
719                         return;
720                 }
721
722                 /* a bit of linefeed handling */
723                 if ( *p->cursor == '\n' ) {
724                         *hasLFs = 1;
725                         p->curLine++;
726                 }
727                 /* go to next character */
728                 p->cursor++;
729         }
730 }
731
732 /* _pico_new_parser:
733  *  allocates a new ascii parser object.
734  */
735 picoParser_t *_pico_new_parser( const picoByte_t *buffer, int bufSize ){
736         picoParser_t *p;
737
738         /* sanity check */
739         if ( buffer == NULL || bufSize <= 0 ) {
740                 return NULL;
741         }
742
743         /* allocate reader */
744         p = _pico_alloc( sizeof( picoParser_t ) );
745         if ( p == NULL ) {
746                 return NULL;
747         }
748         memset( p,0,sizeof( picoParser_t ) );
749
750         /* allocate token space */
751         p->tokenSize = 0;
752         p->tokenMax = 1024;
753         p->token = _pico_alloc( p->tokenMax );
754         if ( p->token == NULL ) {
755                 _pico_free( p );
756                 return NULL;
757         }
758         /* setup */
759         p->buffer   = (const char *) buffer;
760         p->cursor   = p->buffer;
761         p->bufSize  = bufSize;
762         p->max      = p->buffer + bufSize;
763         p->curLine = 1; /* sea: new */
764
765         /* return ptr to parser */
766         return p;
767 }
768
769 /* _pico_free_parser:
770  *  frees an existing pico parser object.
771  */
772 void _pico_free_parser( picoParser_t *p ){
773         /* sanity check */
774         if ( p == NULL ) {
775                 return;
776         }
777
778         /* free the parser */
779         if ( p->token != NULL ) {
780                 _pico_free( p->token );
781         }
782         _pico_free( p );
783 }
784
785 /* _pico_parse_ex:
786  *  reads the next token from given pico parser object. if param
787  * 'allowLFs' is 1 it will read beyond linefeeds and return 0 when
788  *  the EOF is reached. if 'allowLFs' is 0 it will return 0 when
789  *  the EOL is reached. if 'handleQuoted' is 1 the parser function
790  *  will handle "quoted" strings and return the data between the
791  *  quotes as token. returns 0 on end/error or 1 on success. -sea
792  */
793 int _pico_parse_ex( picoParser_t *p, int allowLFs, int handleQuoted ){
794         int hasLFs = 0;
795         const char *old;
796
797         /* sanity checks */
798         if ( p == NULL || p->buffer == NULL ||
799                  p->cursor <  p->buffer ||
800                  p->cursor >= p->max ) {
801                 return 0;
802         }
803         /* clear parser token */
804         p->tokenSize = 0;
805         p->token[ 0 ] = '\0';
806         old = p->cursor;
807
808         /* skip whitespaces */
809         while ( p->cursor < p->max && *p->cursor <= 32 )
810         {
811                 if ( *p->cursor == '\n' ) {
812                         p->curLine++;
813                         hasLFs++;
814                 }
815                 p->cursor++;
816         }
817         /* return if we're not allowed to go beyond lfs */
818         if ( ( hasLFs > 0 ) && !allowLFs ) {
819                 p->cursor = old;
820                 return 0;
821         }
822         /* get next quoted string */
823         if ( *p->cursor == '\"' && handleQuoted ) {
824                 p->cursor++;
825                 while ( p->cursor < p->max && *p->cursor )
826                 {
827                         if ( *p->cursor == '\\' ) {
828                                 if ( *( p->cursor + 1 ) == '"' ) {
829                                         p->cursor++;
830                                 }
831                                 p->token[ p->tokenSize++ ] = *p->cursor++;
832                                 continue;
833                         }
834                         else if ( *p->cursor == '\"' ) {
835                                 p->cursor++;
836                                 break;
837                         }
838                         else if ( *p->cursor == '\n' ) {
839                                 p->curLine++;
840                         }
841                         p->token[ p->tokenSize++ ] = *p->cursor++;
842                 }
843                 /* terminate token */
844                 p->token[ p->tokenSize ] = '\0';
845                 return 1;
846         }
847         /* otherwise get next word */
848         while ( p->cursor < p->max && *p->cursor > 32 )
849         {
850                 if ( *p->cursor == '\n' ) {
851                         p->curLine++;
852                 }
853                 p->token[ p->tokenSize++ ] = *p->cursor++;
854         }
855         /* terminate token */
856         p->token[ p->tokenSize ] = '\0';
857         return 1;
858 }
859
860 /* _pico_parse_first:
861  *  reads the first token from the next line and returns
862  *  a pointer to it. returns NULL on EOL or EOF. -sea
863  */
864 char *_pico_parse_first( picoParser_t *p ){
865         /* sanity check */
866         if ( p == NULL ) {
867                 return NULL;
868         }
869
870         /* try to read next token (with lfs & quots) */
871         if ( !_pico_parse_ex( p,1,1 ) ) {
872                 return NULL;
873         }
874
875         /* return ptr to the token string */
876         return p->token;
877 }
878
879 /* _pico_parse:
880  *  reads the next token from the parser and returns a pointer
881  *  to it. quoted strings are handled as usual. returns NULL
882  *  on EOL or EOF. -sea
883  */
884 char *_pico_parse( picoParser_t *p, int allowLFs ){
885         /* sanity check */
886         if ( p == NULL ) {
887                 return NULL;
888         }
889
890         /* try to read next token (with quots) */
891         if ( !_pico_parse_ex( p,allowLFs,1 ) ) {
892                 return NULL;
893         }
894
895         /* return ptr to the token string */
896         return p->token;
897 }
898
899 /* _pico_parse_skip_rest:
900  *  skips the rest of the current line in parser.
901  */
902 void _pico_parse_skip_rest( picoParser_t *p ){
903         while ( _pico_parse_ex( p,0,0 ) ) ;
904 }
905
906 /* _pico_parse_skip_braced:
907  *  parses/skips over a braced section. returns 1 on success
908  *  or 0 on error (when there was no closing bracket and the
909  *  end of buffer was reached or when the opening bracket was
910  *  missing).
911  */
912 int _pico_parse_skip_braced( picoParser_t *p ){
913         int firstToken = 1;
914         int level;
915
916         /* sanity check */
917         if ( p == NULL ) {
918                 return 0;
919         }
920
921         /* set the initial level for parsing */
922         level = 0;
923
924         /* skip braced section */
925         while ( 1 )
926         {
927                 /* read next token (lfs allowed) */
928                 if ( !_pico_parse_ex( p,1,1 ) ) {
929                         /* end of parser buffer reached */
930                         return 0;
931                 }
932                 /* first token must be an opening bracket */
933                 if ( firstToken && p->token[0] != '{' ) {
934                         /* opening bracket missing */
935                         return 0;
936                 }
937                 /* we only check this once */
938                 firstToken = 0;
939
940                 /* update level */
941                 if ( p->token[1] == '\0' ) {
942                         if ( p->token[0] == '{' ) {
943                                 level++;
944                         }
945                         if ( p->token[0] == '}' ) {
946                                 level--;
947                         }
948                 }
949                 /* break if we're back at our starting level */
950                 if ( level == 0 ) {
951                         break;
952                 }
953         }
954         /* successfully skipped braced section */
955         return 1;
956 }
957
958 int _pico_parse_check( picoParser_t *p, int allowLFs, char *str ){
959         if ( !_pico_parse_ex( p,allowLFs,1 ) ) {
960                 return 0;
961         }
962         if ( !strcmp( p->token,str ) ) {
963                 return 1;
964         }
965         return 0;
966 }
967
968 int _pico_parse_checki( picoParser_t *p, int allowLFs, char *str ){
969         if ( !_pico_parse_ex( p,allowLFs,1 ) ) {
970                 return 0;
971         }
972         if ( !_pico_stricmp( p->token,str ) ) {
973                 return 1;
974         }
975         return 0;
976 }
977
978 int _pico_parse_int( picoParser_t *p, int *out ){
979         char *token;
980
981         /* sanity checks */
982         if ( p == NULL || out == NULL ) {
983                 return 0;
984         }
985
986         /* get token and turn it into an integer */
987         *out = 0;
988         token = _pico_parse( p,0 );
989         if ( token == NULL ) {
990                 return 0;
991         }
992         *out = atoi( token );
993
994         /* success */
995         return 1;
996 }
997
998 int _pico_parse_int_def( picoParser_t *p, int *out, int def ){
999         char *token;
1000
1001         /* sanity checks */
1002         if ( p == NULL || out == NULL ) {
1003                 return 0;
1004         }
1005
1006         /* get token and turn it into an integer */
1007         *out = def;
1008         token = _pico_parse( p,0 );
1009         if ( token == NULL ) {
1010                 return 0;
1011         }
1012         *out = atoi( token );
1013
1014         /* success */
1015         return 1;
1016 }
1017
1018 int _pico_parse_float( picoParser_t *p, float *out ){
1019         char *token;
1020
1021         /* sanity checks */
1022         if ( p == NULL || out == NULL ) {
1023                 return 0;
1024         }
1025
1026         /* get token and turn it into a float */
1027         *out = 0.0f;
1028         token = _pico_parse( p,0 );
1029         if ( token == NULL ) {
1030                 return 0;
1031         }
1032         *out = (float) atof( token );
1033
1034         /* success */
1035         return 1;
1036 }
1037
1038 int _pico_parse_float_def( picoParser_t *p, float *out, float def ){
1039         char *token;
1040
1041         /* sanity checks */
1042         if ( p == NULL || out == NULL ) {
1043                 return 0;
1044         }
1045
1046         /* get token and turn it into a float */
1047         *out = def;
1048         token = _pico_parse( p,0 );
1049         if ( token == NULL ) {
1050                 return 0;
1051         }
1052         *out = (float) atof( token );
1053
1054         /* success */
1055         return 1;
1056 }
1057
1058 int _pico_parse_vec( picoParser_t *p, picoVec3_t out ){
1059         char *token;
1060         int i;
1061
1062         /* sanity checks */
1063         if ( p == NULL || out == NULL ) {
1064                 return 0;
1065         }
1066
1067         /* zero out outination vector */
1068         _pico_zero_vec( out );
1069
1070         /* parse three vector components */
1071         for ( i = 0; i < 3; i++ )
1072         {
1073                 token = _pico_parse( p,0 );
1074                 if ( token == NULL ) {
1075                         _pico_zero_vec( out );
1076                         return 0;
1077                 }
1078                 out[ i ] = (float) atof( token );
1079         }
1080         /* success */
1081         return 1;
1082 }
1083
1084 int _pico_parse_vec_def( picoParser_t *p, picoVec3_t out, picoVec3_t def ){
1085         char *token;
1086         int i;
1087
1088         /* sanity checks */
1089         if ( p == NULL || out == NULL ) {
1090                 return 0;
1091         }
1092
1093         /* assign default vector value */
1094         _pico_copy_vec( def,out );
1095
1096         /* parse three vector components */
1097         for ( i = 0; i < 3; i++ )
1098         {
1099                 token = _pico_parse( p,0 );
1100                 if ( token == NULL ) {
1101                         _pico_copy_vec( def,out );
1102                         return 0;
1103                 }
1104                 out[ i ] = (float) atof( token );
1105         }
1106         /* success */
1107         return 1;
1108 }
1109
1110 int _pico_parse_vec2( picoParser_t *p, picoVec2_t out ){
1111         char *token;
1112         int i;
1113
1114         /* sanity checks */
1115         if ( p == NULL || out == NULL ) {
1116                 return 0;
1117         }
1118
1119         /* zero out outination vector */
1120         _pico_zero_vec2( out );
1121
1122         /* parse two vector components */
1123         for ( i = 0; i < 2; i++ )
1124         {
1125                 token = _pico_parse( p,0 );
1126                 if ( token == NULL ) {
1127                         _pico_zero_vec2( out );
1128                         return 0;
1129                 }
1130                 out[ i ] = (float) atof( token );
1131         }
1132         /* success */
1133         return 1;
1134 }
1135
1136 int _pico_parse_vec2_def( picoParser_t *p, picoVec2_t out, picoVec2_t def ){
1137         char *token;
1138         int i;
1139
1140         /* sanity checks */
1141         if ( p == NULL || out == NULL ) {
1142                 return 0;
1143         }
1144
1145         /* assign default vector value */
1146         _pico_copy_vec2( def,out );
1147
1148         /* parse two vector components */
1149         for ( i = 0; i < 2; i++ )
1150         {
1151                 token = _pico_parse( p,0 );
1152                 if ( token == NULL ) {
1153                         _pico_copy_vec2( def,out );
1154                         return 0;
1155                 }
1156                 out[ i ] = (float) atof( token );
1157         }
1158         /* success */
1159         return 1;
1160 }
1161
1162 int _pico_parse_vec4( picoParser_t *p, picoVec4_t out ){
1163         char *token;
1164         int i;
1165
1166         /* sanity checks */
1167         if ( p == NULL || out == NULL ) {
1168                 return 0;
1169         }
1170
1171         /* zero out outination vector */
1172         _pico_zero_vec4( out );
1173
1174         /* parse four vector components */
1175         for ( i = 0; i < 4; i++ )
1176         {
1177                 token = _pico_parse( p,0 );
1178                 if ( token == NULL ) {
1179                         _pico_zero_vec4( out );
1180                         return 0;
1181                 }
1182                 out[ i ] = (float) atof( token );
1183         }
1184         /* success */
1185         return 1;
1186 }
1187
1188 int _pico_parse_vec4_def( picoParser_t *p, picoVec4_t out, picoVec4_t def ){
1189         char *token;
1190         int i;
1191
1192         /* sanity checks */
1193         if ( p == NULL || out == NULL ) {
1194                 return 0;
1195         }
1196
1197         /* assign default vector value */
1198         _pico_copy_vec4( def,out );
1199
1200         /* parse four vector components */
1201         for ( i = 0; i < 4; i++ )
1202         {
1203                 token = _pico_parse( p,0 );
1204                 if ( token == NULL ) {
1205                         _pico_copy_vec4( def,out );
1206                         return 0;
1207                 }
1208                 out[ i ] = (float) atof( token );
1209         }
1210         /* success */
1211         return 1;
1212 }
1213
1214 /* _pico_new_memstream:
1215  *  allocates a new memorystream object.
1216  */
1217 picoMemStream_t *_pico_new_memstream( const picoByte_t *buffer, int bufSize ){
1218         picoMemStream_t *s;
1219
1220         /* sanity check */
1221         if ( buffer == NULL || bufSize <= 0 ) {
1222                 return NULL;
1223         }
1224
1225         /* allocate stream */
1226         s = _pico_alloc( sizeof( picoMemStream_t ) );
1227         if ( s == NULL ) {
1228                 return NULL;
1229         }
1230         memset( s,0,sizeof( picoMemStream_t ) );
1231
1232         /* setup */
1233         s->buffer   = buffer;
1234         s->curPos   = buffer;
1235         s->bufSize  = bufSize;
1236         s->flag     = 0;
1237
1238         /* return ptr to stream */
1239         return s;
1240 }
1241
1242 /* _pico_free_memstream:
1243  *  frees an existing pico memorystream object.
1244  */
1245 void _pico_free_memstream( picoMemStream_t *s ){
1246         /* sanity check */
1247         if ( s == NULL ) {
1248                 return;
1249         }
1250
1251         /* free the stream */
1252         _pico_free( s );
1253 }
1254
1255 /* _pico_memstream_read:
1256  *  reads data from a pico memorystream into a buffer.
1257  */
1258 int _pico_memstream_read( picoMemStream_t *s, void *buffer, int len ){
1259         int ret = 1;
1260
1261         /* sanity checks */
1262         if ( s == NULL || buffer == NULL ) {
1263                 return 0;
1264         }
1265
1266         if ( s->curPos + len > s->buffer + s->bufSize ) {
1267                 s->flag |= PICO_IOEOF;
1268                 len = s->buffer + s->bufSize - s->curPos;
1269                 ret = 0;
1270         }
1271
1272         /* read the data */
1273         memcpy( buffer, s->curPos, len );
1274         s->curPos += len;
1275         return ret;
1276 }
1277
1278 /* _pico_memstream_read:
1279  *  reads a character from a pico memorystream
1280  */
1281 int _pico_memstream_getc( picoMemStream_t *s ){
1282         int c = 0;
1283
1284         /* sanity check */
1285         if ( s == NULL ) {
1286                 return -1;
1287         }
1288
1289         /* read the character */
1290         if ( _pico_memstream_read( s, &c, 1 ) == 0 ) {
1291                 return -1;
1292         }
1293
1294         return c;
1295 }
1296
1297 /* _pico_memstream_seek:
1298  *  sets the current read position to a different location
1299  */
1300 int _pico_memstream_seek( picoMemStream_t *s, long offset, int origin ){
1301         int overflow;
1302
1303         /* sanity check */
1304         if ( s == NULL ) {
1305                 return -1;
1306         }
1307
1308         if ( origin == PICO_SEEK_SET ) {
1309                 s->curPos = s->buffer + offset;
1310                 overflow = s->curPos - ( s->buffer + s->bufSize );
1311                 if ( overflow > 0 ) {
1312                         s->curPos = s->buffer + s->bufSize;
1313                         return offset - overflow;
1314                 }
1315                 return 0;
1316         }
1317         else if ( origin == PICO_SEEK_CUR ) {
1318                 s->curPos += offset;
1319                 overflow = s->curPos - ( s->buffer + s->bufSize );
1320                 if ( overflow > 0 ) {
1321                         s->curPos = s->buffer + s->bufSize;
1322                         return offset - overflow;
1323                 }
1324                 return 0;
1325         }
1326         else if ( origin == PICO_SEEK_END ) {
1327                 s->curPos = ( s->buffer + s->bufSize ) - offset;
1328                 overflow = s->buffer - s->curPos;
1329                 if ( overflow > 0 ) {
1330                         s->curPos = s->buffer;
1331                         return offset - overflow;
1332                 }
1333                 return 0;
1334         }
1335
1336         return -1;
1337 }
1338
1339 /* _pico_memstream_tell:
1340  *  returns the current read position in the pico memorystream
1341  */
1342 long _pico_memstream_tell( picoMemStream_t *s ){
1343         /* sanity check */
1344         if ( s == NULL ) {
1345                 return -1;
1346         }
1347
1348         return s->curPos - s->buffer;
1349 }