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
112
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
499bool resource_load(resource_item_t *res)
500{
501 openned_file_t *of = NULL;
502//#define ERROR_MSG printf
503//#define DEBUG_MSG printf
504
505 DEBUG_RES("loading resource: %s, %s\n", resourceTypeToString(res->type), resourceStatusToString(res->status));
506
507 ASSERT(res);
508
509 switch (res->status) {
510 case ress_none:
511 case ress_starts_good:
512 case ress_invalid:
513 case ress_failed:
514 ERROR_MSG("resource_load: can't load not available resource: %s\n", res->URLrequest);
515 break;
516
517
518
519 case ress_downloaded:
520 //if(1) printf("[%s]\n",res->parsed_request); //to print successfully downloaded urls
521 of = load_file(res->actual_file);
522
523 if (of) {
524 res->status = ress_loaded;
525 //res->openned_files = ml_append( (s_list_t *) res->openned_files, ml_new(of) );
526 res->openned_files = of;
527
528 /* If type is not specified by the caller try to identify it automatically */
529 if (res->media_type == resm_unknown) {
530 resource_identify_type(res);
531 }
532
533 } else {
534
535 res->status = ress_not_loaded;
536 ERROR_MSG("resource_load: can't load file: %s\n", res->actual_file);
537 }
538
539 break;
540
541 case ress_loaded:
542 ERROR_MSG("resource_load: MISTAKE: can't load already loaded resource: %s\n", res->URLrequest);
543 break;
544
545 case ress_not_loaded:
546 ERROR_MSG("resource_load: loader already failed for this resource: %s\n", res->URLrequest);
547 break;
548
549 case ress_parsed:
550 ERROR_MSG("resource_load: MISTAKE: can't load resource already parsed: %s\n", res->URLrequest);
551 break;
552
553 case ress_not_parsed:
554 ERROR_MSG("resource_load: MISTAKE: can't load resource already parsed (and failed): %s\n", res->URLrequest);
555 break;
556 }
557
558 return (of != NULL);
559}
560#ifdef _MSC_VER
561#define strcasecmp stricmp
562#endif //_MSC_VER
563#
567void resource_identify_type(resource_item_t *res)
568{
569 char *test_it = NULL;
570 int test_it_len = 0;
571
572 //s_list_t *l;
573 openned_file_t *of;
574 int t;
575
576 if (res->media_type != resm_unknown)
577 /* caller specified type, or we already identified it */
578 return;
579
580 switch (res->status) {
581 case ress_loaded:
582 switch (res->type) {
583 case rest_invalid:
584 ERROR_MSG("can't identify type for invalid resource: %s\n", res->URLrequest);
585 return;
586 break;
587 case rest_string:
588 test_it = (char*)res->URLrequest;
589 ConsoleMessage ("test_it is :%s:",test_it);
590 test_it_len = (int)strlen(res->URLrequest);
591 break;
592 case rest_url:
593 case rest_file:
594 case rest_multi:
595 //l = (s_list_t *) res->openned_files;
596 //if (!l) {
597 // /* error */
598 // return;
599 //}
600 //
601 //of = ml_elem(l);
602 of = res->openned_files;
603 if (!of) {
604 /* error */
605 return;
606 }
607 /* maybe .x3z (.zip) archive? */
608 {
609 char *sourcename = (char *)of->fileFileName;
610 if(res->type == rest_url) sourcename = res->URLrequest;
611 if(!strcmp(&sourcename[strlen(sourcename)-4],".x3z")){
612 res->media_type = resm_x3z;
613 return;
614 }
615 //knronos glTF and derivitives
616 if(!strcasecmp(&sourcename[strlen(sourcename)-5],".glTF")){
617 // IANA MIME model/gltf-json
618 res->media_type = resm_gltf;
619 return;
620 }
621 if(!strcasecmp(&sourcename[strlen(sourcename)-4],".glb")){
622 // IANA MIME model/gltf-binary
623 res->media_type = resm_glb;
624 return;
625 }
626 if(!strcasecmp(&sourcename[strlen(sourcename)-4],".bin")){
627 // MIME application.octet-stream
628 res->media_type = resm_bin;
629 return;
630 }
631 // cesium (geo) derivitives from gltf - first 4 bytes of content == their suffix
632 // tileset .json application/json
633 // tilesetStyle .json application/json
634 if(!strcasecmp(&sourcename[strlen(sourcename)-4],".json")){
635 res->media_type = resm_json;
636 return;
637 }
638 if(!strcasecmp(&sourcename[strlen(sourcename)-4],".b3dm")){
639 res->media_type = resm_b3dm;
640 return;
641 }
642 if(!strcasecmp(&sourcename[strlen(sourcename)-4],".i3dm")){
643 res->media_type = resm_i3dm;
644 return;
645 }
646 if(!strcasecmp(&sourcename[strlen(sourcename)-4],".pnts")){
647 res->media_type = resm_pnts;
648 return;
649 }
650 if(!strcasecmp(&sourcename[strlen(sourcename)-4],".cmpt")){
651 res->media_type = resm_cmpt;
652 return;
653 }
654 }
655 /* might this be a gzipped input file? */
656 possiblyUnzip(of);
657 test_it = of->fileData;
658 test_it_len = of->fileDataSize;
659 break;
660 }
661
662
663 /* Test it */
664 // gltf?
665 if(memcmp(test_it,"glTF",4)){
666 //.glb
667 res->media_type = resm_glb;
668 }
669 t = determineFileType(test_it,test_it_len);
670 switch (t) {
671 case IS_TYPE_VRML:
672 case IS_TYPE_VRML1:
673
674#if defined (INCLUDE_STL_FILES)
675 case IS_TYPE_BINARY_STL: case IS_TYPE_ASCII_STL:
676#endif //INCLUDE_STL_FILES
677
678
679 res->media_type = resm_vrml;
680 break;
681
682#if defined (INCLUDE_NON_WEB3D_FORMATS)
683 case IS_TYPE_COLLADA:
684 case IS_TYPE_KML:
685 case IS_TYPE_SKETCHUP:
686#endif //INCLUDE_NON_WEB3D_FORMATS
687
688 case IS_TYPE_XML_X3D:
689 res->media_type = resm_x3d;
690 break;
691 }
692 break;
693 default:
694 break;
695 }
696 return;
697}
698
699
703void remove_file_or_folder(const char *path);
704
705void resource_remove_cached_file(s_list_t *cfe)
706{
707 const char *cached_file;
708 cached_file = (const char *) cfe->elem;
709 ASSERT(cached_file);
710 /* TODO: reference counter on cached files... */
711 remove_file_or_folder(cached_file);
712 //UNLINK(cached_file);
713}
714
719
720void _resourceFreeCallback(void *resource);
721
722void resource_destroy(resource_item_t *res)
723{
724 s_list_t *cf; // *of,
725
726 if(!res) return;
727 DEBUG_RES("destroying resource: %d, %d\n", res->type, res->status);
728
729 ASSERT(res);
730
731 switch (res->type) {
732 case rest_invalid:
733 /* nothing to do */
734 break;
735 case rest_url:
736 switch (res->status) {
737 case ress_none:
738 case ress_starts_good:
739 case ress_invalid:
740 /* nothing to do */
741 break;
742
743 case ress_downloaded:
744 case ress_failed:
745 case ress_loaded:
746 case ress_not_loaded:
747 case ress_parsed:
748 case ress_not_parsed:
749 if(0){
750 /* Remove openned file ? */
751 //of = (s_list_t *) res->openned_files;
752 //of = res->openned_files;
753 //if (of) {
754 // /* close any openned file */
755 // close( ((openned_file_t*)of->elem)->fileDescriptor );
756 //}
757
758 /* Remove cached file ? */
759 cf = (s_list_t *) res->cached_files;
760 if (cf) {
761 /* remove any cached file:
762 TODO: reference counter on cached files...
763 */
764 ml_foreach(cf, resource_remove_cached_file(__l));
765 }
766 }
767 /* free the actual file */
768 FREE_IF_NZ(res->actual_file);
769 break;
770 }
771
772 /* free the parsed_request url */
773 FREE_IF_NZ(res->parsed_request);
774 break;
775
776 case rest_file:
777 switch (res->status) {
778 case ress_none:
779 case ress_starts_good:
780 case ress_invalid:
781 /* nothing to do */
782 break;
783
784 case ress_downloaded:
785 case ress_failed:
786 case ress_loaded:
787 case ress_not_loaded:
788 case ress_parsed:
789 case ress_not_parsed:
790 /* Remove openned file ? */
791 //of = (s_list_t *) res->openned_files;
792 //if (of) {
793 // /* close any openned file */
794 //}
795
796 /* free the actual file */
797 FREE(res->actual_file);
798 break;
799 }
800
801 /* free the parsed_request url */
802 FREE_IF_NZ(res->parsed_request);
803 break;
804
805 case rest_string:
806 /* Nothing to do */
807 break;
808
809 case rest_multi:
810 /* JAS Apr 2017 - entry was not handled in case; do nothing? */
811 break;
812 }
813
814 /* Free the list */
815 ml_delete_all2(res->m_request, (void (*)(void *))ml_free);
816 res->m_request = NULL;
817
818 FREE_IF_NZ(res->URLbase);
819 FREE_IF_NZ(res->afterPoundCharacters);
820 FREE_IF_NZ(res->openned_files);
821 //if (!res->parent) {
822 // /* Remove base */
823 // FREE_IF_NZ(res->URLbase);
824 //} else {
825 // /* We used parent's base, so remove us from parent's childs */
826 // //resource_remove_child(res->parent, res);
827 //}
828
829 FREE_IF_NZ(res->URLrequest);
830 FREE_IF_NZ(res);
831}
832
833void resource_unlink_cachedfiles(resource_item_t *res)
834{
835 s_list_t *cf;
836
837 if(!res) return;
838 DEBUG_RES("destroying resource: %d, %d\n", res->type, res->status);
839
840 ASSERT(res);
841
842 /* Remove cached file ? */
843 cf = (s_list_t *) res->cached_files;
844 if (cf) {
845 /* remove any cached file:
846 TODO: reference counter on cached files...
847 */
848 ml_foreach(cf, resource_remove_cached_file(__l));
849 }
850
851}
852
853void resource_close_files(resource_item_t *res)
854{
855
856 if(!res) return;
857 DEBUG_RES("closing resource file: %d, %d\n", res->type, res->status);
858
859 ASSERT(res);
860
861 /* Remove openned file ? */
862
863}
864
865
869void resource_remove_child(resource_item_t *parent, resource_item_t *child)
870{
871 s_list_t *cf;
872
873 ASSERT(parent);
874 ASSERT(child);
875
876 //cf = ml_find_elem(parent->cached_files, child);
877 cf = ml_find_elem(parent->children, child);
878 if (cf) {
879 //ml_delete(parent->cached_files, cf);
880 ml_delete(parent->children, cf);
881 }
882}
883
887void destroy_root_res()
888{
889 resource_destroy((resource_item_t*)gglobal()->resources.root_res);
890 gglobal()->resources.root_res = NULL;
891}
892
893void resource_tree_destroy()
894{
895 resource_item_t* root;
896 root = (resource_item_t*)gglobal()->resources.root_res;
897 if(root){
898 ml_foreach(root->children,resource_close_files((resource_item_t*)ml_elem(__l)));
899 ml_foreach(root->children,resource_unlink_cachedfiles((resource_item_t*)ml_elem(__l)));
900 ml_foreach(root->children,resource_destroy((resource_item_t*)ml_elem(__l)));
901 ml_foreach(root->children,resource_remove_child(root,(resource_item_t*)ml_elem(__l)));
902 ml_foreach(root->children,ml_free(__l));
903 resource_close_files(root);
904 resource_unlink_cachedfiles(root);
905 destroy_root_res();
906 }
907
908}
912void resource_dump(resource_item_t *res)
913{
914 s_list_t *cf;
915 //openned_file_t *of;
916 //s_list_t *of;
917 void *ofv;
918
919 PRINTF ("resource_dump: %p\n"
920 "request: %s\n"
921 "parsed request: %s\n"
922 "actual file: %s\n"
923 "cached files: ",
924 res, res->URLrequest, res->parsed_request, res->actual_file);
925
926 cf = (s_list_t *) res->cached_files;
927 if (cf) {
928 ml_foreach(cf, PRINTF("%s ", (char *) ml_elem(__l)));
929 } else {
930 PRINTF("none");
931 }
932 PRINTF("\nopenned files: ");
933
934 //of = (s_list_t *) res->openned_files;
935 ofv = res->openned_files;
936 if (ofv) {
937 openned_file_t *of = (openned_file_t*)ofv;
938 PRINTF("%s ", of->fileFileName);
939 } else {
940 PRINTF("none");
941 }
942 PRINTF("\n");
943}
944void splitpath_local_suffix(const char *url, char **local_name, char **suff);
948void fwl_resource_push_single_request(const char *request)
949{
950 resource_item_t *res;
951
952 if (!request)
953 return;
954
955 res = resource_create_single(request);
956 //send_resource_to_parser(res);
957 resitem_enqueue(ml_new(res));
958 if(request){
959 //update information about the scene for scripting (Q. what about window title?)
960 //not sure this is a good place, in part because the calling thread may not be in a gglobal thread
961 ttglobal tg = gglobal();
962 char* suff = NULL;
963 char* local_name = NULL;
964 splitpath_local_suffix(request, &local_name, &suff);
965 //tg->Mainloop.url //can be http or disk file
966 //tg->Mainloop.tmpFileLocation - if downloaded, to where on disk
967 tg->Mainloop.scene_name = local_name;
968 tg->Mainloop.scene_suff = suff;
969 }
970
971}
972
976void resource_push_multi_request(struct Multi_String *request)
977{
978 resource_item_t *res;
979
980 if (!request)
981 return;
982
983 res = resource_create_multi(request);
984 resitem_enqueue(ml_new(res));
985 //send_resource_to_parser(res);
986}
987
988
993void resource_tree_dump(int level, resource_item_t *root)
994{
995#define spacer for (lc=0; lc<level; lc++) printf ("\t");
996
997 s_list_t *children;
998 int lc;
999
1000 if (root == NULL) return;
1001 if (level == 0) printf("\nResource tree:\n\n");
1002 else printf("\n");
1003
1004 spacer printf("==> request:\t %s\n\n", root->URLrequest);
1005 spacer printf("this:\t %p\n", root);
1006 spacer printf("parent:\t %p\n", root->parent);
1007 spacer printf("network:\t %s\n", BOOL_STR(root->network));
1008 spacer printf("new_root:\t %s\n", BOOL_STR(root->new_root));
1009 spacer printf("type:\t %u\n", root->type);
1010 spacer printf("status:\t %u\n", root->status);
1011 spacer printf("complete:\t %s\n", BOOL_STR(root->complete));
1012 spacer printf("where:\t %p\n", root->whereToPlaceData);
1013 spacer printf("offsetFromWhere:\t %d\n", root->offsetFromWhereToPlaceData);
1014 spacer printf("m_request:\t %p\n", root->m_request);
1015 spacer printf("base:\t %s\n", root->URLbase);
1016 spacer printf("temp_dir:\t %s\n", root->temp_dir);
1017 spacer printf("parsed_request:\t %s\n", root->parsed_request);
1018 spacer printf("actual_file:\t %s\n", root->actual_file);
1019 spacer printf("cached_files:\t %p\n", root->cached_files);
1020 //if (root->openned_files) {
1021 // spacer printf("openned_files:\t "); ml_foreach(root->openned_files, of_dump((openned_file_t *)ml_elem(__l)));
1022 //} else {
1023 // spacer printf("openned_files:\t <empty>\n");
1024 //}
1025 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]);
1026 spacer printf("media_type:\t %u\n", root->media_type);
1027
1028 children = root->children;
1029
1030 ml_foreach(children, resource_tree_dump(level + 1, ml_elem(__l)));
1031
1032 printf("\n");
1033}
1034
1035void resource_tree_count_files(int *count, resource_item_t *root)
1036{
1037 if (root == NULL) return;
1038 (*count)++;
1039 ml_foreach(root->children, resource_tree_count_files(count, ml_elem(__l)));
1040}
1041void printStatsResources()
1042{
1043 int count = 0;
1044 resource_tree_count_files(&count, gglobal()->resources.root_res);
1045 ConsoleMessage("%25s %d\n","resource file count", count);
1046}
1047
1052void resource_tree_list_files(int level, resource_item_t *root)
1053{
1054#define spacer for (lc=0; lc<level; lc++) printf ("\t");
1055 int lc;
1056
1057 if (root == NULL) return;
1058 if (level == 0) printf("\nResource file list:\n");
1059
1060 spacer printf("%s\n", root->actual_file);
1061 ml_foreach(root->children, resource_tree_list_files(-1, ml_elem(__l)));
1062}
1063
1064char *resourceTypeToString(int type) {
1065 switch (type) {
1066 case rest_invalid: return "rest_invalid";
1067 case rest_url: return "rest_url";
1068 case rest_file: return "rest_file";
1069 case rest_multi: return "rest_multi";
1070 case rest_string : return "rest_string ";
1071 default: return "resource OUT OF RANGE";
1072 }
1073}
1074
1075
1076char *resourceStatusToString(int status) {
1077 switch (status) {
1078 case ress_none: return "ress_none";
1079 case ress_starts_good: return "ress_starts_good";
1080 case ress_invalid: return "ress_invalid";
1081 case ress_downloaded: return "ress_downloaded";
1082 case ress_failed: return "ress_failed";
1083 case ress_loaded: return "ress_loaded";
1084 case ress_not_loaded: return "ress_not_loaded";
1085 case ress_parsed: return "ress_parsed";
1086 case ress_not_parsed: return "ress_not_parsed";
1087 default: return "resource OUT OF RANGE";
1088 }
1089}
1090
1091char *resourceMediaTypeToString (int mt) {
1092 switch (mt) {
1093 case resm_unknown: return " resm_unknown";
1094 case resm_vrml: return " resm_vrml";
1095 case resm_x3d: return " resm_x3d";
1096 case resm_image: return " resm_image";
1097 case resm_image_buffer: return " resm_image_buffer";
1098 case resm_movie: return " resm_movie";
1099 case resm_pshader: return " resm_pshader";
1100 case resm_fshader: return " resm_fshader";
1101 case resm_mocap: return " resm_mocap";
1102 case resm_x3z: return " resm_x3z";
1103 case resm_gltf: return " resm_gltf";
1104 case resm_glb: return " resm_glb";
1105 case resm_bin: return " resm_bin";
1106 case resm_b3dm: return " resm_b3dm";
1107 case resm_i3dm: return " resm_i3dm";
1108 case resm_pnts: return " resm_pnts";
1109 case resm_cmpt: return " resm_cmpt";
1110 case resm_midi: return " resm_midi";
1111 default: return "resource OUT OF RANGE";
1112 }
1113}
1114
1115
1116
1117#define SLASHDOTDOTSLASH "/../"
1118#if defined(_MSC_VER) || defined(_ANDROID) || defined(ANDROIDNDK)
1119#define rindex strrchr
1120#endif
1121void removeFilenameFromPath (char *path) {
1122 char *slashindex;
1123 char *slashDotDotSlash;
1124
1125 /* and strip off the file name from the current path, leaving any path */
1126 slashindex = (char *) rindex(path, ((int) '/'));
1127 if (slashindex != NULL) {
1128 /* slashindex ++; */ /* <msvc DO NOT> leave the slash there */
1129 *slashindex = 0;
1130 } else {path[0] = 0;}
1131 /* printf ("removeFielnameFromPath, parenturl is %s\n",path); */
1132
1133 /* are there any "/../" bits in the path? if so, lets clean them up */
1134 slashDotDotSlash = strstr(path, SLASHDOTDOTSLASH);
1135 while (slashDotDotSlash != NULL) {
1136 char tmpline[2000];
1137 /* might have something like: _levels_plus/tiles/0/../1/../1/../2/../ */
1138 /* find the preceeding slash: */
1139 *slashDotDotSlash = '\0';
1140 /* printf ("have slashdotdot, path now :%s:\n",path); */
1141
1142 slashindex = (char *)rindex(path, ((int) '/'));
1143 if (slashindex != NULL) {
1144
1145 slashindex ++;
1146 *slashindex = '\0';
1147 slashDotDotSlash += strlen(SLASHDOTDOTSLASH);
1148 strcpy(tmpline,path);
1149 /* printf ("tmpline step 1 is :%s:\n",tmpline); */
1150 strcat (tmpline, slashDotDotSlash);
1151 /* printf ("tmpline step 2 is :%s:\n",tmpline); */
1152 strcpy (path, tmpline);
1153 slashDotDotSlash = strstr(path, SLASHDOTDOTSLASH);
1154 /* printf ("end of loop, path :%s: slashdot %u\n",path,slashDotDotSlash); */
1155
1156
1157 }
1158 }
1159}
1160
1161
1162/* is this a gzipped file? if so, unzip the text and replace the original with this. */
1163static void possiblyUnzip (openned_file_t *of) {
1164#if !(defined(IPHONE) || defined(_ANDROID))
1165 if (of->fileData == NULL) return;
1166 if (of->fileData[0] == '\0') return;
1167 if (of->fileData[1] == '\0') return;
1168 if (((unsigned char) of->fileData[0] == 0x1f) && ((unsigned char) of->fileData[1] == 0x8b)) {
1169 #define GZIP_BUFF_SIZE 2048
1170
1171 gzFile source;
1172 FILE *dest;
1173 char buffer[GZIP_BUFF_SIZE];
1174 int num_read = 0;
1175 openned_file_t *newFile;
1176
1177 char *tempname; // [1000];
1178
1179 /* make a temporary name for the gunzipped file */
1180 // sprintf (tempname, "%s",tempnam(gglobal()->Mainloop.tmpFileLocation,"freewrl_tmp"));
1181 tempname = TEMPNAM(gglobal()->Mainloop.tmpFileLocation, "freewrl_tmp");
1182
1183 /* read in the text, unzip it, write it out again */
1184 source = gzopen(of->fileFileName,"rb");
1185 dest = fopen(tempname,"wb");
1186
1187 if (!source || !source) {
1188 ConsoleMessage ("unable to unzip this file: %s\n",of->fileFileName);
1189 printf ("wow - problem\n");
1190 }
1191
1192 while ((num_read = gzread(source, buffer, GZIP_BUFF_SIZE)) > 0) {
1193 fwrite(buffer, 1, num_read, dest);
1194 }
1195
1196 gzclose(source);
1197 fclose(dest);
1198
1199 /* read in the unzipped text... */
1200 newFile = load_file((const char *) tempname);
1201 UNLINK(tempname);
1202
1203 if (newFile->fileData == NULL) {
1204 ConsoleMessage ("problem re-reading gunzipped text file");
1205 return;
1206 }
1207
1208 /* replace the old text with the unzipped; and clean up */
1209 FREE_IF_NZ(of->fileData);
1210 of->fileData = newFile->fileData;
1211/* seems odd that we wouldn't need to also update the fileDataSize, like so:
1212 of->fileDataSize = newFile->fileDataSize; */
1213 FREE_IF_NZ(newFile);
1214 unlink (tempname);
1215 }
1216#endif
1217}
1218
1219bool resource_is_root_loaded()
1220{
1221 return ((gglobal()->resources.root_res != NULL) && (((resource_item_t*)gglobal()->resources.root_res)->status == ress_parsed));
1222}
1223
1229
1230/* keep the last base resource around, for times when we are making nodes during runtime, eg
1231 textures in Background nodes */
1232
1233
1234void pushInputResource(resource_item_t *url)
1235{
1236 presources p = gglobal()->resources.prv;
1237 DEBUG_MSG("pushInputResource current Resource is %s", url->parsed_request);
1238 //printf("pushInputResource %s\n", url->parsed_request);
1239
1240
1241
1242 /* push this one */
1243 if (p->resStack==NULL) {
1244 p->resStack = newStack (resource_item_t *);
1245 }
1246
1247 /* is this an EAI/SAI request? If not, we don't push this one on the stack */
1248 /*
1249 if (url->parsed_request != NULL)
1250 if (strncmp(url->parsed_request,EAI_Flag,strlen(EAI_Flag)) == 0) {
1251 DEBUG_MSG("pushInputResource, from EAI, ignoring");
1252 return;
1253 }
1254*/
1255 stack_push (resource_item_t*, p->resStack, url);
1256 DEBUG_MSG("pushInputResource, after push, stack size %d",vectorSize(p->resStack));
1257}
1258
1259void popInputResource() {
1260 resource_item_t *cwu;
1261 presources p = gglobal()->resources.prv;
1262
1263 /* lets just keep this one around, to see if it is really the bottom of the stack */
1264 DEBUG_MSG("popInputResource, stack size %d",vectorSize(p->resStack));
1265 //printf("popInputResource, stack size %d\n",vectorSize(p->resStack));
1266
1267 cwu = stack_top(resource_item_t *, p->resStack);
1268
1269 /* pop the stack, and if we are at "nothing" keep the pointer to the last resource */
1270 stack_pop((resource_item_t *), p->resStack);
1271
1272 if (stack_empty(p->resStack)) {
1273 DEBUG_MSG ("popInputResource, stack now empty and we have saved the last resource\n");
1274 p->lastBaseResource = cwu;
1275 } else {
1276 cwu = stack_top(resource_item_t *, p->resStack);
1277 DEBUG_MSG("popInputResource, cwu = %p",cwu);
1278 DEBUG_MSG("popInputResource before pop, current Resource is %s\n", cwu->parsed_request);
1279 }
1280}
1281
1282resource_item_t *getInputResource()
1283{
1284 resource_item_t *cwu;
1285 presources p = gglobal()->resources.prv;
1286
1287
1288 DEBUG_MSG("getInputResource \n");
1289 if (p->resStack==NULL) {
1290 DEBUG_MSG("getInputResource, stack NULL\n");
1291 return NULL;
1292 }
1293
1294 /* maybe we are running, and are, say, making up background textures at runtime? */
1295 if (stack_empty(p->resStack)) {
1296 if (p->lastBaseResource == NULL) {
1297 ConsoleMessage ("stacking error - looking for input resource, but it is null");
1298 } else {
1299 DEBUG_MSG("so, returning %s\n",p->lastBaseResource->parsed_request);
1300 }
1301 //printf("getLastResource %s\n",p->lastBaseResource->parsed_request);
1302 return p->lastBaseResource;
1303 }
1304
1305
1306 cwu = stack_top(resource_item_t *, p->resStack);
1307 DEBUG_MSG("getInputResource current Resource is %lu %lx %s\n", (unsigned long int) cwu, (unsigned long int) cwu, cwu->parsed_request);
1308 //printf("getCurrentResource %s\n",cwu->parsed_request);
1309 return cwu;
1310}
1311
1312//used by FEGF configs in frontend
1313char* fwl_resitem_getURL(void *resp){
1314 resource_item_t *res = (resource_item_t *)resp;
1315 return res->parsed_request;
1316}
1317void fwl_resitem_setActualFile(void *resp, char *fname){
1318 resource_item_t *res = (resource_item_t *)resp;
1319 res->actual_file = STRDUP(fname);
1320 if(strcmp(res->actual_file,res->parsed_request)){
1321 //it's a temp file
1322 s_list_t *item;
1323 item = ml_new(res->actual_file);
1324 if (!res->cached_files)
1325 res->cached_files = (void *)item;
1326 else
1327 res->cached_files = ml_append(res->cached_files,item);
1328 }
1329}
1330char* fwl_resitem_getTempDir(void *resp){
1331 resource_item_t *res = (resource_item_t *)resp;
1332 return res->temp_dir;
1333}
1334void fwl_resitem_enqueuNextMulti(void *resp){
1335 resource_item_t *res = (resource_item_t *)resp;
1336 int more_multi = (res->status == ress_failed) && (res->m_request != NULL);
1337 if(more_multi){
1338 //still some hope via multi_string url, perhaps next one
1339 res->status = ress_invalid; //downgrade ress_fail to ress_invalid
1340 res->type = rest_multi; //should already be flagged
1341 //must consult BE to convert relativeURL to absoluteURL via baseURL
1342 //(or could we absolutize in a batch in resource_create_multi0()?)
1343 resource_identify(res->parent, res); //should increment multi pointer/iterator
1344 frontenditem_enqueue(ml_new(res));
1345 }
1346}
1347char *strBackslash2fore(char *);
1348//int file2blob(resource_item_t *res);
1349void fwl_resitem_setLocalPath(void *resp, char* path){
1350 int delete_after_load;
1351 resource_item_t *res = (resource_item_t *)resp;
1352 res->status = ress_downloaded;
1353 res->actual_file = strBackslash2fore(STRDUP(path));
1354 delete_after_load = 1;
1355 if (delete_after_load){
1356 //warning this will delete the actual_file setLocalPath is for downloaded/copied/cached files only,
1357 //not direct intranet files as with desktop.c
1358 s_list_t *item;
1359 item = ml_new(res->actual_file);
1360 if (!res->cached_files)
1361 res->cached_files = (void *)item;
1362 else
1363 res->cached_files = ml_append(res->cached_files, item);
1364 }
1365 res->_loadFunc = (void *)file2blob; //msvc can also do &file2blob
1366}
1367int fwl_resitem_getStatus(void *resp){
1368 resource_item_t *res = (resource_item_t *)resp;
1369 return res->status;
1370}
1371void fwl_resitem_setStatus(void *resp, int status) {
1372 resource_item_t *res = (resource_item_t *)resp;
1373 res->status = status;
1374}
1375
1376int fwl_resitem_getType(void *resp){
1377 resource_item_t *res = (resource_item_t *)resp;
1378 return res->type;
1379}
1380int fwl_resitem_getMediaType(void *resp){
1381 resource_item_t *res = (resource_item_t *)resp;
1382 return res->media_type;
1383}
1384void fwl_resitem_setDownloadThread(void *resp, void *thread){
1385 resource_item_t *res = (resource_item_t *)resp;
1386 res->_loadThread = (pthread_t*)thread;
1387}
1388void * fwl_resitem_getDownloadThread(void *resp){
1389 resource_item_t *res = (resource_item_t *)resp;
1390 return res->_loadThread;
1391}
1392void * fwl_resitem_getGlobal(void *resp){
1393 resource_item_t *res = (resource_item_t *)resp;
1394 return res->tg;
1395}