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  */
_pico_alloc(size_t size)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  */
_pico_calloc(size_t num,size_t size)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  */
_pico_realloc(void ** ptr,size_t oldSize,size_t newSize)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  */
_pico_clone_alloc(const char * str)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  */
_pico_free(void * ptr)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  */
_pico_load_file(const char * name,unsigned char ** buffer,int * bufSize)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  */
_pico_free_file(void * buffer)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  */
_pico_printf(int level,const char * format,...)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 
_pico_first_token(char * str)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  */
_pico_strltrim(char * str)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  */
_pico_strrtrim(char * str)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  */
_pico_strlwr(char * str)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  */
_pico_strchcount(char * str,int ch)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 
_pico_zero_bounds(picoVec3_t mins,picoVec3_t maxs)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 
_pico_expand_bounds(picoVec3_t p,picoVec3_t mins,picoVec3_t maxs)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 
_pico_zero_vec(picoVec3_t vec)368 void _pico_zero_vec( picoVec3_t vec ){
369 	vec[ 0 ] = vec[ 1 ] = vec[ 2 ] = 0;
370 }
371 
_pico_zero_vec2(picoVec2_t vec)372 void _pico_zero_vec2( picoVec2_t vec ){
373 	vec[ 0 ] = vec[ 1 ] = 0;
374 }
375 
_pico_zero_vec4(picoVec4_t vec)376 void _pico_zero_vec4( picoVec4_t vec ){
377 	vec[ 0 ] = vec[ 1 ] = vec[ 2 ] = vec[ 3 ] = 0;
378 }
379 
_pico_set_vec(picoVec3_t v,float a,float b,float c)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 
_pico_set_vec4(picoVec4_t v,float a,float b,float c,float d)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 
_pico_copy_vec(picoVec3_t src,picoVec3_t dest)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 
_pico_copy_vec2(picoVec2_t src,picoVec2_t dest)399 void _pico_copy_vec2( picoVec2_t src, picoVec2_t dest ){
400 	dest[ 0 ] = src[ 0 ];
401 	dest[ 1 ] = src[ 1 ];
402 }
403 
_pico_copy_vec4(picoVec4_t src,picoVec4_t dest)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 */
_pico_normalize_vec(picoVec3_t vec)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 
_pico_add_vec(picoVec3_t a,picoVec3_t b,picoVec3_t dest)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 
_pico_subtract_vec(picoVec3_t a,picoVec3_t b,picoVec3_t dest)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 
_pico_scale_vec(picoVec3_t v,float scale,picoVec3_t dest)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 
_pico_scale_vec4(picoVec4_t v,float scale,picoVec4_t dest)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 
_pico_dot_vec(picoVec3_t a,picoVec3_t b)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 
_pico_cross_vec(picoVec3_t a,picoVec3_t b,picoVec3_t dest)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 
_pico_calc_plane(picoVec4_t plane,picoVec3_t a,picoVec3_t b,picoVec3_t c)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 */
_pico_set_color(picoColor_t c,int r,int g,int b,int a)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 
_pico_copy_color(picoColor_t src,picoColor_t dest)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 
_pico_big_long(int src)488 int   _pico_big_long( int src ) { return src; }
_pico_big_short(short src)489 short _pico_big_short( short src ) { return src; }
_pico_big_float(float src)490 float _pico_big_float( float src ) { return src; }
491 
_pico_little_long(int src)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 
_pico_little_short(short src)499 short _pico_little_short( short src ){
500 	return ( ( src & 0xFF00 ) >> 8 ) |
501 		   ( ( src & 0x00FF ) << 8 );
502 }
503 
_pico_little_float(float src)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 
_pico_little_long(int src)515 int   _pico_little_long( int src ) { return src; }
_pico_little_short(short src)516 short _pico_little_short( short src ) { return src; }
_pico_little_float(float src)517 float _pico_little_float( float src ) { return src; }
518 
_pico_big_long(int src)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 
_pico_big_short(short src)526 short _pico_big_short( short src ){
527 	return ( ( src & 0xFF00 ) >> 8 ) |
528 		   ( ( src & 0x00FF ) << 8 );
529 }
530 
_pico_big_float(float src)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  */
_pico_stristr(const char * str,const char * substr)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 
_pico_unixify(char * path)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  */
_pico_nofname(const char * path,char * dest,int destSize)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  */
_pico_nopath(const char * path)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  */
_pico_setfext(char * path,const char * ext)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  */
_pico_getline(char * buf,int bufsize,char * dest,int destsize)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  */
_pico_parse_skip_white(picoParser_t * p,int * hasLFs)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  */
_pico_new_parser(const picoByte_t * buffer,int bufSize)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  */
_pico_free_parser(picoParser_t * p)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  */
_pico_parse_ex(picoParser_t * p,int allowLFs,int handleQuoted)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  */
_pico_parse_first(picoParser_t * p)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  */
_pico_parse(picoParser_t * p,int allowLFs)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  */
_pico_parse_skip_rest(picoParser_t * p)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  */
_pico_parse_skip_braced(picoParser_t * p)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 
_pico_parse_check(picoParser_t * p,int allowLFs,char * str)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 
_pico_parse_checki(picoParser_t * p,int allowLFs,char * str)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 
_pico_parse_int(picoParser_t * p,int * out)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 
_pico_parse_int_def(picoParser_t * p,int * out,int def)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 
_pico_parse_float(picoParser_t * p,float * out)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 
_pico_parse_float_def(picoParser_t * p,float * out,float def)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 
_pico_parse_vec(picoParser_t * p,picoVec3_t out)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 
_pico_parse_vec_def(picoParser_t * p,picoVec3_t out,picoVec3_t def)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 
_pico_parse_vec2(picoParser_t * p,picoVec2_t out)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 
_pico_parse_vec2_def(picoParser_t * p,picoVec2_t out,picoVec2_t def)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 
_pico_parse_vec4(picoParser_t * p,picoVec4_t out)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 
_pico_parse_vec4_def(picoParser_t * p,picoVec4_t out,picoVec4_t def)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  */
_pico_new_memstream(const picoByte_t * buffer,int bufSize)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  */
_pico_free_memstream(picoMemStream_t * s)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  */
_pico_memstream_read(picoMemStream_t * s,void * buffer,int len)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  */
_pico_memstream_getc(picoMemStream_t * s)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  */
_pico_memstream_seek(picoMemStream_t * s,long offset,int origin)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  */
_pico_memstream_tell(picoMemStream_t * s)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 }
1356