Mercurial > hg > dmlib
comparison src/dmres.c @ 812:1e5cf1144f36
Move library source under src/ subdirectory.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 16 May 2014 03:22:39 +0300 |
parents | dmres.c@897646df1de1 |
children | 27949209238b |
comparison
equal
deleted
inserted
replaced
811:aebc2f8b2c2d | 812:1e5cf1144f36 |
---|---|
1 /* | |
2 * dmlib | |
3 * -- Resource management | |
4 * Programmed and designed by Matti 'ccr' Hamalainen | |
5 * (C) Copyright 2003-2013 Tecnic Software productions (TNSP) | |
6 */ | |
7 #include "dmres.h" | |
8 #include <time.h> | |
9 | |
10 #ifdef DM_USE_PACKFS | |
11 #include <zlib.h> | |
12 #endif | |
13 | |
14 #ifdef DM_USE_STDIO | |
15 #include <sys/types.h> | |
16 #include <sys/stat.h> | |
17 #include <unistd.h> | |
18 #include <dirent.h> | |
19 #endif | |
20 | |
21 | |
22 DMResource *dmResourceNew(DMResourceLib *lib, const char *filename, const size_t size) | |
23 { | |
24 DMResource *node = dmMalloc0(sizeof(DMResource)); | |
25 if (node == NULL) | |
26 return NULL; | |
27 | |
28 node->lib = lib; | |
29 node->filename = dm_strdup(filename); | |
30 node->rawSize = size; | |
31 | |
32 return node; | |
33 } | |
34 | |
35 | |
36 void dmResourceFreeResData(DMResource *node) | |
37 { | |
38 if (node->resData != NULL && | |
39 node->rops != NULL && | |
40 node->rops->free != NULL) | |
41 { | |
42 node->rops->free(node); | |
43 } | |
44 | |
45 node->resData = NULL; | |
46 node->flags &= ~DMF_LOADED_RES; | |
47 } | |
48 | |
49 | |
50 void dmResourceFreeRawData(DMResource *node) | |
51 { | |
52 if ((node->flags & DMF_UNALLOCATED) == 0) | |
53 { | |
54 dmFree(node->rawData); | |
55 node->rawData = NULL; | |
56 node->flags &= ~DMF_LOADED_RAW; | |
57 } | |
58 } | |
59 | |
60 | |
61 void dmResourceFree(DMResource *node) | |
62 { | |
63 if (node != NULL) | |
64 { | |
65 dmResourceFreeResData(node); | |
66 dmResourceFreeRawData(node); | |
67 dmFree(node->filename); | |
68 dmFree(node); | |
69 } | |
70 } | |
71 | |
72 | |
73 void dmResourceInsert(DMResourceLib *lib, DMResource * node) | |
74 { | |
75 if (lib == NULL || node == NULL) | |
76 return; | |
77 | |
78 node->lib = lib; | |
79 | |
80 if (lib->resources != NULL) | |
81 { | |
82 node->prev = lib->resources->prev; | |
83 lib->resources->prev->next = node; | |
84 lib->resources->prev = node; | |
85 } | |
86 else | |
87 { | |
88 lib->resources = node->prev = node; | |
89 } | |
90 | |
91 node->next = NULL; | |
92 } | |
93 | |
94 | |
95 void dmResourceDelete(DMResourceLib *lib, DMResource * node) | |
96 { | |
97 if (lib == NULL) | |
98 return; | |
99 | |
100 if (node->prev) | |
101 node->prev->next = node->next; | |
102 | |
103 if (node->next) | |
104 node->next->prev = node->prev; | |
105 else | |
106 lib->resources->prev = node->prev; | |
107 | |
108 node->prev = node->next = NULL; | |
109 } | |
110 | |
111 | |
112 DMResource * dmResourceFind(DMResourceLib *lib, const char *filename) | |
113 { | |
114 DMResource *node, *found = NULL; | |
115 | |
116 if (lib == NULL) | |
117 return NULL; | |
118 | |
119 dmMutexLock(lib->mutex); | |
120 | |
121 for (node = lib->resources; node != NULL; node = node->next) | |
122 { | |
123 if (strcmp(node->filename, filename) == 0) | |
124 { | |
125 found = node; | |
126 break; | |
127 } | |
128 } | |
129 | |
130 dmMutexUnlock(lib->mutex); | |
131 | |
132 return found; | |
133 } | |
134 | |
135 | |
136 #ifdef DM_USE_STDIO | |
137 /* Basic stdio file routines | |
138 */ | |
139 static int dm_stdio_fopen(DMResource *handle) | |
140 { | |
141 char *rfilename = dm_strdup_printf("%s%s", DMRES_DATA_PATH, handle->filename); | |
142 if (rfilename == NULL) | |
143 return DMERR_MALLOC; | |
144 | |
145 handle->fh = fopen(rfilename, "rb"); | |
146 dmFree(rfilename); | |
147 | |
148 handle->error = dmGetErrno(); | |
149 return (handle->fh != NULL) ? DMERR_OK : DMERR_FOPEN; | |
150 } | |
151 | |
152 | |
153 static void dm_stdio_fclose(DMResource * f) | |
154 { | |
155 if (f->fh != NULL) | |
156 { | |
157 fclose(f->fh); | |
158 f->fh = NULL; | |
159 } | |
160 } | |
161 | |
162 | |
163 static int dm_stdio_ferror(DMResource * f) | |
164 { | |
165 return f->error; | |
166 } | |
167 | |
168 | |
169 static int dm_stdio_fseek(DMResource *f, const off_t pos, const int whence) | |
170 { | |
171 int ret = fseek(f->fh, pos, whence); | |
172 f->error = dmGetErrno(); | |
173 return ret; | |
174 } | |
175 | |
176 | |
177 static int dm_stdio_freset(DMResource * f) | |
178 { | |
179 if (f->fh != NULL) | |
180 return dm_stdio_fseek(f, 0L, SEEK_SET); | |
181 else | |
182 return DMERR_OK; | |
183 } | |
184 | |
185 | |
186 static off_t dm_stdio_fsize(DMResource *f) | |
187 { | |
188 off_t savePos, fileSize; | |
189 | |
190 // Check if the size is cached | |
191 if (f->rawSize != 0) | |
192 return f->rawSize; | |
193 | |
194 // Get file size | |
195 savePos = ftello(f->fh); | |
196 if (fseeko(f->fh, 0L, SEEK_END) != 0) | |
197 { | |
198 f->error = dmGetErrno(); | |
199 return -1; | |
200 } | |
201 | |
202 fileSize = ftello(f->fh); | |
203 if (fseeko(f->fh, savePos, SEEK_SET) != 0) | |
204 { | |
205 f->error = dmGetErrno(); | |
206 return -1; | |
207 } | |
208 | |
209 f->rawSize = fileSize; | |
210 return fileSize; | |
211 } | |
212 | |
213 | |
214 static off_t dm_stdio_ftell(DMResource * f) | |
215 { | |
216 return ftell(f->fh); | |
217 } | |
218 | |
219 | |
220 static BOOL dm_stdio_feof(DMResource * f) | |
221 { | |
222 return feof(f->fh); | |
223 } | |
224 | |
225 | |
226 static int dm_stdio_fgetc(DMResource * f) | |
227 { | |
228 int ret = fgetc(f->fh); | |
229 f->error = dmGetErrno(); | |
230 return ret; | |
231 } | |
232 | |
233 | |
234 static int dm_stdio_fputc(int v, DMResource * f) | |
235 { | |
236 int ret = fputc(v, f->fh); | |
237 f->error = dmGetErrno(); | |
238 return ret; | |
239 } | |
240 | |
241 | |
242 static size_t dm_stdio_fread(void *ptr, size_t size, size_t nmemb, DMResource * f) | |
243 { | |
244 size_t ret = fread(ptr, size, nmemb, f->fh); | |
245 f->error = dmGetErrno(); | |
246 return ret; | |
247 } | |
248 | |
249 | |
250 static size_t dm_stdio_fwrite(void *ptr, size_t size, size_t nmemb, DMResource * f) | |
251 { | |
252 size_t ret = fwrite(ptr, size, nmemb, f->fh); | |
253 f->error = dmGetErrno(); | |
254 return ret; | |
255 } | |
256 | |
257 | |
258 static int dm_stdio_preload(DMResource *handle) | |
259 { | |
260 int ret = dm_stdio_fopen(handle); | |
261 if (ret != DMERR_OK) | |
262 return ret; | |
263 | |
264 dm_stdio_fsize(handle); | |
265 | |
266 handle->rawData = dmMalloc(handle->rawSize); | |
267 if (handle->rawData == NULL) | |
268 return DMERR_MALLOC; | |
269 | |
270 if (dm_stdio_fread(handle->rawData, sizeof(Uint8), handle->rawSize, handle) != handle->rawSize) | |
271 return DMERR_FREAD; | |
272 | |
273 return DMERR_OK; | |
274 } | |
275 | |
276 | |
277 DMResourceOps dfStdioFileOps = | |
278 { | |
279 "Stdio", | |
280 | |
281 dm_stdio_freset, | |
282 dm_stdio_ferror, | |
283 dm_stdio_fseek, | |
284 dm_stdio_fsize, | |
285 dm_stdio_ftell, | |
286 dm_stdio_feof, | |
287 dm_stdio_fgetc, | |
288 dm_stdio_fputc, | |
289 dm_stdio_fread, | |
290 dm_stdio_fwrite, | |
291 | |
292 dm_stdio_fopen, | |
293 dm_stdio_fclose, | |
294 dm_stdio_preload | |
295 }; | |
296 | |
297 DMResourceOps dfStdioFHOps = | |
298 { | |
299 "StdioFH", | |
300 | |
301 dm_stdio_freset, | |
302 dm_stdio_ferror, | |
303 dm_stdio_fseek, | |
304 dm_stdio_fsize, | |
305 dm_stdio_ftell, | |
306 dm_stdio_feof, | |
307 dm_stdio_fgetc, | |
308 dm_stdio_fputc, | |
309 dm_stdio_fread, | |
310 dm_stdio_fwrite, | |
311 | |
312 NULL, | |
313 NULL, | |
314 NULL | |
315 }; | |
316 #endif | |
317 | |
318 | |
319 // Some mingw/windows headers define these as macros, which is bad for us | |
320 #ifdef __WIN32 | |
321 #undef ferror | |
322 #undef feof | |
323 #endif | |
324 | |
325 | |
326 /* | |
327 * PACK file routines | |
328 */ | |
329 #ifdef DM_USE_PACKFS | |
330 static int dm_pack_preload(DMResource *handle) | |
331 { | |
332 DMPackEntry *node; | |
333 int res = DMERR_OK, cres, cdataLeft; | |
334 z_stream cstream; | |
335 Uint8 * cbuffer = NULL; | |
336 | |
337 if (handle->lib == NULL || handle->lib->packFile == NULL) | |
338 return DMERR_NULLPTR; | |
339 | |
340 // Search PACK nodelist for file | |
341 if ((node = dmPackFind(handle->lib->packFile->entries, handle->filename)) == NULL) | |
342 { | |
343 dmError("Entry '%s' not found in PACK file.\n", handle->filename); | |
344 res = DMERR_NOT_FOUND; | |
345 goto error; | |
346 } | |
347 | |
348 // Seek to entry | |
349 if (fseek(handle->lib->packFile->file, node->offset, SEEK_SET) == -1) | |
350 { | |
351 dmError("Could not seek node position in PACK file.\n"); | |
352 res = DMERR_FSEEK; | |
353 goto error; | |
354 } | |
355 | |
356 // Allocate a structures and buffers | |
357 cbuffer = (Uint8 *) dmMalloc(DPACK_TMPSIZE); | |
358 if (cbuffer == NULL) | |
359 { | |
360 res = DMERR_MALLOC; | |
361 goto error; | |
362 } | |
363 | |
364 // Initialize fields | |
365 handle->rawOffset = 0; | |
366 handle->rawSize = node->size; | |
367 handle->rawData = (Uint8 *) dmMalloc(node->size); | |
368 if (handle->rawData == NULL) | |
369 { | |
370 res = DMERR_MALLOC; | |
371 goto error; | |
372 } | |
373 | |
374 // Initialize decompression | |
375 cstream.zalloc = (alloc_func) Z_NULL; | |
376 cstream.zfree = (free_func) Z_NULL; | |
377 cstream.opaque = (voidpf) Z_NULL; | |
378 cstream.next_out = handle->rawData; | |
379 cstream.avail_out = handle->rawSize; | |
380 cdataLeft = node->length; | |
381 cres = inflateInit(&(cstream)); | |
382 if (cres != Z_OK) | |
383 { | |
384 dmError("Could not initialize zlib stream inflation.\n"); | |
385 res = DMERR_INIT_FAIL; | |
386 goto error; | |
387 } | |
388 | |
389 // Uncompress the data | |
390 while (cdataLeft > 0 && | |
391 cstream.avail_out > 0 && cres == Z_OK) | |
392 { | |
393 cstream.avail_in = fread( | |
394 cbuffer, sizeof(Uint8), | |
395 (cdataLeft >= DPACK_TMPSIZE) ? DPACK_TMPSIZE : cdataLeft, | |
396 handle->lib->packFile->file); | |
397 | |
398 cdataLeft -= cstream.avail_in; | |
399 cstream.next_in = cbuffer; | |
400 cres = inflate(&cstream, Z_FULL_FLUSH); | |
401 } | |
402 | |
403 // Cleanup | |
404 inflateEnd(&(cstream)); | |
405 | |
406 error: | |
407 dmFree(cbuffer); | |
408 return res; | |
409 } | |
410 | |
411 | |
412 static int dm_pack_fopen(DMResource * f) | |
413 { | |
414 if ((f->flags & DMF_LOADED_RAW) == 0) | |
415 { | |
416 int res = dm_pack_preload(f); | |
417 if (res == DMERR_OK) | |
418 f->flags |= DMF_LOADED_RAW; | |
419 | |
420 return res; | |
421 } | |
422 else | |
423 return DMERR_OK; | |
424 } | |
425 | |
426 | |
427 static void dm_pack_fclose(DMResource * f) | |
428 { | |
429 if ((f->flags & DMF_PERSIST) == 0) | |
430 dmResourceFreeRawData(f); | |
431 } | |
432 #endif | |
433 | |
434 | |
435 static int dm_mem_freset(DMResource * f) | |
436 { | |
437 f->rawOffset = 0; | |
438 return DMERR_OK; | |
439 } | |
440 | |
441 | |
442 static int dm_mem_ferror(DMResource * f) | |
443 { | |
444 return f->error; | |
445 } | |
446 | |
447 | |
448 static int dm_mem_fseek(DMResource * f, const off_t offset, const int whence) | |
449 { | |
450 off_t newPos; | |
451 | |
452 // Calculate the new position | |
453 switch (whence) | |
454 { | |
455 case SEEK_SET: | |
456 newPos = offset; | |
457 break; | |
458 | |
459 case SEEK_CUR: | |
460 newPos = f->rawOffset + offset; | |
461 break; | |
462 | |
463 case SEEK_END: | |
464 newPos = f->rawSize + offset; | |
465 break; | |
466 | |
467 default: | |
468 return -1; | |
469 } | |
470 | |
471 // Set the new position | |
472 f->rawOffset = newPos; | |
473 | |
474 // Check the new position | |
475 if (newPos < 0 && (size_t) newPos >= f->rawSize) | |
476 return -1; | |
477 | |
478 return 0; | |
479 } | |
480 | |
481 | |
482 static off_t dm_mem_fsize(DMResource * f) | |
483 { | |
484 return f->rawSize; | |
485 } | |
486 | |
487 | |
488 static off_t dm_mem_ftell(DMResource * f) | |
489 { | |
490 return f->rawOffset; | |
491 } | |
492 | |
493 | |
494 static BOOL dm_mem_feof(DMResource * f) | |
495 { | |
496 // Check for EOF | |
497 if ((size_t) f->rawOffset <= f->rawSize) | |
498 return FALSE; | |
499 else | |
500 return TRUE; | |
501 } | |
502 | |
503 | |
504 static int dm_mem_fgetc(DMResource * f) | |
505 { | |
506 // Check for EOF | |
507 if ((size_t) f->rawOffset < f->rawSize) | |
508 return f->rawData[f->rawOffset++]; | |
509 else | |
510 return EOF; | |
511 } | |
512 | |
513 | |
514 static size_t dm_mem_fread(void *buf, size_t size, size_t nmemb, DMResource * f) | |
515 { | |
516 size_t length = (size * nmemb); | |
517 | |
518 // Check if we can read the whole chunk | |
519 if (((size_t) f->rawOffset + length) >= f->rawSize) | |
520 { | |
521 nmemb = (f->rawSize - f->rawOffset) / size; | |
522 length = size * nmemb; | |
523 } | |
524 | |
525 memcpy(buf, f->rawData + f->rawOffset, length); | |
526 f->rawOffset += length; | |
527 return nmemb; | |
528 } | |
529 | |
530 | |
531 static int dm_mem_fputc(int ch, DMResource * f) | |
532 { | |
533 // Check for EOF | |
534 if ((size_t) f->rawOffset < f->rawSize) | |
535 { | |
536 f->rawData[f->rawOffset++] = ch; | |
537 return ch; | |
538 } | |
539 else | |
540 return EOF; | |
541 } | |
542 | |
543 | |
544 static size_t dm_mem_fwrite(void *buf, size_t size, size_t nmemb, DMResource * f) | |
545 { | |
546 size_t length = (size * nmemb); | |
547 | |
548 // Check if we can write the whole chunk | |
549 if (((size_t) f->rawOffset + length) >= f->rawSize) | |
550 { | |
551 nmemb = (f->rawSize - f->rawOffset) / size; | |
552 length = size * nmemb; | |
553 } | |
554 | |
555 if (length > 0) | |
556 { | |
557 memcpy(f->rawData + f->rawOffset, buf, length); | |
558 f->rawOffset += length; | |
559 } | |
560 return nmemb; | |
561 } | |
562 | |
563 | |
564 #ifdef DM_USE_PACKFS | |
565 DMResourceOps dfPackFileOps = | |
566 { | |
567 "PackFS", | |
568 | |
569 dm_mem_freset, | |
570 dm_mem_ferror, | |
571 dm_mem_fseek, | |
572 dm_mem_fsize, | |
573 dm_mem_ftell, | |
574 dm_mem_feof, | |
575 dm_mem_fgetc, | |
576 NULL, | |
577 dm_mem_fread, | |
578 NULL, | |
579 | |
580 dm_pack_fopen, | |
581 dm_pack_fclose, | |
582 dm_pack_preload, | |
583 }; | |
584 #endif | |
585 | |
586 | |
587 DMResourceOps dfMemIOFileOps = | |
588 { | |
589 "MemIO", | |
590 | |
591 dm_mem_freset, | |
592 dm_mem_ferror, | |
593 dm_mem_fseek, | |
594 dm_mem_fsize, | |
595 dm_mem_ftell, | |
596 dm_mem_feof, | |
597 dm_mem_fgetc, | |
598 dm_mem_fputc, | |
599 dm_mem_fread, | |
600 dm_mem_fwrite, | |
601 | |
602 NULL, | |
603 dmResourceFree, | |
604 NULL | |
605 }; | |
606 | |
607 | |
608 /* FS file handling functions. These functions call the actual | |
609 * functions depending on where the file is located. | |
610 */ | |
611 static int dmResourcePreload(DMResource *handle) | |
612 { | |
613 int ret = DMERR_OK; | |
614 | |
615 // Check if we want to preload raw data? | |
616 if ((handle->lib->flags & DRF_PRELOAD_RAW) || | |
617 handle->rops == NULL || handle->rops->load == NULL) | |
618 { | |
619 if (handle->flags & DMF_LOADED_RAW) | |
620 ret = DMERR_OK; | |
621 else | |
622 if (handle->fops->preload != NULL) | |
623 { | |
624 ret = handle->fops->preload(handle); | |
625 if (ret == DMERR_OK) | |
626 handle->flags |= DMF_LOADED_RAW | DMF_PERSIST; | |
627 } | |
628 else | |
629 ret = DMERR_INIT_FAIL; | |
630 | |
631 dmfreset(handle); | |
632 } | |
633 | |
634 // Check if resource data is to be preloaded | |
635 if (handle->lib->flags & DRF_PRELOAD_RES) | |
636 { | |
637 if (handle->flags & DMF_LOADED_RES) | |
638 ret = DMERR_OK; | |
639 else | |
640 if (handle->rops != NULL && | |
641 handle->rops->load != NULL) | |
642 { | |
643 if ((ret = handle->fops->fopen(handle)) == DMERR_OK) | |
644 { | |
645 ret = handle->rops->load(handle); | |
646 handle->fops->fclose(handle); | |
647 } | |
648 | |
649 if (ret == DMERR_OK) | |
650 handle->flags |= DMF_LOADED_RES; | |
651 } | |
652 | |
653 dmfreset(handle); | |
654 } | |
655 | |
656 return ret; | |
657 } | |
658 | |
659 | |
660 int dmf_open(DMResourceLib *lib, const char *filename, DMResource **phandle) | |
661 { | |
662 DMResource *handle; | |
663 int res; | |
664 | |
665 // Check master directory for resource | |
666 if ((*phandle = handle = dmResourceFind(lib, filename)) == NULL) | |
667 { | |
668 #ifdef DM_USE_STDIO | |
669 if (lib->flags & DRF_USE_STDIO) | |
670 { | |
671 // Hmm.. does not exist? Fall back to a stdio file | |
672 *phandle = handle = dmResourceNew(lib, filename, 0); | |
673 if (handle == NULL) | |
674 return DMERR_MALLOC; | |
675 | |
676 handle->fops = &dfStdioFileOps; | |
677 } | |
678 else | |
679 return DMERR_INIT_FAIL; | |
680 #else | |
681 // Stdio not enabled, fail | |
682 return DMERR_INIT_FAIL; | |
683 #endif | |
684 } | |
685 | |
686 // Check if the data is preloaded | |
687 if ((res = handle->fops->fopen(handle)) == DMERR_OK) | |
688 { | |
689 dmResourceRef(handle); | |
690 if (handle->flags & DMF_TEMPORARY) | |
691 { | |
692 handle->flags &= ~DMF_TEMPORARY; | |
693 dmResourceInsert(lib, handle); | |
694 } | |
695 } | |
696 else | |
697 if (handle->flags & DMF_TEMPORARY) | |
698 { | |
699 dmResourceFree(handle); | |
700 *phandle = handle = NULL; | |
701 } | |
702 | |
703 dmfreset(handle); | |
704 return res; | |
705 } | |
706 | |
707 | |
708 int dmf_create_memio(DMResourceLib *lib, const char *filename, | |
709 Uint8 *buf, const size_t size, DMResource **phandle) | |
710 { | |
711 DMResource *handle; | |
712 | |
713 // Check master directory for resource | |
714 if ((*phandle = handle = dmResourceFind(lib, filename)) == NULL) | |
715 { | |
716 if ((*phandle = handle = dmResourceNew(lib, filename, size)) == NULL) | |
717 return DMERR_MALLOC; | |
718 | |
719 handle->flags = DMF_LOADED_RAW | DMF_UNALLOCATED; | |
720 handle->fops = &dfMemIOFileOps; | |
721 handle->rawData = buf; | |
722 dmResourceInsert(lib, handle); | |
723 } | |
724 | |
725 // Increase refcount | |
726 dmResourceRef(handle); | |
727 dmfreset(handle); | |
728 return DMERR_OK; | |
729 } | |
730 | |
731 | |
732 #ifdef DM_USE_STDIO | |
733 int dmf_create_stdio(const char *filename, const char *mode, DMResource **phandle) | |
734 { | |
735 DMResource *handle; | |
736 if ((*phandle = handle = dmResourceNew(NULL, filename, 0)) == NULL) | |
737 return DMERR_MALLOC; | |
738 | |
739 handle->fops = &dfStdioFileOps; | |
740 handle->fh = fopen(filename, mode); | |
741 handle->error = dmGetErrno(); | |
742 | |
743 if (handle->fh == NULL) | |
744 { | |
745 dmResourceFree(handle); | |
746 return handle->error; | |
747 } | |
748 | |
749 dmResourceRef(handle); | |
750 return DMERR_OK; | |
751 } | |
752 | |
753 | |
754 int dmf_create_stdio_stream(FILE *fh, DMResource **phandle) | |
755 { | |
756 DMResource *handle; | |
757 if ((*phandle = handle = dmResourceNew(NULL, "", 0)) == NULL) | |
758 return DMERR_MALLOC; | |
759 | |
760 handle->fops = &dfStdioFHOps; | |
761 handle->fh = fh; | |
762 dmResourceRef(handle); | |
763 return DMERR_OK; | |
764 } | |
765 #endif | |
766 | |
767 | |
768 void dmf_close(DMResource * f) | |
769 { | |
770 if (f == NULL) | |
771 return; | |
772 | |
773 if (f->fops->fclose != NULL) | |
774 f->fops->fclose(f); | |
775 | |
776 dmResourceUnref(f); | |
777 } | |
778 | |
779 | |
780 int dmfreset(DMResource *f) | |
781 { | |
782 if (f == NULL) | |
783 return DMERR_NULLPTR; | |
784 | |
785 if (f->fops == NULL || f->fops->freset == NULL) | |
786 return DMERR_OK; | |
787 | |
788 return f->fops->freset(f); | |
789 } | |
790 | |
791 int dmferror(DMResource * f) | |
792 { | |
793 f->atime = time(NULL); | |
794 return f->fops->ferror(f); | |
795 } | |
796 | |
797 int dmfseek(DMResource * f, off_t offset, int whence) | |
798 { | |
799 f->atime = time(NULL); | |
800 return f->fops->fseek(f, offset, whence); | |
801 } | |
802 | |
803 off_t dmfsize(DMResource * f) | |
804 { | |
805 f->atime = time(NULL); | |
806 return f->fops->fsize(f); | |
807 } | |
808 | |
809 off_t dmftell(DMResource * f) | |
810 { | |
811 f->atime = time(NULL); | |
812 return f->fops->ftell(f); | |
813 } | |
814 | |
815 BOOL dmfeof(DMResource * f) | |
816 { | |
817 f->atime = time(NULL); | |
818 return f->fops->feof(f); | |
819 } | |
820 | |
821 int dmfgetc(DMResource * f) | |
822 { | |
823 f->atime = time(NULL); | |
824 return f->fops->fgetc(f); | |
825 } | |
826 | |
827 int dmfputc(int v, DMResource * f) | |
828 { | |
829 f->atime = time(NULL); | |
830 return f->fops->fputc(v, f); | |
831 } | |
832 | |
833 size_t dmfread(void *ptr, size_t size, size_t nmemb, DMResource * f) | |
834 { | |
835 f->atime = time(NULL); | |
836 return f->fops->fread(ptr, size, nmemb, f); | |
837 } | |
838 | |
839 size_t dmfwrite(void *ptr, size_t size, size_t nmemb, DMResource * f) | |
840 { | |
841 f->atime = time(NULL); | |
842 return f->fops->fwrite(ptr, size, nmemb, f); | |
843 } | |
844 | |
845 char *dmfgets(char *s, int size, DMResource * f) | |
846 { | |
847 char *p = s, c; | |
848 int n = 0; | |
849 | |
850 while ((c = f->fops->fgetc(f)) != EOF) | |
851 { | |
852 n++; | |
853 if (c == '\n') | |
854 break; | |
855 else | |
856 if (n < size - 1) | |
857 *p++ = c; | |
858 } | |
859 *p = 0; | |
860 | |
861 return (n > 0) ? s : NULL; | |
862 } | |
863 | |
864 | |
865 int dmResourceRef(DMResource *node) | |
866 { | |
867 if (node->lib != NULL) dmMutexLock(node->lib->mutex); | |
868 node->atime = time(NULL); | |
869 node->refcount++; | |
870 if (node->lib != NULL) dmMutexUnlock(node->lib->mutex); | |
871 | |
872 return node->refcount; | |
873 } | |
874 | |
875 | |
876 int dmResourceUnref(DMResource *node) | |
877 { | |
878 if (node->lib != NULL) dmMutexLock(node->lib->mutex); | |
879 node->refcount--; | |
880 if (node->lib != NULL) dmMutexUnlock(node->lib->mutex); | |
881 | |
882 return node->refcount; | |
883 } | |
884 | |
885 | |
886 #ifdef DM_USE_STDIO | |
887 static int dmResourcesLoadDirectory(DMResourceLib *lib, const char *path) | |
888 { | |
889 int res = DMERR_OK; | |
890 struct dirent *dh; | |
891 DIR *hdir = opendir(path); | |
892 if (hdir == NULL) | |
893 return dmGetErrno(); | |
894 | |
895 dmMutexLock(lib->mutex); | |
896 | |
897 do | |
898 { | |
899 DMResource *node = NULL; | |
900 dh = readdir(hdir); | |
901 if (dh != NULL) | |
902 { | |
903 struct stat sbuf; | |
904 char *fname = dm_strdup_printf("%s/%s", path, dh->d_name); | |
905 if (stat(fname, &sbuf) == -1) | |
906 { | |
907 res = dmGetErrno(); | |
908 dmError("Could not stat() %s, #%d: %s\n", | |
909 fname, res, dmErrorStr(res)); | |
910 dmFree(fname); | |
911 goto out; | |
912 } | |
913 | |
914 if (S_ISREG(sbuf.st_mode)) | |
915 node = dmResourceNew(lib, dh->d_name, sbuf.st_size); | |
916 } | |
917 | |
918 if (node != NULL) | |
919 { | |
920 node->fops = &dfStdioFileOps; | |
921 dmResourceInsert(lib, node); | |
922 } | |
923 } while (dh != NULL); | |
924 | |
925 out: | |
926 dmMutexUnlock(lib->mutex); | |
927 | |
928 #ifdef __WIN32 | |
929 #else | |
930 closedir(hdir); | |
931 #endif | |
932 | |
933 return res; | |
934 } | |
935 #endif | |
936 | |
937 | |
938 /* Resources subsystem initialization and shutdown routines | |
939 */ | |
940 int dmResourcesInit(DMResourceLib **plib, const char *filename, const char *path, const int flags, int (*classifier)(DMResource *)) | |
941 { | |
942 DMResourceLib *lib; | |
943 BOOL initialized = FALSE; | |
944 | |
945 // Allocate the resource library structure | |
946 if ((*plib = lib = dmMalloc0(sizeof(DMResourceLib))) == NULL) | |
947 return DMERR_MALLOC; | |
948 | |
949 // Basic data | |
950 lib->mutex = dmCreateMutex(); | |
951 lib->flags = flags; | |
952 lib->resPath = dm_strdup((path != NULL) ? path : DMRES_DATA_PATH); | |
953 | |
954 | |
955 #ifdef DM_USE_PACKFS | |
956 if (flags & DRF_USE_PACK) | |
957 { | |
958 int ret; | |
959 DMPackEntry *node; | |
960 | |
961 lib->packFilename = dm_strdup((filename != NULL) ? filename : DMRES_DATA_PACK); | |
962 | |
963 // Initialize PACK, open as read-only | |
964 ret = dmPackOpen(lib->packFilename, &lib->packFile, TRUE); | |
965 if (ret != DMERR_OK) | |
966 { | |
967 if ((flags & DRF_USE_STDIO) == 0) | |
968 { | |
969 dmError("Error opening PACK file '%s', #%d: %s\n", | |
970 lib->packFilename, ret, dmErrorStr(ret)); | |
971 | |
972 return DMERR_INIT_FAIL; | |
973 } | |
974 else | |
975 dmError("Failed to open PACK, falling back to STDIO, '%s' %d: %s\n", | |
976 lib->packFilename, ret, dmErrorStr(ret)); | |
977 } | |
978 else | |
979 { | |
980 // Initialize resources from a PACK file | |
981 for (node = lib->packFile->entries; node != NULL; node = node->next) | |
982 { | |
983 DMResource *res = dmResourceNew(lib, node->filename, node->size); | |
984 if (res == NULL) | |
985 { | |
986 dmError("Could not allocate memory for resource node '%s' [0x%08x], %d.\n", | |
987 node->filename, node->flags, node->size); | |
988 return DMERR_INIT_FAIL; | |
989 } | |
990 | |
991 res->fops = &dfPackFileOps; | |
992 dmResourceInsert(lib, res); | |
993 } | |
994 | |
995 initialized = TRUE; | |
996 } | |
997 } | |
998 #endif | |
999 | |
1000 #ifdef DM_USE_STDIO | |
1001 if (!initialized && (flags & DRF_USE_STDIO)) | |
1002 { | |
1003 // Initialize resources from a resource directory | |
1004 int ret = dmResourcesLoadDirectory(lib, lib->resPath); | |
1005 if (ret != DMERR_OK) | |
1006 return ret; | |
1007 initialized = TRUE; | |
1008 } | |
1009 #endif | |
1010 | |
1011 if (!initialized) | |
1012 return DMERR_INIT_FAIL; | |
1013 | |
1014 // Okay, classify resources | |
1015 if (lib->resources != NULL && classifier != NULL) | |
1016 { | |
1017 DMResource *node; | |
1018 for (node = lib->resources; node != NULL; node = node->next) | |
1019 { | |
1020 int ret = classifier(node); | |
1021 if (ret != DMERR_OK) | |
1022 return ret; | |
1023 } | |
1024 } | |
1025 | |
1026 // Initialization complete | |
1027 return DMERR_OK; | |
1028 } | |
1029 | |
1030 | |
1031 int dmResourcesClose(DMResourceLib *lib) | |
1032 { | |
1033 DMResource *node; | |
1034 | |
1035 if (lib == NULL) | |
1036 return DMERR_NULLPTR; | |
1037 | |
1038 dmMutexLock(lib->mutex); | |
1039 | |
1040 // Shutdown possible subsystems | |
1041 #ifdef DM_USE_PACKFS | |
1042 if (lib->flags & DRF_USE_PACK) | |
1043 { | |
1044 int res = dmPackClose(lib->packFile); | |
1045 if (res != DMERR_OK) | |
1046 { | |
1047 dmError("Error closing PACK, #%i: %s\n", | |
1048 res, dmErrorStr(res)); | |
1049 } | |
1050 | |
1051 dmFree(lib->packFilename); | |
1052 } | |
1053 #endif | |
1054 | |
1055 // Free resource entries | |
1056 node = lib->resources; | |
1057 while (node != NULL) | |
1058 { | |
1059 DMResource *next = node->next; | |
1060 dmResourceFree(node); | |
1061 node = next; | |
1062 } | |
1063 | |
1064 // Etc. | |
1065 dmFree(lib->resPath); | |
1066 dmMutexUnlock(lib->mutex); | |
1067 dmDestroyMutex(lib->mutex); | |
1068 return DMERR_OK; | |
1069 } | |
1070 | |
1071 | |
1072 int dmResourcesPreload(DMResourceLib *lib, BOOL start, int *loaded, int *total) | |
1073 { | |
1074 int ret = DMERR_OK; | |
1075 | |
1076 dmMutexLock(lib->mutex); | |
1077 | |
1078 // Initialize preloading | |
1079 if (lib->preload == NULL || start) | |
1080 { | |
1081 DMResource *node; | |
1082 | |
1083 lib->preload = lib->resources; | |
1084 *loaded = 0; | |
1085 *total = 0; | |
1086 | |
1087 // Calculate total number of resources to be preloaded | |
1088 if (lib->flags & (DRF_PRELOAD_RAW | DRF_PRELOAD_RES)) | |
1089 { | |
1090 for (node = lib->resources; node != NULL; node = node->next) | |
1091 (*total)++; | |
1092 } | |
1093 } | |
1094 else | |
1095 if (lib->preload != NULL) | |
1096 { | |
1097 // Attempt to preload the resource | |
1098 if ((ret = dmResourcePreload(lib->preload)) != DMERR_OK) | |
1099 { | |
1100 dmError("Error preloading '%s', %d: %s\n", | |
1101 lib->preload->filename, ret, dmErrorStr(ret)); | |
1102 goto error; | |
1103 } | |
1104 | |
1105 (*loaded)++; | |
1106 lib->preload = lib->preload->next; | |
1107 } | |
1108 | |
1109 dmMutexUnlock(lib->mutex); | |
1110 return (lib->preload == NULL) ? DMERR_OK : DMERR_PROGRESS; | |
1111 | |
1112 error: | |
1113 dmMutexUnlock(lib->mutex); | |
1114 return ret; | |
1115 } | |
1116 | |
1117 | |
1118 void dmResourcePrune(DMResourceLib *lib, const int agems, int const flags) | |
1119 { | |
1120 DMResource *node; | |
1121 const int stamp = time(NULL); | |
1122 dmMutexLock(lib->mutex); | |
1123 | |
1124 for (node = lib->resources; node != NULL; node = node->next) | |
1125 { | |
1126 // Check if node has refcount of 0 and is | |
1127 // not marked as persistent resource | |
1128 if (node->refcount == 0 && | |
1129 (node->flags & DMF_PERSIST) == 0 && | |
1130 (node->flags & (DMF_LOADED_RES | DMF_LOADED_RAW))) | |
1131 { | |
1132 if (((flags & DMPRUNE_ATIME) && stamp - node->atime >= agems) || | |
1133 ((flags & DMPRUNE_MTIME) && stamp - node->mtime >= agems)) | |
1134 { | |
1135 dmResourceFreeResData(node); | |
1136 dmResourceFreeRawData(node); | |
1137 } | |
1138 } | |
1139 } | |
1140 | |
1141 dmMutexUnlock(lib->mutex); | |
1142 } | |
1143 | |
1144 | |
1145 /* Helper resource access routines | |
1146 */ | |
1147 int dmf_read_str(DMResource *f, void *s, size_t l) | |
1148 { | |
1149 return dmfread(s, 1, l, f) == l; | |
1150 } | |
1151 | |
1152 BOOL dmf_read_byte(DMResource *f, Uint8 *val) | |
1153 { | |
1154 int tmp = dmfgetc(f); | |
1155 *val = tmp; | |
1156 return tmp != EOF; | |
1157 } | |
1158 | |
1159 #define DM_DEFINE_FUNC(xname, xtype, xmacro) \ | |
1160 BOOL dmf_read_ ## xname (DMResource *f, xtype *v) { \ | |
1161 xtype result; \ | |
1162 if (dmfread(&result, sizeof( xtype ), 1, f) != 1) \ | |
1163 return FALSE; \ | |
1164 *v = DM_ ## xmacro ## _TO_NATIVE (result); \ | |
1165 return TRUE; \ | |
1166 } | |
1167 | |
1168 DM_DEFINE_FUNC(le16, Uint16, LE16) | |
1169 DM_DEFINE_FUNC(le32, Uint32, LE32) | |
1170 | |
1171 DM_DEFINE_FUNC(be16, Uint16, BE16) | |
1172 DM_DEFINE_FUNC(be32, Uint32, BE32) | |
1173 | |
1174 #ifdef DM_HAVE_64BIT | |
1175 DM_DEFINE_FUNC(le64, Uint64, LE64) | |
1176 DM_DEFINE_FUNC(be64, Uint64, BE64) | |
1177 #endif |