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