FreeWRL / FreeX3D 4.3.0
resources.c
1/*
2
3 FreeWRL support library.
4 Resources handling: URL, files, ...
5
6*/
7
8/****************************************************************************
9 This file is part of the FreeWRL/FreeX3D Distribution.
10
11 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
12
13 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
14 it under the terms of the GNU Lesser Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
25****************************************************************************/
26
27
28
29#include <config.h>
30#include <system.h>
31#include <system_threads.h>
32#include <display.h>
33#include <internal.h>
34#include <libFreeWRL.h>
35
36#include "vrml_parser/Structs.h"
37#include "input/InputFunctions.h"
38#include "opengl/OpenGL_Utils.h"
39#include "opengl/Textures.h" /* for finding a texture url in a multi url */
40#include "opengl/LoadTextures.h" /* for finding a texture url in a multi url */
41
42#include <list.h>
43#include <io_files.h>
44#include <io_http.h>
45#include <threads.h>
46
47#ifdef _ANDROID
48#include <strings.h>
49#endif
50
51#include "zlib.h"
52//#define DEBUG_RES printf
53static void possiblyUnzip (openned_file_t *of);
54
55void close_openned_file(openned_file_t *file);
56
57typedef struct presources{
58 struct Vector *resStack; //=NULL;
59 resource_item_t *lastBaseResource; //=NULL;
60}* presources;
61void *resources_constructor()
62{
63 void *v = MALLOCV(sizeof(struct presources));
64 memset(v,0,sizeof(struct presources));
65 return v;
66}
67void resources_init(struct tresources* t)
68{
69 //public
70 //private
71 //presources p;
72 t->prv = resources_constructor();
73 //p = (presources)t->prv);
74}
75void resources_clear(struct tresources* t)
76{
77 //public
78 //private
79 presources p;
80 p = (presources)t->prv;
81 deleteVector(void *,p->resStack);
82}
83
84
85/* move Michel Briand's initialization code to one place to ensure consistency
86 when fields are added/removed */
87
88resource_item_t *newResourceItem() {
89 resource_item_t *item = XALLOC(resource_item_t);
90
91 /* item is NULL for every byte; some of the enums might not work out to 0 */
92 item->media_type = resm_unknown;
93 item->type = rest_invalid;
94 item->status = ress_invalid;
95 item->parent = NULL;
96 item->actual_file = NULL;
97 item->cached_files = NULL;
98 item->tg = gglobal();
99 return item;
100}
101
102
116static void resource_tree_append(resource_item_t *item){
117 /* Lock access to the resource tree */
118 pthread_mutex_lock( &gglobal()->threads.mutex_resource_tree );
119
120 if (!gglobal()->resources.root_res) {
121 /* This is the first resource we try to load */
122 gglobal()->resources.root_res = (void*)item;
123 DEBUG_RES("setting root_res in resource_create_single for file %s\n",request);
124 } else {
125 /* Not the first, so keep it in the main list */
126 ((resource_item_t*)gglobal()->resources.root_res)->children = ml_append(((resource_item_t*)gglobal()->resources.root_res)->children, ml_new(item));
127 item->parent = (resource_item_t*)gglobal()->resources.root_res;
128 }
129
130 /* Unlock the resource tree mutex */
131 pthread_mutex_unlock( &gglobal()->threads.mutex_resource_tree );
132}
133
134resource_item_t* resource_create_single0(const char *request)
135{
136 resource_item_t *item;
137 DEBUG_RES("creating resource: SINGLE: %s\n", request);
138
139 item = newResourceItem();
140 item->URLrequest = STRDUP(request);
141 item->_loadThread = NULL;
142 return item;
143}
144
145resource_item_t* resource_create_single(const char *request)
146{
147 resource_item_t *item = resource_create_single0(request);
148 resource_tree_append(item);
149 return item;
150}
151
157resource_item_t* resource_create_multi0(const s_Multi_String_t *request)
158{
159 /* anchor to new scene might use the multi0 directly, so plugin_res isn't deleted in killOldWorld */
160 int i;
161 resource_item_t *item;
162 DEBUG_RES("creating resource: MULTI: %d, %s ...\n", request->n, request->p[0]->strptr);
163 item = newResourceItem();
164
165
166 item->type = rest_multi;
167
168
169 /* Convert Mutli_String to a list string */
170 for (i = 0; i < request->n; i++) {
171 char *url = STRDUP(request->p[i]->strptr);
172 //ConsoleMessage ("putting %s on the list\n",url);
173 item->m_request = ml_append(item->m_request, ml_new(url));
174 }
175 return item;
176}
177resource_item_t* resource_create_multi(const s_Multi_String_t *request)
178{
179 resource_item_t *item = resource_create_multi0(request);
180 resource_tree_append(item);
181 return item;
182}
183
189resource_item_t* resource_create_from_string(const char *string)
190{
191 resource_item_t *item;
192 DEBUG_RES("creating resource: STRING: %s\n", string);
193 item = newResourceItem();
194
195
196 item->URLrequest = STRDUP(string);
197 item->type = rest_string;
198 item->status = ress_loaded;
199
200 resource_tree_append(item);
201 return item;
202}
203
204
205/*
206 * Check to see if the file name is a local file, or a network file.
207 * return TRUE if it looks like a file from the network, false if it
208 * is local to this machine
209 * October 2007 - Michel Briand suggested the https:// lines.
210 */
214bool checkNetworkFile(const char *fn)
215{
216 //int i = 0;
217 //char *pt = fn;
218
219 if (fn == NULL) {
220 ConsoleMessage ("checkNetworkFile, got a NULL here");
221 return FALSE;
222 }
223
224 // while (*pt != '\0') {
225 // ConsoleMessage ("cfn %d is %x %c",i,*pt,*pt);
226 // i++;
227 // pt++;
228 // }
229
230 //ConsoleMessage ("checkNetworkFile, have %s, len %d\n",fn,strlen(fn));
231
232 if ((strncmp(fn,"ftp://", strlen("ftp://"))) &&
233 (strncmp(fn,"FTP://", strlen("FTP://"))) &&
234 (strncmp(fn,"http://", strlen("http://"))) &&
235 (strncmp(fn,"HTTP://", strlen("HTTP://"))) &&
236 (strncmp(fn,"https://", strlen("https://"))) &&
237 (strncmp(fn,"HTTPS://", strlen("HTTPS://"))) &&
238/* JAS - these really are local files | MB - indeed :^) !
239 (strncmp(fn,"file://", strlen("file://"))) &&
240 (strncmp(fn,"FILE://", strlen("FILE://"))) &&
241*/
242 (strncmp(fn,"urn://", strlen("urn://"))) &&
243 (strncmp(fn,"URN://", strlen("URN://")))
244
245 ) {
246 //ConsoleMessage ("CNF returning FALSE");
247 return FALSE;
248 }
249 //ConsoleMessage ("CNF returning TRUE");
250 return TRUE;
251}
252
253
274 static int res_id_error_once = 0;
275void resource_identify(resource_item_t *baseResource, resource_item_t *res)
276{
277 bool network;
278 char *url = NULL;
279 size_t len;
280 resource_item_t *defaults = NULL;
281
282 ASSERT(res);
283 DEBUG_RES("resource_identify, we have resource %s ptrs %p and %p\n",res->URLrequest,baseResource,baseResource);
284
285 if (baseResource) {
286 DEBUG_RES(" base specified, taking the base values.\n");
287 defaults = baseResource;
288 res->parent = baseResource;
289 } else {
290 if (res->parent) {
291 DEBUG_RES(" no base specified, taking parent's values.\n");
292 defaults = res->parent;
293 } else {
294 DEBUG_RES(" no base neither parent, no default values.\n");
295 }
296 }
297
298 if (defaults) {
299 DEBUG_RES(" default values: network=%s type=%s status=%s"
300 " URLrequest=<%s> URLbase=<%s>parsed_request=<%s> [parent %p, %s]\n",
301 BOOL_STR(defaults->network), resourceTypeToString(defaults->type),
302 resourceStatusToString(defaults->status), defaults->URLrequest,
303 defaults->URLbase, defaults->parsed_request,
304 defaults->parent, (defaults->parent ? defaults->parent->URLbase : "N/A")
305 );
306 }
307
308 if (res->type == rest_multi) {
309 /* We want to consume the list of requests */
310 if (res->m_request) {
311 s_list_t *l;
312 l = res->m_request;
313 /* Pick up next request in our list */
314 FREE_IF_NZ(res->URLrequest);
315 res->URLrequest = (char *) l->elem;
316 /* Point to the next... */
317 res->m_request = res->m_request->next;
318 ml_free(l);
319 } else {
320 /* list empty - this error can be caused by a wrong USE='name' on URL node */
321 if(!res_id_error_once) //don't flood, there's probably a better error message before this
322 ERROR_MSG("resource_identify: ERROR: empty multi string as input\n");
323 res_id_error_once++;
324 return;
325 }
326 }
327
328 network = FALSE;
329 if (defaults) {
330 network = defaults->network;
331 }
332
333 {
334 char* pound;
335 pound = NULL;
336 pound = strchr(res->URLrequest, '#'); //moved here Aug2014 Q. should it be later on strdup of URLrequest?
337 if (pound != NULL) {
338 *pound = '\0';
339 /* copy the name out, so that Anchors can go to correct Viewpoint */
340 pound++;
341 res->afterPoundCharacters = STRDUP(pound);
342 }
343 }
344 /* URI specifier at the beginning ? */
345 res->network = checkNetworkFile(res->URLrequest);
346
347 DEBUG_RES("resource_identify: base network / resource network: %s/%s\n",
348 BOOL_STR(network),
349 BOOL_STR(res->network));
350
351 /* Parse request as url or local file ? */
352 if (res->network || network) {
353 /* We will always have a network url */
354
355 if (res->network) {
356 /* We have an absolute url for this resource */
357 res->type = rest_url;
358 res->status = ress_starts_good;
359 url = STRDUP(res->URLrequest);
360
361 } else {
362 /* We have an absolute url for main world,
363 and a relative url for this resource:
364 Create an url with base+request */
365 if (defaults) {
366
367 /* note that, if FRONTEND_GETS_FILES is defined, we have to clean
368 this, here. */
369
370 char *cleanedURL;
371 cleanedURL = stripLocalFileName(res->URLrequest);
372
373 /* Relative to base */
374 IF_cleanedURL_IS_ABSOLUTE {
375 /* this is an absolute url, which we can do, even if we have a base to
376 base this from. eg, url='/Users/john/tests/2.wrl' */
377 url = STRDUP(cleanedURL);
378 } else {
379 char *cwd;
380 cwd = STRDUP(defaults->URLbase);
381 url = concat_path(cwd, cleanedURL);
382 FREE_IF_NZ(cwd);
383 }
384 res->network = TRUE; //dug9 sep1,2013 added this line, so geoLod 2nd level texture sees its parent 2nd level .x3d as a network file
385 res->type = rest_url;
386 res->status = ress_starts_good;
387 } else {
388 res->type = rest_invalid;
389 ERROR_MSG("resource_identify: can't handle relative url without base: %s\n", res->URLrequest);
390 }
391 }
392
393 } else {
394 /* We may have a local file */
395 DEBUG_RES("resource_identify, we may have a local file for resource %s\n", res->URLrequest);
396
397 /* We do not want to have system error */
398 len = strlen(res->URLrequest);
399 if (len > PATH_MAX) {
400
401 res->type = rest_invalid;
402 url="invalid URL";
403 ERROR_MSG("resource_identify: path too long: %s\n", res->URLrequest);
404
405 } else {
406 char *cleanedURL = NULL;
407 /* remove any possible file:// off of the front of the name */
408 /* NOTE: this is NOT a new string, possibly just incremented res->request */
409
410 cleanedURL = stripLocalFileName(res->URLrequest);
411
412 /* We are relative to current dir or base */
413 if (defaults) {
414 /* Relative to base */
415 IF_cleanedURL_IS_ABSOLUTE {
416 /* this is an absolute url, which we can do, even if we have a base to
417 base this from. eg, url='/Users/john/tests/2.wrl' */
418 res->type = rest_file;
419 res->status = ress_starts_good;
420 url = STRDUP(cleanedURL);
421 } else {
422 char *cwd;
423 cwd = STRDUP(defaults->URLbase);
424 res->type = rest_file;
425 res->status = ress_starts_good;
426 url = concat_path(cwd, cleanedURL);
427 FREE_IF_NZ(cwd);
428 }
429
430 } else {
431 /* No default values: we are hanging alone */
432 /* Is this a full path ? */
433 IF_cleanedURL_IS_ABSOLUTE {
434 /* This is an absolute filename */
435
436 res->type = rest_file;
437 res->status = ress_starts_good;
438 url = STRDUP(cleanedURL);
439
440 } else {
441 /* Relative to current dir (we are loading main file/world) */
442 char *cwd;
443
444 cwd = get_current_dir();
445 removeFilenameFromPath(cwd);
446
447 /* Make full path from current dir and relative filename */
448
449 /* printf("about to join :%s: and :%s: resource.c L299\n",cwd,res->request);*/
450 url = concat_path(cwd, res->URLrequest);
451 res->type = rest_file;
452 res->status = ress_starts_good;
453 }
454 }
455 }
456 }
457
458 /* record the url, and the path to the url */
459 FREE_IF_NZ(res->parsed_request);
460 res->parsed_request = url;
461 FREE_IF_NZ(res->URLbase);
462 res->URLbase = STRDUP(url);
463 removeFilenameFromPath(res->URLbase);
464
465 // ok we should be good to go now res->network = TRUE;
466
467 DEBUG_RES("resource_identify (end): network=%s type=%s status=%s"
468 " request=<%s> base=<%s> url=<%s> [parent %p, %s]\n",
469 BOOL_STR(res->network), resourceTypeToString(res->type),
470 resourceStatusToString(res->status), res->URLrequest,
471 res->URLbase, res->parsed_request,
472 res->parent, (res->parent ? res->parent->URLbase : "N/A"));
473 return;
474}
475textureTableIndexStruct_s *getTableIndex(int i);
476bool imagery_load(resource_item_t *res){
477 bool retval;
478 int textureNumber;
479 struct textureTableIndexStruct *entry; // = res->whereToPlaceData;
480 textureNumber = res->textureNumber;
481 if(res->status == ress_downloaded){
482 entry = getTableIndex(textureNumber);
483 if(entry)
484 if (texture_load_from_file(entry, res->actual_file)) {
485 entry->status = TEX_READ; /* tell the texture thread to convert data to OpenGL-format */
486 res->status = ress_loaded;
487 retval = TRUE;
488 return retval;
489 }
490 }
491 res->status = ress_not_loaded;
492 retval = FALSE;
493 return retval;
494}
495
496
500bool resource_load(resource_item_t *res)
501{
502 openned_file_t *of = NULL;
503
504 DEBUG_RES("loading resource: %s, %s\n", resourceTypeToString(res->type), resourceStatusToString(res->status));
505
506 ASSERT(res);
507
508 switch (res->status) {
509 case ress_none:
510 case ress_starts_good:
511 case ress_invalid:
512 case ress_failed:
513 ERROR_MSG("resource_load: can't load not available resource: %s\n", res->URLrequest);
514 break;
515
516
517
518 case ress_downloaded:
519 //if(1) printf("[%s]\n",res->parsed_request); //to print successfully downloaded urls
520 of = load_file(res->actual_file);
521
522 if (of) {
523 res->status = ress_loaded;
524 //res->openned_files = ml_append( (s_list_t *) res->openned_files, ml_new(of) );
525 res->openned_files = of;
526
527 /* If type is not specified by the caller try to identify it automatically */
528 if (res->media_type == resm_unknown) {
529 resource_identify_type(res);
530 }
531
532 } else {
533
534 res->status = ress_not_loaded;
535 ERROR_MSG("resource_load: can't load file: %s\n", res->actual_file);
536 }
537
538 break;
539
540 case ress_loaded:
541 ERROR_MSG("resource_load: MISTAKE: can't load already loaded resource: %s\n", res->URLrequest);
542 break;
543
544 case ress_not_loaded:
545 ERROR_MSG("resource_load: loader already failed for this resource: %s\n", res->URLrequest);
546 break;
547
548 case ress_parsed:
549 ERROR_MSG("resource_load: MISTAKE: can't load resource already parsed: %s\n", res->URLrequest);
550 break;
551
552 case ress_not_parsed:
553 ERROR_MSG("resource_load: MISTAKE: can't load resource already parsed (and failed): %s\n", res->URLrequest);
554 break;
555 }
556
557 return (of != NULL);
558}
559
563void resource_identify_type(resource_item_t *res)
564{
565 char *test_it = NULL;
566 int test_it_len = 0;
567
568 //s_list_t *l;
569 openned_file_t *of;
570 int t;
571
572 if (res->media_type != resm_unknown)
573 /* caller specified type, or we already identified it */
574 return;
575
576 switch (res->status) {
577 case ress_loaded:
578 switch (res->type) {
579 case rest_invalid:
580 ERROR_MSG("can't identify type for invalid resource: %s\n", res->URLrequest);
581 return;
582 break;
583 case rest_string:
584 test_it = (char*)res->URLrequest;
585 ConsoleMessage ("test_it is :%s:",test_it);
586 test_it_len = (int)strlen(res->URLrequest);
587 break;
588 case rest_url:
589 case rest_file:
590 case rest_multi:
591 //l = (s_list_t *) res->openned_files;
592 //if (!l) {
593 // /* error */
594 // return;
595 //}
596 //
597 //of = ml_elem(l);
598 of = res->openned_files;
599 if (!of) {
600 /* error */
601 return;
602 }
603 /* maybe .x3z (.zip) archive? */
604 {
605 char *sourcename = (char *)of->fileFileName;
606 if(res->type == rest_url) sourcename = res->URLrequest;
607 if(!strcmp(&sourcename[strlen(sourcename)-4],".x3z")){
608 res->media_type = resm_x3z;
609 return;
610 }
611 }
612 /* might this be a gzipped input file? */
613 possiblyUnzip(of);
614 test_it = of->fileData;
615 test_it_len = of->fileDataSize;
616 break;
617 }
618
619
620 /* Test it */
621 t = determineFileType(test_it,test_it_len);
622 switch (t) {
623 case IS_TYPE_VRML:
624 case IS_TYPE_VRML1:
625
626#if defined (INCLUDE_STL_FILES)
627 case IS_TYPE_BINARY_STL: case IS_TYPE_ASCII_STL:
628#endif //INCLUDE_STL_FILES
629
630
631 res->media_type = resm_vrml;
632 break;
633
634#if defined (INCLUDE_NON_WEB3D_FORMATS)
635 case IS_TYPE_COLLADA:
636 case IS_TYPE_KML:
637 case IS_TYPE_SKETCHUP:
638#endif //INCLUDE_NON_WEB3D_FORMATS
639
640 case IS_TYPE_XML_X3D:
641 res->media_type = resm_x3d;
642 break;
643 }
644 break;
645 default:
646 break;
647 }
648 return;
649}
650
651
655void remove_file_or_folder(const char *path);
656
657void resource_remove_cached_file(s_list_t *cfe)
658{
659 const char *cached_file;
660 cached_file = (const char *) cfe->elem;
661 ASSERT(cached_file);
662 /* TODO: reference counter on cached files... */
663 remove_file_or_folder(cached_file);
664 //UNLINK(cached_file);
665}
666
672void _resourceFreeCallback(void *resource);
673
674void resource_destroy(resource_item_t *res)
675{
676 s_list_t *cf; // *of,
677
678 if(!res) return;
679 DEBUG_RES("destroying resource: %d, %d\n", res->type, res->status);
680
681 ASSERT(res);
682
683 switch (res->type) {
684 case rest_invalid:
685 /* nothing to do */
686 break;
687 case rest_url:
688 switch (res->status) {
689 case ress_none:
690 case ress_starts_good:
691 case ress_invalid:
692 /* nothing to do */
693 break;
694
695 case ress_downloaded:
696 case ress_failed:
697 case ress_loaded:
698 case ress_not_loaded:
699 case ress_parsed:
700 case ress_not_parsed:
701 if(0){
702 /* Remove openned file ? */
703 //of = (s_list_t *) res->openned_files;
704 //of = res->openned_files;
705 //if (of) {
706 // /* close any openned file */
707 // close( ((openned_file_t*)of->elem)->fileDescriptor );
708 //}
709
710 /* Remove cached file ? */
711 cf = (s_list_t *) res->cached_files;
712 if (cf) {
713 /* remove any cached file:
714 TODO: reference counter on cached files...
715 */
716 ml_foreach(cf, resource_remove_cached_file(__l));
717 }
718 }
719 /* free the actual file */
720 FREE_IF_NZ(res->actual_file);
721 break;
722 }
723
724 /* free the parsed_request url */
725 FREE_IF_NZ(res->parsed_request);
726 break;
727
728 case rest_file:
729 switch (res->status) {
730 case ress_none:
731 case ress_starts_good:
732 case ress_invalid:
733 /* nothing to do */
734 break;
735
736 case ress_downloaded:
737 case ress_failed:
738 case ress_loaded:
739 case ress_not_loaded:
740 case ress_parsed:
741 case ress_not_parsed:
742 /* Remove openned file ? */
743 //of = (s_list_t *) res->openned_files;
744 //if (of) {
745 // /* close any openned file */
746 //}
747
748 /* free the actual file */
749 FREE(res->actual_file);
750 break;
751 }
752
753 /* free the parsed_request url */
754 FREE_IF_NZ(res->parsed_request);
755 break;
756
757 case rest_string:
758 /* Nothing to do */
759 break;
760
761 case rest_multi:
762 /* JAS Apr 2017 - entry was not handled in case; do nothing? */
763 break;
764 }
765
766 /* Free the list */
767 ml_delete_all2(res->m_request, (void (*)(void *))ml_free);
768 res->m_request = NULL;
769
770 FREE_IF_NZ(res->URLbase);
771 FREE_IF_NZ(res->afterPoundCharacters);
772 FREE_IF_NZ(res->openned_files);
773 //if (!res->parent) {
774 // /* Remove base */
775 // FREE_IF_NZ(res->URLbase);
776 //} else {
777 // /* We used parent's base, so remove us from parent's childs */
778 // //resource_remove_child(res->parent, res);
779 //}
780
781 FREE_IF_NZ(res->URLrequest);
782 FREE_IF_NZ(res);
783}
784
785void resource_unlink_cachedfiles(resource_item_t *res)
786{
787 s_list_t *cf;
788
789 if(!res) return;
790 DEBUG_RES("destroying resource: %d, %d\n", res->type, res->status);
791
792 ASSERT(res);
793
794 /* Remove cached file ? */
795 cf = (s_list_t *) res->cached_files;
796 if (cf) {
797 /* remove any cached file:
798 TODO: reference counter on cached files...
799 */
800 ml_foreach(cf, resource_remove_cached_file(__l));
801 }
802
803}
804
805void resource_close_files(resource_item_t *res)
806{
807
808 if(!res) return;
809 DEBUG_RES("closing resource file: %d, %d\n", res->type, res->status);
810
811 ASSERT(res);
812
813 /* Remove openned file ? */
814
815}
816
817
821void resource_remove_child(resource_item_t *parent, resource_item_t *child)
822{
823 s_list_t *cf;
824
825 ASSERT(parent);
826 ASSERT(child);
827
828 //cf = ml_find_elem(parent->cached_files, child);
829 cf = ml_find_elem(parent->children, child);
830 if (cf) {
831 //ml_delete(parent->cached_files, cf);
832 ml_delete(parent->children, cf);
833 }
834}
835
839void destroy_root_res()
840{
841 resource_destroy((resource_item_t*)gglobal()->resources.root_res);
842 gglobal()->resources.root_res = NULL;
843}
844
845void resource_tree_destroy()
846{
847 resource_item_t* root;
848 root = (resource_item_t*)gglobal()->resources.root_res;
849 if(root){
850 ml_foreach(root->children,resource_close_files((resource_item_t*)ml_elem(__l)));
851 ml_foreach(root->children,resource_unlink_cachedfiles((resource_item_t*)ml_elem(__l)));
852 ml_foreach(root->children,resource_destroy((resource_item_t*)ml_elem(__l)));
853 ml_foreach(root->children,resource_remove_child(root,(resource_item_t*)ml_elem(__l)));
854 ml_foreach(root->children,ml_free(__l));
855 resource_close_files(root);
856 resource_unlink_cachedfiles(root);
857 destroy_root_res();
858 }
859
860}
864void resource_dump(resource_item_t *res)
865{
866 s_list_t *cf;
867 //openned_file_t *of;
868 //s_list_t *of;
869 void *ofv;
870
871 PRINTF ("resource_dump: %p\n"
872 "request: %s\n"
873 "parsed request: %s\n"
874 "actual file: %s\n"
875 "cached files: ",
876 res, res->URLrequest, res->parsed_request, res->actual_file);
877
878 cf = (s_list_t *) res->cached_files;
879 if (cf) {
880 ml_foreach(cf, PRINTF("%s ", (char *) ml_elem(__l)));
881 } else {
882 PRINTF("none");
883 }
884 PRINTF("\nopenned files: ");
885
886 //of = (s_list_t *) res->openned_files;
887 ofv = res->openned_files;
888 if (ofv) {
889 openned_file_t *of = (openned_file_t*)ofv;
890 PRINTF("%s ", of->fileFileName);
891 } else {
892 PRINTF("none");
893 }
894 PRINTF("\n");
895}
896void splitpath_local_suffix(const char *url, char **local_name, char **suff);
900void fwl_resource_push_single_request(const char *request)
901{
902 resource_item_t *res;
903
904 if (!request)
905 return;
906
907 res = resource_create_single(request);
908 //send_resource_to_parser(res);
909 resitem_enqueue(ml_new(res));
910 if(request){
911 //update information about the scene for scripting (Q. what about window title?)
912 //not sure this is a good place, in part because the calling thread may not be in a gglobal thread
913 ttglobal tg = gglobal();
914 char* suff = NULL;
915 char* local_name = NULL;
916 splitpath_local_suffix(request, &local_name, &suff);
917 tg->Mainloop.scene_name = local_name;
918 tg->Mainloop.scene_suff = suff;
919 }
920
921}
922
926void resource_push_multi_request(struct Multi_String *request)
927{
928 resource_item_t *res;
929
930 if (!request)
931 return;
932
933 res = resource_create_multi(request);
934 resitem_enqueue(ml_new(res));
935 //send_resource_to_parser(res);
936}
937
938
943void resource_tree_dump(int level, resource_item_t *root)
944{
945#define spacer for (lc=0; lc<level; lc++) printf ("\t");
946
947 s_list_t *children;
948 int lc;
949
950 if (root == NULL) return;
951 if (level == 0) printf("\nResource tree:\n\n");
952 else printf("\n");
953
954 spacer printf("==> request:\t %s\n\n", root->URLrequest);
955 spacer printf("this:\t %p\n", root);
956 spacer printf("parent:\t %p\n", root->parent);
957 spacer printf("network:\t %s\n", BOOL_STR(root->network));
958 spacer printf("new_root:\t %s\n", BOOL_STR(root->new_root));
959 spacer printf("type:\t %u\n", root->type);
960 spacer printf("status:\t %u\n", root->status);
961 spacer printf("complete:\t %s\n", BOOL_STR(root->complete));
962 spacer printf("where:\t %p\n", root->whereToPlaceData);
963 spacer printf("offsetFromWhere:\t %d\n", root->offsetFromWhereToPlaceData);
964 spacer printf("m_request:\t %p\n", root->m_request);
965 spacer printf("base:\t %s\n", root->URLbase);
966 spacer printf("temp_dir:\t %s\n", root->temp_dir);
967 spacer printf("parsed_request:\t %s\n", root->parsed_request);
968 spacer printf("actual_file:\t %s\n", root->actual_file);
969 spacer printf("cached_files:\t %p\n", root->cached_files);
970 //if (root->openned_files) {
971 // spacer printf("openned_files:\t "); ml_foreach(root->openned_files, of_dump((openned_file_t *)ml_elem(__l)));
972 //} else {
973 // spacer printf("openned_files:\t <empty>\n");
974 //}
975 spacer printf("four_first_bytes:\t %c %c %c %c\n", root->four_first_bytes[0], root->four_first_bytes[1], root->four_first_bytes[2], root->four_first_bytes[3]);
976 spacer printf("media_type:\t %u\n", root->media_type);
977
978 children = root->children;
979
980 ml_foreach(children, resource_tree_dump(level + 1, ml_elem(__l)));
981
982 printf("\n");
983}
984
985void resource_tree_count_files(int *count, resource_item_t *root)
986{
987 if (root == NULL) return;
988 (*count)++;
989 ml_foreach(root->children, resource_tree_count_files(count, ml_elem(__l)));
990}
991void printStatsResources()
992{
993 int count = 0;
994 resource_tree_count_files(&count, gglobal()->resources.root_res);
995 ConsoleMessage("%25s %d\n","resource file count", count);
996}
997
1002void resource_tree_list_files(int level, resource_item_t *root)
1003{
1004#define spacer for (lc=0; lc<level; lc++) printf ("\t");
1005 int lc;
1006
1007 if (root == NULL) return;
1008 if (level == 0) printf("\nResource file list:\n");
1009
1010 spacer printf("%s\n", root->actual_file);
1011 ml_foreach(root->children, resource_tree_list_files(-1, ml_elem(__l)));
1012}
1013
1014char *resourceTypeToString(int type) {
1015 switch (type) {
1016 case rest_invalid: return "rest_invalid";
1017 case rest_url: return "rest_url";
1018 case rest_file: return "rest_file";
1019 case rest_multi: return "rest_multi";
1020 case rest_string : return "rest_string ";
1021 default: return "resource OUT OF RANGE";
1022 }
1023}
1024
1025
1026char *resourceStatusToString(int status) {
1027 switch (status) {
1028 case ress_none: return "ress_none";
1029 case ress_starts_good: return "ress_starts_good";
1030 case ress_invalid: return "ress_invalid";
1031 case ress_downloaded: return "ress_downloaded";
1032 case ress_failed: return "ress_failed";
1033 case ress_loaded: return "ress_loaded";
1034 case ress_not_loaded: return "ress_not_loaded";
1035 case ress_parsed: return "ress_parsed";
1036 case ress_not_parsed: return "ress_not_parsed";
1037 default: return "resource OUT OF RANGE";
1038 }
1039}
1040
1041char *resourceMediaTypeToString (int mt) {
1042 switch (mt) {
1043 case resm_unknown: return " resm_unknown";
1044 case resm_vrml: return " resm_vrml";
1045 case resm_x3d: return " resm_x3d";
1046 case resm_image: return " resm_image";
1047 case resm_movie: return " resm_movie";
1048 case resm_pshader: return " resm_pshader";
1049 case resm_fshader: return " resm_fshader";
1050 case resm_x3z: return " resm_x3z";
1051 default: return "resource OUT OF RANGE";
1052 }
1053}
1054
1055
1056
1057#define SLASHDOTDOTSLASH "/../"
1058#if defined(_MSC_VER) || defined(_ANDROID) || defined(ANDROIDNDK)
1059#define rindex strrchr
1060#endif
1061void removeFilenameFromPath (char *path) {
1062 char *slashindex;
1063 char *slashDotDotSlash;
1064
1065 /* and strip off the file name from the current path, leaving any path */
1066 slashindex = (char *) rindex(path, ((int) '/'));
1067 if (slashindex != NULL) {
1068 /* slashindex ++; */ /* <msvc DO NOT> leave the slash there */
1069 *slashindex = 0;
1070 } else {path[0] = 0;}
1071 /* printf ("removeFielnameFromPath, parenturl is %s\n",path); */
1072
1073 /* are there any "/../" bits in the path? if so, lets clean them up */
1074 slashDotDotSlash = strstr(path, SLASHDOTDOTSLASH);
1075 while (slashDotDotSlash != NULL) {
1076 char tmpline[2000];
1077 /* might have something like: _levels_plus/tiles/0/../1/../1/../2/../ */
1078 /* find the preceeding slash: */
1079 *slashDotDotSlash = '\0';
1080 /* printf ("have slashdotdot, path now :%s:\n",path); */
1081
1082 slashindex = (char *)rindex(path, ((int) '/'));
1083 if (slashindex != NULL) {
1084
1085 slashindex ++;
1086 *slashindex = '\0';
1087 slashDotDotSlash += strlen(SLASHDOTDOTSLASH);
1088 strcpy(tmpline,path);
1089 /* printf ("tmpline step 1 is :%s:\n",tmpline); */
1090 strcat (tmpline, slashDotDotSlash);
1091 /* printf ("tmpline step 2 is :%s:\n",tmpline); */
1092 strcpy (path, tmpline);
1093 slashDotDotSlash = strstr(path, SLASHDOTDOTSLASH);
1094 /* printf ("end of loop, path :%s: slashdot %u\n",path,slashDotDotSlash); */
1095
1096
1097 }
1098 }
1099}
1100
1101
1102/* is this a gzipped file? if so, unzip the text and replace the original with this. */
1103static void possiblyUnzip (openned_file_t *of) {
1104#if !(defined(IPHONE) || defined(_ANDROID))
1105 if (of->fileData == NULL) return;
1106 if (of->fileData[0] == '\0') return;
1107 if (of->fileData[1] == '\0') return;
1108 if (((unsigned char) of->fileData[0] == 0x1f) && ((unsigned char) of->fileData[1] == 0x8b)) {
1109 #define GZIP_BUFF_SIZE 2048
1110
1111 gzFile source;
1112 FILE *dest;
1113 char buffer[GZIP_BUFF_SIZE];
1114 int num_read = 0;
1115 openned_file_t *newFile;
1116
1117 char *tempname; // [1000];
1118
1119 /* make a temporary name for the gunzipped file */
1120 // sprintf (tempname, "%s",tempnam(gglobal()->Mainloop.tmpFileLocation,"freewrl_tmp"));
1121 tempname = TEMPNAM(gglobal()->Mainloop.tmpFileLocation, "freewrl_tmp");
1122
1123 /* read in the text, unzip it, write it out again */
1124 source = gzopen(of->fileFileName,"rb");
1125 dest = fopen(tempname,"wb");
1126
1127 if (!source || !source) {
1128 ConsoleMessage ("unable to unzip this file: %s\n",of->fileFileName);
1129 printf ("wow - problem\n");
1130 }
1131
1132 while ((num_read = gzread(source, buffer, GZIP_BUFF_SIZE)) > 0) {
1133 fwrite(buffer, 1, num_read, dest);
1134 }
1135
1136 gzclose(source);
1137 fclose(dest);
1138
1139 /* read in the unzipped text... */
1140 newFile = load_file((const char *) tempname);
1141 UNLINK(tempname);
1142
1143 if (newFile->fileData == NULL) {
1144 ConsoleMessage ("problem re-reading gunzipped text file");
1145 return;
1146 }
1147
1148 /* replace the old text with the unzipped; and clean up */
1149 FREE_IF_NZ(of->fileData);
1150 of->fileData = newFile->fileData;
1151/* seems odd that we wouldn't need to also update the fileDataSize, like so:
1152 of->fileDataSize = newFile->fileDataSize; */
1153 FREE_IF_NZ(newFile);
1154 unlink (tempname);
1155 }
1156#endif
1157}
1158
1159bool resource_is_root_loaded()
1160{
1161 return ((gglobal()->resources.root_res != NULL) && (((resource_item_t*)gglobal()->resources.root_res)->status == ress_parsed));
1162}
1163
1170/* keep the last base resource around, for times when we are making nodes during runtime, eg
1171 textures in Background nodes */
1172
1173
1174void pushInputResource(resource_item_t *url)
1175{
1176 presources p = gglobal()->resources.prv;
1177 DEBUG_MSG("pushInputResource current Resource is %s", url->parsed_request);
1178 //printf("pushInputResource %s\n", url->parsed_request);
1179
1180
1181
1182 /* push this one */
1183 if (p->resStack==NULL) {
1184 p->resStack = newStack (resource_item_t *);
1185 }
1186
1187 /* is this an EAI/SAI request? If not, we don't push this one on the stack */
1188 /*
1189 if (url->parsed_request != NULL)
1190 if (strncmp(url->parsed_request,EAI_Flag,strlen(EAI_Flag)) == 0) {
1191 DEBUG_MSG("pushInputResource, from EAI, ignoring");
1192 return;
1193 }
1194*/
1195 stack_push (resource_item_t*, p->resStack, url);
1196 DEBUG_MSG("pushInputResource, after push, stack size %d",vectorSize(p->resStack));
1197}
1198
1199void popInputResource() {
1200 resource_item_t *cwu;
1201 presources p = gglobal()->resources.prv;
1202
1203 /* lets just keep this one around, to see if it is really the bottom of the stack */
1204 DEBUG_MSG("popInputResource, stack size %d",vectorSize(p->resStack));
1205 //printf("popInputResource, stack size %d\n",vectorSize(p->resStack));
1206
1207 cwu = stack_top(resource_item_t *, p->resStack);
1208
1209 /* pop the stack, and if we are at "nothing" keep the pointer to the last resource */
1210 stack_pop((resource_item_t *), p->resStack);
1211
1212 if (stack_empty(p->resStack)) {
1213 DEBUG_MSG ("popInputResource, stack now empty and we have saved the last resource\n");
1214 p->lastBaseResource = cwu;
1215 } else {
1216 cwu = stack_top(resource_item_t *, p->resStack);
1217 DEBUG_MSG("popInputResource, cwu = %p",cwu);
1218 DEBUG_MSG("popInputResource before pop, current Resource is %s\n", cwu->parsed_request);
1219 }
1220}
1221
1222resource_item_t *getInputResource()
1223{
1224 resource_item_t *cwu;
1225 presources p = gglobal()->resources.prv;
1226
1227
1228 DEBUG_MSG("getInputResource \n");
1229 if (p->resStack==NULL) {
1230 DEBUG_MSG("getInputResource, stack NULL\n");
1231 return NULL;
1232 }
1233
1234 /* maybe we are running, and are, say, making up background textures at runtime? */
1235 if (stack_empty(p->resStack)) {
1236 if (p->lastBaseResource == NULL) {
1237 ConsoleMessage ("stacking error - looking for input resource, but it is null");
1238 } else {
1239 DEBUG_MSG("so, returning %s\n",p->lastBaseResource->parsed_request);
1240 }
1241 //printf("getLastResource %s\n",p->lastBaseResource->parsed_request);
1242 return p->lastBaseResource;
1243 }
1244
1245
1246 cwu = stack_top(resource_item_t *, p->resStack);
1247 DEBUG_MSG("getInputResource current Resource is %lu %lx %s\n", (unsigned long int) cwu, (unsigned long int) cwu, cwu->parsed_request);
1248 //printf("getCurrentResource %s\n",cwu->parsed_request);
1249 return cwu;
1250}
1251
1252//used by FEGF configs in frontend
1253char* fwl_resitem_getURL(void *resp){
1254 resource_item_t *res = (resource_item_t *)resp;
1255 return res->parsed_request;
1256}
1257void fwl_resitem_setActualFile(void *resp, char *fname){
1258 resource_item_t *res = (resource_item_t *)resp;
1259 res->actual_file = STRDUP(fname);
1260 if(strcmp(res->actual_file,res->parsed_request)){
1261 //it's a temp file
1262 s_list_t *item;
1263 item = ml_new(res->actual_file);
1264 if (!res->cached_files)
1265 res->cached_files = (void *)item;
1266 else
1267 res->cached_files = ml_append(res->cached_files,item);
1268 }
1269}
1270char* fwl_resitem_getTempDir(void *resp){
1271 resource_item_t *res = (resource_item_t *)resp;
1272 return res->temp_dir;
1273}
1274void fwl_resitem_enqueuNextMulti(void *resp){
1275 resource_item_t *res = (resource_item_t *)resp;
1276 int more_multi = (res->status == ress_failed) && (res->m_request != NULL);
1277 if(more_multi){
1278 //still some hope via multi_string url, perhaps next one
1279 res->status = ress_invalid; //downgrade ress_fail to ress_invalid
1280 res->type = rest_multi; //should already be flagged
1281 //must consult BE to convert relativeURL to absoluteURL via baseURL
1282 //(or could we absolutize in a batch in resource_create_multi0()?)
1283 resource_identify(res->parent, res); //should increment multi pointer/iterator
1284 frontenditem_enqueue(ml_new(res));
1285 }
1286}
1287char *strBackslash2fore(char *);
1288//int file2blob(resource_item_t *res);
1289void fwl_resitem_setLocalPath(void *resp, char* path){
1290 int delete_after_load;
1291 resource_item_t *res = (resource_item_t *)resp;
1292 res->status = ress_downloaded;
1293 res->actual_file = strBackslash2fore(STRDUP(path));
1294 delete_after_load = 1;
1295 if (delete_after_load){
1296 //warning this will delete the actual_file setLocalPath is for downloaded/copied/cached files only,
1297 //not direct intranet files as with desktop.c
1298 s_list_t *item;
1299 item = ml_new(res->actual_file);
1300 if (!res->cached_files)
1301 res->cached_files = (void *)item;
1302 else
1303 res->cached_files = ml_append(res->cached_files, item);
1304 }
1305 res->_loadFunc = (void *)file2blob; //msvc can also do &file2blob
1306}
1307int fwl_resitem_getStatus(void *resp){
1308 resource_item_t *res = (resource_item_t *)resp;
1309 return res->status;
1310}
1311void fwl_resitem_setStatus(void *resp, int status) {
1312 resource_item_t *res = (resource_item_t *)resp;
1313 res->status = status;
1314}
1315
1316int fwl_resitem_getType(void *resp){
1317 resource_item_t *res = (resource_item_t *)resp;
1318 return res->type;
1319}
1320int fwl_resitem_getMediaType(void *resp){
1321 resource_item_t *res = (resource_item_t *)resp;
1322 return res->media_type;
1323}
1324void fwl_resitem_setDownloadThread(void *resp, void *thread){
1325 resource_item_t *res = (resource_item_t *)resp;
1326 res->_loadThread = (pthread_t*)thread;
1327}
1328void * fwl_resitem_getDownloadThread(void *resp){
1329 resource_item_t *res = (resource_item_t *)resp;
1330 return res->_loadThread;
1331}
1332void * fwl_resitem_getGlobal(void *resp){
1333 resource_item_t *res = (resource_item_t *)resp;
1334 return res->tg;
1335}