Merge branch 'master' of git://github.com/BTAxis/naev into testmission
[naev.git] / src / pack.c
blob400c1b038b7e0fc97e61b4f2a81ba2e8a99bed70
1 /*
2 * See Licensing and Copyright notice in naev.h
3 */
5 /**
6 * @file pack.c
8 * @brief Stores data in funky format.
10 * Format Overview:
12 * 1.1) Index
13 * 1.1.1) Magic Number (16 bytes)
14 * 1.1.2) Number of Files (uint32_t)
15 * 1.1.3) Files in format Name/Location
16 * 1.1.3.1) File Name (128 bytes max, ended in NUL)
17 * 1.1.3.2) File Location (uint32_t)
18 * 1.2) File data in format Size/Data
19 * 1.2.1) File Size (uint32_t)
20 * 1.2,2) File Data (char*)
21 * 1.2.3) File MD5 (16 byte char*)
22 * 1.3) EOF
25 * Program Overview:
27 * 2.1) Write Magic Number and Number of Files (1.1 and 1.2 above)
28 * 2,2) Write the Index
29 * 3,3) Pack the files
33 #include "pack.h"
35 #include "naev.h"
37 #include <stdio.h> /* printf() */
38 #include <fcntl.h> /* creat() and friends */
39 #include <stdint.h> /* uint32_t */
40 #if HAS_FD
41 #include <sys/types.h> /* ssize_t */
42 #include <sys/stat.h> /* S_IRUSR */
43 #endif /* HAS_FD */
44 #include <unistd.h> /* WRITE() */
45 #include <errno.h> /* error numbers */
46 #include <string.h> /* strlen() and friends */
47 #include <stdlib.h> /* malloc */
48 #if HAS_POSIX
49 #include <arpa/inet.h> /* ntohl */
50 #endif /* HAS_POSIX */
51 #if HAS_WIN32
52 #include <winsock2.h> /* ntohl */
53 #endif /* HAS_WIN32 */
55 #include "log.h"
56 #include "md5.h"
59 #if HAS_BIGENDIAN
60 #define htonll(x) (x)
61 #define ntohll(x) (x)
62 #else /* HAS_BIGENDIAN */
63 #define htonll(x) ((((uint64_t)htonl((uint32_t)x))<<32) + htonl((uint32_t)(x>>32)))
64 #define ntohll(x) ((((uint64_t)ntohl((uint32_t)x))<<32) + ntohl((uint32_t)(x>>32)))
65 #endif /* HAS_BIGENDIAN */
68 /**
69 * @brief Abstracts around packfiles.
71 struct Packfile_s {
72 #if HAS_FD
73 int fd; /**< file descriptor */
74 #else /* not HAS_FD */
75 FILE* fp; /**< For non-posix. */
76 #endif /* HAS_FD */
77 uint32_t pos; /**< cursor position */
78 uint32_t start; /**< File start. */
79 uint32_t end; /**< File end. */
81 uint32_t flags; /**< Special control flags. */
85 /**
86 * @brief Allows much faster creation of packfiles.
88 struct Packcache_s {
89 #if HAS_FD
90 int fd; /**< file descriptor */
91 #else /* not HAS_FD */
92 FILE *fp; /**< For non-posix. */
93 #endif /* HAS_FD */
95 char *name; /**< To open the file again. */
96 char **index; /**< Cached index for faster lookups. */
97 uint32_t *start; /**< Cached index starts. */
98 uint32_t nindex; /**< Number of index entries. */
102 * Helper defines.
104 #if HAS_FD
105 #define READ(f,b,n) if (read((f)->fd,(b),(n))!=(n)) { \
106 WARN("Fewer bytes read than expected"); \
107 return NULL; } /**< Helper define to check for errors. */
108 #else /* not HAS_FD */
109 #define READ(f,b,n) if (fread((b),1,(n),(f)->fp)!=(n)) { \
110 WARN("Fewer bytes read than expected"); \
111 return NULL; } /**< Helper define to check for errors. */
112 #endif /* HAS_FD */
114 #undef DEBUG /* mucho spamo */
115 #define DEBUG(str, args...) do {;} while(0) /**< Hack to disable debugging. */
118 #define BLOCKSIZE 128*1024 /**< The read/write block size. */
120 #ifndef PATH_MAX
121 #define PATH_MAX 256 /**< maximum file name length. */
122 #endif
125 #if HAS_FD
126 #define PERMS S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH /**< default permissions. */
127 #endif /* HAS_FD */
130 static const uint64_t magic = 0x4e41455644415441ULL; /**< File magic number: NAEVDATA */
134 * Flags.
136 #define PACKFILE_FROMCACHE (1<<0) /**< Packfile comes from a packcache. */
140 * Prototypes.
142 static off_t getfilesize( const char* filename );
143 /* RWops stuff. */
144 #if SDL_VERSION_ATLEAST(1,3,0)
145 static long packrw_seek( SDL_RWops *rw, long offset, int whence );
146 static size_t packrw_read( SDL_RWops *rw, void *ptr, size_t size, size_t maxnum );
147 static size_t packrw_write( SDL_RWops *rw, const void *ptr, size_t size, size_t num );
148 #else /* SDL_VERSION_ATLEAST(1,3,0) */
149 static int packrw_seek( SDL_RWops *rw, int offset, int whence );
150 static int packrw_read( SDL_RWops *rw, void *ptr, int size, int maxnum );
151 static int packrw_write( SDL_RWops *rw, const void *ptr, int size, int num );
152 #endif /* SDL_VERSION_ATLEAST(1,3,0) */
153 static int packrw_close( SDL_RWops *rw );
157 * @brief Opens a Packfile as a cache.
159 * @param packfile Name of the packfile to cache.
160 * @return NULL if an error occured or the Packcache.
162 Packcache_t* pack_openCache( const char* packfile )
164 int j;
165 uint32_t i;
166 char buf[PATH_MAX];
167 Packcache_t *cache;
168 uint64_t end64;
171 * Allocate memory.
173 cache = calloc(1, sizeof(Packcache_t));
174 if (cache == NULL) {
175 WARN("Out of Memory.");
176 return NULL;
180 * Open file.
182 cache->name = strdup(packfile);
183 #if HAS_FD
184 cache->fd = open( packfile, O_RDONLY );
185 if (cache->fd == -1) {
186 #else /* not HAS_FD */
187 cache->fp = fopen( packfile, "rb" );
188 if (cache->fp == NULL) {
189 #endif /* HAS_FD */
190 WARN("Erroring opening %s: %s", packfile, strerror(errno));
191 return NULL;
195 * Check for validity.
197 READ( cache, buf, sizeof(magic));
198 end64 = ntohll(magic);
199 if (memcmp(buf, &end64, sizeof(magic))) {
200 WARN("File %s is not a valid packfile", packfile);
201 return NULL;
205 * Get number of files and allocate memory.
207 READ( cache, &cache->nindex, 4 );
208 cache->nindex = htonl( cache->nindex );
209 cache->index = calloc( cache->nindex, sizeof(char*) );
210 cache->start = calloc( cache->nindex, sizeof(uint32_t) );
213 * Read index.
215 for (i=0; i<cache->nindex; i++) { /* start to search files */
216 j = 0;
217 READ( cache, &buf[j], 1 ); /* get the name */
218 while ( buf[j++] != '\0' )
219 READ( cache, &buf[j], 1 );
221 cache->index[i] = strdup(buf);
222 READ( cache, &cache->start[i], 4 );
223 cache->start[i] = htonl( cache->start[i] );
224 DEBUG("'%s' found at %d", cache->index[i], cache->start[i]);
228 * Return the built cache.
230 return cache;
235 * @brief Closes a Packcache.
237 * @param cache Packcache to close.
239 void pack_closeCache( Packcache_t* cache )
241 uint32_t i;
244 * Close file.
246 #if HAS_FD
247 close( cache->fd );
248 #else /* not HAS_FD */
249 fclose( cache->fp );
250 #endif /* HAS_FD */
251 free( cache->name );
254 * Free memory.
256 if (cache->nindex > 0) {
257 for (i=0; i<cache->nindex; i++)
258 free(cache->index[i]);
259 free(cache->index);
260 free(cache->start);
262 free(cache);
267 * @brief Opens a Packfile from a Packcache.
269 * @param cache Packcache to create Packfile from.
270 * @param filename Name of the file to open in the Cache.
271 * @return A packfile for filename from cache.
273 Packfile_t* pack_openFromCache( Packcache_t* cache, const char* filename )
275 uint32_t i;
276 Packfile_t *file;
278 file = calloc( 1, sizeof(Packfile_t) );
280 for (i=0; i<cache->nindex; i++) {
281 if (strcmp(cache->index[i], filename)==0) {
282 /* Copy file. */
283 #if HAS_FD
284 file->fd = open( cache->name, O_RDONLY );
285 #else /* not HAS_FD */
286 file->fp = fopen( cache->name, "rb" );
287 #endif /* HAS_FD */
289 /* Copy information. */
290 file->flags |= PACKFILE_FROMCACHE;
291 file->start = cache->start[i];
293 /* Seek. */
294 if (file->start) { /* go to the beginning of the file */
295 #if HAS_FD
296 if ((uint32_t)lseek( file->fd, file->start, SEEK_SET ) != file->start) {
297 #else /* not HAS_FD */
298 if (fseek( file->fp, file->start, SEEK_SET )) {
299 #endif /* HAS_FD */
300 WARN("Failure to seek to file start: %s", strerror(errno));
301 return NULL;
303 READ( file, &file->end, 4 );
304 file->end = htonl( file->end );
305 file->start += 4;
306 file->pos = file->start;
307 file->end += file->start;
308 DEBUG("Opened '%s' from cache from %u to %u (%u long)", filename,
309 file->start, file->end, file->end - file->start);
312 return file;
316 free(file);
317 WARN("File '%s' not found in packfile.", filename);
318 return NULL;
323 * @brief Gets the file's size.
325 * @param filename File to get the size of.
326 * @return The size of the file.
328 static off_t getfilesize( const char* filename )
330 #if HAS_FD
331 struct stat file;
333 if (!stat( filename, &file ))
334 return file.st_size;
336 WARN( "Unable to get filesize of %s", filename );
337 return 0;
338 #else /* not HAS_FD */
339 long size;
340 FILE* fp = fopen( filename, "rb" );
341 if (fp == NULL) return 0;
343 fseek( fp, 0, SEEK_END );
344 size = ftell(fp);
346 fclose(fp);
348 return size;
349 #endif /* HAS_FD */
354 * @brief Checks to see if a file is a packfile.
356 * @param filename Name of the file to check.
357 * @return 0 if it is a packfile, 1 if it isn't and -1 on error.
359 int pack_check( const char* filename )
361 int ret;
362 char *buf;
363 uint64_t end64;
365 buf = malloc(sizeof(magic));
367 /* Must convert magic. */
368 end64 = ntohll(magic);
370 #if HAS_FD
371 int fd = open( filename, O_RDONLY );
372 if (fd == -1) {
373 WARN("Erroring opening %s: %s", filename, strerror(errno));
374 return -1;
377 if (read( fd, buf, sizeof(magic) ) != sizeof(magic)) {
378 WARN("Error reading magic number: %s", strerror(errno));
379 free(buf);
380 return -1;
383 close(fd);
384 #else /* not HAS_FD */
385 FILE* file = fopen( filename, "rb" );
386 if (file == NULL) {
387 WARN("Erroring opening '%s': %s", filename, strerror(errno));
388 return -1;
391 if (fread( buf, 1, sizeof(magic), file ) != sizeof(magic)) {
392 WARN("Error reading magic number: %s", strerror(errno));
393 free(buf);
394 return -1;
397 fclose( file );
398 #endif /* HAS_FD */
400 /* Compare. */
401 ret = (memcmp(buf,&end64,sizeof(magic))==0) ? 0 : 1 ;
403 free(buf);
405 return ret;
409 #if HAS_FD
410 #define WRITE(b,n) if (write(outfd,b,n)==-1) { \
411 WARN("Error writing to file: %s", strerror(errno)); \
412 free(buf); return -1; } /**< Macro to help check for errors. */
413 #else /* not HAS_FD */
414 #define WRITE(b,n) if (fwrite(b,1,n,outf)==0) { \
415 WARN("Error writing to file: %s", strerror(errno)); \
416 free(buf); return -1; } /**< Macro to help check for errors. */
417 #endif /* HAS_FD */
419 * @brief Packages files into a packfile.
421 * @param outfile Name of the file to output to.
422 * @param infiles Array of filenames to package.
423 * @param nfiles Number of filenames in infiles.
424 * @return 0 on success.
426 int pack_files( const char* outfile, const char** infiles, const uint32_t nfiles )
428 void *buf;
429 #if HAS_FD
430 struct stat file;
431 int outfd, infd;
432 #else /* HAS_FD */
433 FILE *outf, *inf;
434 #endif /* HAS_FD */
435 uint32_t i;
436 int namesize;
437 uint32_t indexsize, pointer;
438 int bytes;
439 const uint8_t b = '\0';
440 uint32_t end32;
441 uint64_t end64;
444 for (namesize=0,i=0; i < nfiles; i++) { /* make sure files exist before writing */
445 #if HAS_FD
446 if (stat(infiles[i], &file)) {
447 #else /* not HAS_FD */
448 if (getfilesize(infiles[i]) == 0) {
449 #endif /* HAS_FD */
450 WARN("File %s does not exist", infiles[i]);
451 return -1;
453 if (strlen(infiles[i]) > PATH_MAX) {
454 WARN("Filename '%s' is too long, should be only %d characters",
455 infiles[i], PATH_MAX );
456 return -1;
458 namesize += strlen(infiles[i]);
460 indexsize = (sizeof(magic) + 4 + /* magic number and number of files */
461 namesize + /* total length of file names */
462 (1+4)*nfiles); /* file size and extra end of string char '\0' */
463 DEBUG("Index size is %d", indexsize );
465 /* creates the output file */
466 #if HAS_FD
467 outfd = creat( outfile, PERMS );
468 if (outfd == -1) {
469 #else /* not HAS_FD */
470 outf = fopen( outfile, "wb" );
471 if (outf == NULL) {
472 #endif /* HAS_FD */
473 WARN("Unable to open %s for writing", outfile);
474 return -1;
478 * INDEX
480 buf = malloc(BLOCKSIZE);
481 /* magic number */
482 end64 = htonll(magic);
483 WRITE( &end64, sizeof(magic));
484 DEBUG("Wrote magic number");
485 /* number of files */
486 end32 = htonl(nfiles);
487 WRITE( &end32, sizeof(nfiles));
488 DEBUG("Wrote number of files: %d", nfiles);
489 /* create file dependent index part */
490 pointer = indexsize;
491 for (i=0; i<nfiles; i++) {
492 WRITE( infiles[i], strlen(infiles[i]) );
493 DEBUG("File '%s' at %d", infiles[i], pointer);
494 WRITE( &b, 1 );
495 end32 = htonl(pointer);
496 WRITE( &end32, 4 );
497 pointer += 4 + getfilesize( infiles[i] ) + 16; /* set pointer to be next file pos */
500 * DATA
502 md5_state_t md5;
503 md5_byte_t *md5val = malloc(16);
504 for (i=0; i<nfiles; i++) {
505 bytes = (uint32_t)getfilesize( infiles[i] );
506 end32 = htonl(bytes);
507 WRITE( &end32, 4 ); /* filesize */
508 DEBUG("About to write file '%s' of %d bytes", infiles[i], bytes);
509 md5_init(&md5);
510 #if HAS_FD
511 infd = open( infiles[i], O_RDONLY );
512 while ((bytes = read( infd, buf, BLOCKSIZE ))) {
513 #else /* not HAS_FD */
514 inf = fopen( infiles[i], "rb" );
515 while ((bytes = fread( buf, 1, BLOCKSIZE, inf ))) {
516 #endif /* HAS_FD */
517 WRITE( buf, bytes ); /* data */
518 md5_append( &md5, buf, bytes );
520 md5_finish(&md5, md5val);
521 WRITE( md5val, 16 );
522 #if HAS_FD
523 close(infd);
524 #else /* not HAS_FD */
525 fclose(inf);
526 #endif /* HAS_FD */
527 DEBUG("Wrote file '%s'", infiles[i]);
529 free(md5val);
531 #if HAS_FD
532 close( outfd );
533 #else /* not HAS_FD */
534 fclose( outf );
535 #endif /* HAS_FD */
536 free(buf);
538 DEBUG("Packfile success\n\t%d files\n\t%d bytes", nfiles, (int)getfilesize(outfile));
539 return 0;
541 #undef WRITE
545 * @brief Opens a file in the packfile for reading.
547 * @param packfile Path to the real packfile.
548 * @param filename Name of the file within th. packfile.
549 * @return The newly created packfile or NULL on error.
551 Packfile_t* pack_open( const char* packfile, const char* filename )
553 int j;
554 uint32_t nfiles, i;
555 char buf[PATH_MAX];
556 Packfile_t *file;
557 uint64_t end64;
559 /* Allocate memory. */
560 file = malloc(sizeof(Packfile_t));
561 memset( file, 0, sizeof(Packfile_t) );
563 #if HAS_FD
564 file->fd = open( packfile, O_RDONLY );
565 if (file->fd == -1) {
566 #else /* not HAS_FD */
567 file->fp = fopen( packfile, "rb" );
568 if (file->fp == NULL) {
569 #endif /* HAS_FD */
570 WARN("Erroring opening %s: %s", filename, strerror(errno));
571 return NULL;
574 READ( file, buf, sizeof(magic)); /* make sure it's a packfile */
575 end64 = ntohll(magic);
576 if (memcmp(buf, &end64, sizeof(magic))) {
577 WARN("File %s is not a valid packfile", filename);
578 return NULL;
581 READ( file, &nfiles, 4 );
582 nfiles = htonl(nfiles);
583 for (i=0; i<nfiles; i++) { /* start to search files */
584 j = 0;
585 READ( file, &buf[j], 1 ); /* get the name */
586 while ( buf[j++] != '\0' )
587 READ( file, &buf[j], 1 );
589 if (strcmp(filename, buf)==0) { /* found file */
590 READ( file, &file->start, 4 );
591 file->start = htonl( file->start );
592 DEBUG("'%s' found at %d", filename, file->start);
593 break;
595 #if HAS_FD
596 lseek( file->fd, 4, SEEK_CUR ); /* ignore the file location */
597 #else /* not HAS_FD */
598 fseek( file->fp, 4, SEEK_CUR );
599 #endif /* HAS_FD */
602 if (file->start) { /* go to the beginning of the file */
603 #if HAS_FD
604 if ((uint32_t)lseek( file->fd, file->start, SEEK_SET ) != file->start) {
605 #else /* not HAS_FD */
606 if (fseek( file->fp, file->start, SEEK_SET )) {
607 #endif /* HAS_FD */
608 WARN("Failure to seek to file start: %s", strerror(errno));
609 return NULL;
611 READ( file, &file->end, 4 );
612 file->end = htonl( file->end );
613 DEBUG("\t%d bytes", file->end);
614 file->start += 4;
615 file->pos = file->start;
616 file->end += file->start;
618 else {
619 WARN("File '%s' not found in packfile '%s'", filename, packfile);
620 return NULL;
623 return file;
628 * @brief Reads data from a packfile.
630 * Behaves like FD read.
632 * @param file Opened packfile to read data from.
633 * @param buf Allocated buffer to read into.
634 * @param count Bytes to read.
635 * @return Bytes read or -1 on error.
637 ssize_t pack_read( Packfile_t* file, void* buf, size_t count )
639 int bytes;
641 if ((file->pos + count) > file->end)
642 count = MAX(file->end - file->pos, 0); /* can't go past end */
643 if (count == 0)
644 return 0;
646 #if HAS_FD
647 if ((bytes = read( file->fd, buf, count )) == -1) {
648 #else /* not HAS_FD */
649 if ((bytes = fread( buf, 1, count, file->fp)) == -1) {
650 #endif /* HAS_FD */
651 WARN("Error while reading file: %s", strerror(errno));
652 return -1;
654 file->pos += bytes;
656 DEBUG("Read %d bytes from packfile: offset = %u", bytes, file->pos);
657 DEBUG("start: %u, pos: %u, end: %u -> %d", file->start, file->pos, file->end,
658 bytes );
660 return bytes;
665 * @brief Seeks within a file inside a packfile.
667 * Behaves like lseek/fseek.
669 * @param file File to seek.
670 * @param offset Position to seek to.
671 * @param whence Either SEEK_SET, SEEK_CUR or SEEK_END.
672 * @return The position moved to.
674 off_t pack_seek( Packfile_t* file, off_t offset, int whence)
676 uint32_t base, target, ret;
678 DEBUG("attempting to seek offset: %ld, whence: %d", offset, whence);
680 /* Find where offset is relative to. */
681 switch (whence) {
682 case SEEK_SET:
683 base = file->start;
684 break;
686 case SEEK_CUR:
687 base = file->pos;
688 break;
690 case SEEK_END:
691 base = file->end;
692 break;
694 default:
695 WARN("Whence is not one of SEEK_SET, SEEK_CUR or SEEK_END");
696 return -1;
699 /* Get the target. */
700 target = base + offset;
702 /* Limit checks. */
703 if (target < file->start)
704 return -1;
706 #if HAS_FD
707 ret = lseek( file->fd, target, SEEK_SET );
708 if (ret != target)
709 return -1;
710 #else /* not HAS_FD */
711 ret = fseek( file->fp, target, SEEK_SET );
712 if (ret != 0)
713 return -1;
714 ret = target; /* fseek returns 0. */
715 #endif /* HAS_FD */
717 /* Set the position in the file. */
718 file->pos = ret;
719 DEBUG("start: %u, pos: %u, end: %u -> %u", file->start, file->pos, file->end,
720 file->pos - file->start );
722 return file->pos - file->start;
727 * @brief Gets the current position in the file.
729 * @param file Packfile to get the position from.
730 * @return The current position in the file.
732 long pack_tell( Packfile_t* file )
734 return file->pos - file->start;
739 * @brief Reads a file from a Packfile.
741 static void* pack_readfilePack( Packfile_t *file,
742 const char* filename, uint32_t *filesize )
744 void* buf;
745 char* str;
746 int size, bytes;
748 /* read the entire file */
749 size = file->end - file->start;
750 buf = malloc( size + 1 );
751 if (buf == NULL) {
752 WARN("Unable to allocate %d bytes of memory!", size+1);
753 free(file);
754 return NULL;
756 if ((bytes = pack_read( file, buf, size)) != size) {
757 WARN("Reading '%s' from packfile. Expected %d bytes got %d bytes",
758 filename, size, bytes );
759 free(buf);
760 free(file);
761 return NULL;
763 DEBUG("Read %d bytes from '%s'", bytes, filename );
764 str = buf;
765 str[size] = '\0'; /* append size '\0' for it to validate as a string */
767 /* check the md5 */
768 md5_state_t md5;
769 md5_byte_t *md5val = malloc(16);
770 md5_byte_t *md5fd = malloc(16);
771 md5_init(&md5);
772 md5_append( &md5, buf, bytes );
773 md5_finish(&md5, md5val);
774 #if HAS_FD
775 if ((bytes = read( file->fd, md5fd, 16 )) == -1)
776 #else /* not HAS_FD */
777 if ((bytes = fread( md5fd, 1, 16, file->fp )) == -1)
778 #endif /* HAS_FD */
779 WARN("Failure to read MD5 (Expected %d bytes got %d bytes), continuing anyways...", 16, bytes);
780 else if (memcmp( md5val, md5fd, 16 ))
781 WARN("MD5 gives different value, possible memory corruption, continuing...");
782 free(md5val);
783 free(md5fd);
786 /* cleanup */
787 if (pack_close( file ) == -1) {
788 WARN("Closing packfile");
789 free(file);
790 return NULL;
792 DEBUG("Closed '%s' in packfile", filename );
794 if (filesize)
795 *filesize = size;
796 return buf;
801 * @brief Reads an entire file into memory.
803 * @param packfile Name of the packfile to read frome.
804 * @param filename Name of the packed file to read.
805 * @param filesize Is set to the size of the file.
806 * @return A pointer to the data in the file or NULL if an error occurred.
808 void* pack_readfile( const char* packfile, const char* filename, uint32_t *filesize )
810 Packfile_t* file;
812 /* Initialize size to 0. */
813 if (filesize)
814 *filesize = 0;
816 /* Open the packfile. */
817 file = pack_open( packfile, filename );
818 if (file == NULL) {
819 WARN("Opening packfile '%s'.", packfile);
820 return NULL;
822 DEBUG("Opened file '%s' from '%s'", filename, packfile );
824 return pack_readfilePack( file, filename, filesize );
829 * @brief char** pack_listfiles( const char* packfile, uint32_t* nfiles )
831 * @brief Gets what files are in the packfile.
833 * Each name must be freed individually afterwarsd and the array of names too.
835 * @param packfile Packfile to query it's internal files.
836 * @param nfiles Stores the amount of files in packfile.
837 * @return An array of filenames in packfile.
839 char** pack_listfiles( const char* packfile, uint32_t* nfiles )
841 int j;
842 uint32_t i;
843 Packfile_t file;
844 char** filenames;
845 char* buf = malloc(sizeof(magic));
846 uint64_t end64;
848 *nfiles = 0;
850 #if HAS_FD
851 file.fd = open( packfile, O_RDONLY );
852 if (file.fd == -1) {
853 #else /* not HAS_FD */
854 file.fp = fopen( packfile, "rb" );
855 if (file.fp == NULL) {
856 #endif /* HAS_FD */
857 WARN("Erroring opening %s: %s", packfile, strerror(errno));
858 return NULL;
861 READ( &file, buf, sizeof(magic)); /* make sure it's a packfile */
862 end64 = ntohll( magic );
863 if (memcmp(buf, &end64, sizeof(magic))) {
864 WARN("File %s is not a valid packfile", packfile);
865 return NULL;
868 READ( &file, nfiles, 4 );
869 *nfiles = htonl( *nfiles );
870 filenames = malloc(((*nfiles)+1)*sizeof(char*));
871 for (i=0; i<*nfiles; i++) { /* start to search files */
872 j = 0;
873 filenames[i] = malloc(PATH_MAX*sizeof(char));
874 READ( &file, &filenames[i][j], 1 ); /* get the name */
875 while ( filenames[i][j++] != '\0' )
876 READ( &file, &filenames[i][j], 1 );
877 READ( &file, buf, 4 ); /* skip the location */
879 free(buf);
880 #if HAS_FD
881 close(file.fd);
882 #else /* not HAS_FD */
883 fclose(file.fp);
884 #endif /* HAS_FD */
886 return filenames;
891 * @brief Reads an entire file from the cache.
893 void* pack_readfileCached( Packcache_t* cache, const char* filename, uint32_t *filesize )
895 Packfile_t *file;
897 file = pack_openFromCache( cache, filename );
898 if (file == NULL) {
899 WARN("Unable to create packfile from packcache.");
900 return NULL;
902 return pack_readfilePack( file, filename, filesize );
907 * @brief Gets the list of files en a Packcache.
909 * @param cache Cache to get list of files from.
910 * @param nfiles Number of files in the list.
911 * @return A read only list of files from the pack cache.
913 const char** pack_listfilesCached( Packcache_t* cache, uint32_t* nfiles )
915 *nfiles = cache->nindex;
916 return (const char**) cache->index;
921 * @brief Closes a packfile.
923 * @param file Packfile to close.
924 * @return 0 on success.
926 int pack_close( Packfile_t* file )
928 int i;
930 /* Close files. */
931 #if HAS_FD
932 i = close( file->fd );
933 #else /* not HAS_FD */
934 i = fclose( file->fp );
935 #endif /* HAS_FD */
937 /* Free memory. */
938 free(file);
940 DEBUG("Closing packfile.");
942 return (i) ? -1 : 0 ;
946 #if SDL_VERSION_ATLEAST(1,3,0)
947 static long packrw_seek( SDL_RWops *rw, long offset, int whence )
948 #else /* SDL_VERSION_ATLEAST(1,3,0) */
949 static int packrw_seek( SDL_RWops *rw, int offset, int whence )
950 #endif /* SDL_VERSION_ATLEAST(1,3,0) */
952 int wh;
953 Packfile_t *packfile;
954 packfile = rw->hidden.unknown.data1;
956 if (whence == RW_SEEK_SET)
957 wh = SEEK_SET;
958 else if (whence == RW_SEEK_CUR)
959 wh = SEEK_CUR;
960 else if (whence == RW_SEEK_END)
961 wh = SEEK_END;
962 else
963 return -1;
965 return pack_seek( packfile, offset, wh );
967 #if SDL_VERSION_ATLEAST(1,3,0)
968 static size_t packrw_read( SDL_RWops *rw, void *ptr, size_t size, size_t maxnum )
969 #else /* SDL_VERSION_ATLEAST(1,3,0) */
970 static int packrw_read( SDL_RWops *rw, void *ptr, int size, int maxnum )
971 #endif /* SDL_VERSION_ATLEAST(1,3,0) */
973 ssize_t ret;
974 Packfile_t *packfile;
975 packfile = rw->hidden.unknown.data1;
977 /* Read the data. */
978 ret = pack_read( packfile, ptr, size*maxnum );
980 return ret / size;
982 #if SDL_VERSION_ATLEAST(1,3,0)
983 static size_t packrw_write( SDL_RWops *rw, const void *ptr, size_t size, size_t num )
984 #else /* SDL_VERSION_ATLEAST(1,3,0) */
985 static int packrw_write( SDL_RWops *rw, const void *ptr, int size, int num )
986 #endif /* SDL_VERSION_ATLEAST(1,3,0) */
988 (void) rw;
989 (void) ptr;
990 (void) size;
991 (void) num;
992 return -1;
994 static int packrw_close( SDL_RWops *rw )
996 Packfile_t *packfile;
997 packfile = rw->hidden.unknown.data1;
999 /* Free the data. */
1000 SDL_FreeRW(rw);
1002 return pack_close( packfile );
1007 * @brief Creates a rwops from a packfile.
1009 * @param packfile Packfile to create rwops from.
1010 * @return rwops created from packfile.
1012 static SDL_RWops *pack_rwopsRaw( Packfile_t *packfile )
1014 SDL_RWops *rw;
1016 /* Create the rwops. */
1017 rw = SDL_AllocRW();
1018 if (rw == NULL) {
1019 WARN("Unable to allocate SDL_RWops.");
1020 return NULL;
1023 /* Set the functions. */
1024 rw->seek = packrw_seek;
1025 rw->read = packrw_read;
1026 rw->write = packrw_write;
1027 rw->close = packrw_close;
1029 /* Set the packfile as the hidden data. */
1030 rw->hidden.unknown.data1 = packfile;
1032 return rw;
1037 * @brief Creates an rwops for a file in a packfile.
1039 * @param packfile Packfile to get file from.
1040 * @param filename File within packfile to create rwops from.
1041 * @return SDL_RWops interacting with the file in the packfile.
1043 SDL_RWops *pack_rwops( const char* packfile, const char* filename )
1045 Packfile_t *pack;
1047 /* Open the packfile. */
1048 pack = pack_open( packfile, filename );
1049 if (pack == NULL)
1050 return NULL;
1052 /* Return the rwops. */
1053 return pack_rwopsRaw( pack );
1058 * @brief Creates an rwops for a file in a packcache.
1060 * @param cache Packcache to get file from.
1061 * @param filename File within the cache to create rwops from.
1062 * @return SDL_RWops interacting with the file in the packcache.
1064 SDL_RWops *pack_rwopsCached( Packcache_t* cache, const char* filename )
1066 Packfile_t *packfile;
1068 /* Open the packfile. */
1069 packfile = pack_openFromCache( cache, filename );
1070 if (packfile == NULL)
1071 return NULL;
1073 /* Return the rwops. */
1074 return pack_rwopsRaw( packfile );