diff --git a/src/pack.c b/src/pack.c index e1b2a48..3d2581c 100644 --- a/src/pack.c +++ b/src/pack.c @@ -33,8 +33,6 @@ */ /** - * @struct Packfile - * * @brief Abstracts around packfiles. */ struct Packfile_s { @@ -46,8 +44,36 @@ struct Packfile_s { uint32_t pos; /**< Cursor position. */ uint32_t start; /**< File start. */ uint32_t end; /**< File end. */ + + uint32_t flags; /**< Special control flags. */ }; +/** + * @brief Allows much faster creation of packfiles. + */ +struct Packcache_s { +#ifdef _POSIX_SOURCE + int fd; /**< File descriptor. */ +#else + FILE* fp; /* For non-posix. */ +#endif + + char** index; /**< Cached index for faster lookups. */ + uint32_t* start; /**< Cached index starts. */ + uint32_t* nindex; /**< Number of index entries. */ +}; + +/* Helper defines. */ +#ifdef _POSIX_SOURCE +#define READ(f, b, n) if(read((f)->fd, (b), (n)) != (n)) { \ + ERR("Fewer bytes read then expected"); \ + return NULL; } +#else +#define READ(f, b, n) if(fread((b), 1, (n),(f)->fp) != (n)) { \ + ERR("Fewer bytes read then expected"); \ + return NULL; } +#endif + #undef DEBUG /* This will be spammy. */ #define DEBUG(str, args...) do{;} while(0) @@ -62,9 +88,142 @@ struct Packfile_s { const uint64_t magic = 0x25524573; /**< File magic number: sER% */ +/* Flags. */ +#define PACKFILE_FROMCACHE (1<<0) /**< Packfile comes from a packcache. */ + +static off_t getfilesize(const char* filename); + +/** + * @brief Open a packfile as a cache. + * @param packfile Name of the packfile to cache. + * @return NULL if an error occured or the Packcache. + */ +Packcache_t* pack_openCache(const char* packfile) { + int j; + uint32_t i; + char buf[PATH_MAX]; + Packcache_t* cache; + + /* Allocate memory. */ + cache = calloc(1, sizeof(Packcache_t)); + if(cache == NULL) { + ERR("Out of memory."); + return NULL; + } + + /* Open file. */ +#ifdef _POSIX_SOURCE + cache->fd = open(packfile, O_RDONLY); + if(cache->fd == -1) { +#else + cache->fp = fopen(packfile, "rb"); + if(cache->fp == NULL) { +#endif + ERR("Error opening %s: %s", packfile, strerror(errno)); + return NULL; + } + /* Check for validity. */ + READ(cache, buf, sizeof(magic)); + if(memcmp(buf, &magic, sizeof(magic))) { + ERR("File %s is not a valid packfile", packfile); + return NULL; + } + + /* Get the number of files and allocate memory. */ + READ(cache, &cache->nindex, 4); + cache->index = calloc(cache->nindex, sizeof(char*)); + cache->start = calloc(cache->nindex, sizeof(uint32_t)); + + /* Read index. */ + for(i = 0; i < cache->nindex; i++) { /* Start to search files. */ + j = 0; + READ(cache, &buf[j], 1); /* Get the name. */ + while(buf[j++] != '\0') + READ(cache, &buf[j], 1); + + cache->index[i] = strdup(buf); + READ(cache, &cache->start[i], 4); + DEBUG("'%s' found at %d", filename, cache->start[i]); + break; + } + /* Return the built cache. */ + return cache; +} + +/** + * @brief Close a Packcache. + * @param cache Packcache to close. + */ +void pack_closeCache(Packcache_t* cache) { + uint32_t i; + + /* Close file. */ +#ifdef _POSIX_SOURCE + close(cache->fd); +#else + fclose(cache->fp); +#endif + + /* Free memory. */ + if(cache->nindex > 0) { + for(i = 0; i < cache->nindex; i++) + free(cache->index[i]); + free(cache->index); + free(cache->start); + } +} + +/** + * @brief Open a packfile from a Packcache. + * @param cache Packcache to create Packfile from. + * @param filename Name of the file to open in the Cache. + * @return A packfile for filename from cache. + */ +Packfile_t* pack_openFromCache(Packcache_t* cache, const char* filename) { + uint32_t i; + Packfile_t* file; + + file = calloc(1, sizeof(Packfile_t)); + + for(i = 0; i < cache->nindex; i++) { + if(strcmp(cache->index[i], filename)==0) { + /* Copy file. */ +#ifdef _POSIX_SOURCE + file->fd = dup(cache->fd); +#else + file->fp = cache->fp; +#endif + + /* Copy information. */ + file->flags |= PACKFILE_FROMCACHE; + file->start = cache->start[i]; + + /* Seek. */ + if(file->start) { /* Go to the beginning of the file. */ +#ifdef _POSIX_SOURCE + if((uint32_t)lseek(file->fd, file->start, SEEK_SET) != file->start) { +#else + fseek(file->fp, file->start, SEEK_SET); + if(errno) { +#endif + ERR("Failure to seek to file start: %s", strerror(errno)); + return NULL; + } + + READ(file, &file->end, 4); + DEBUG("\t%d bytes", file->end); + file->pos = file->start; + file->end += file->start; + } + + break; + } + } + + return NULL; +} + /** - * @fn static off_t getfilesize(const char* filename) - * * @brief Get the file size. * @param filename File to get the size of. * @return The size of the file. @@ -269,15 +428,6 @@ int pack_files(const char* outfile, const char** infiles, const uint32_t nfiles) } #undef WRITE -#ifdef _POSIX_SOURCE -#define READ(b,n) if(read(file->fd, (b), (n))!=(n)) { \ - ERR("Fewer bytes read than expected."); \ - free(buf); return NULL; } -#else -#define READ(b,n) if(fread((b), 1, (n), file->fp)!=(n)) { \ - ERR("Fewer bytes read then expected"); \ - free(buf); return NULL; } -#endif /** * @brief Open a file in the packfile for reading. * @param file Packfile to store data into. @@ -288,13 +438,12 @@ int pack_files(const char* outfile, const char** infiles, const uint32_t nfiles) Packfile_t* pack_open(const char* packfile, const char* filename) { int j; uint32_t nfiles, i; - char* buf = malloc(PATH_MAX); + char buf[PATH_MAX]; Packfile_t* file; /* Allocate memory. */ file = malloc(sizeof(Packfile_t)); memset(file, 0, sizeof(Packfile_t)); - buf = malloc(PATH_MAX); #ifdef _POSIX_SOURCE file->fd = open(packfile, O_RDONLY); @@ -307,23 +456,23 @@ Packfile_t* pack_open(const char* packfile, const char* filename) { return NULL; } - READ(buf, sizeof(magic)); /* Make sure it's a packfile. */ + READ(file, buf, sizeof(magic)); /* Make sure it's a packfile. */ if(memcmp(buf, &magic, sizeof(magic))) { ERR("File %s is not a valid packfile", filename); return NULL; } - READ(&nfiles, 4); + READ(file, &nfiles, 4); for(i = 0; i < nfiles; i++) { /* Start to search files. */ j = 0; - READ(&buf[j], 1); /* Get the name. */ + READ(file, &buf[j], 1); /* Get the name. */ while(buf[j++] != '\0') - READ(&buf[j], 1); + READ(file, &buf[j], 1); if(strcmp(filename, buf) == 0) { /* We found the file. */ - READ(&file->start, 4); + READ(file, &file->start, 4); DEBUG("'%s' found at %d", filename, file->start); break; } @@ -333,7 +482,6 @@ Packfile_t* pack_open(const char* packfile, const char* filename) { fseek(file->fp, 4, SEEK_CUR); #endif } - free(buf); if(file->start) { /* Go to the beginning of the file. */ @@ -346,7 +494,7 @@ Packfile_t* pack_open(const char* packfile, const char* filename) { ERR("Failure to seek to file start: %s", strerror(errno)); return NULL; } - READ(&file->end, 4); + READ(file, &file->end, 4); DEBUG("\t%d bytes", file->end); file->pos = file->start; file->end += file->start; @@ -356,7 +504,6 @@ Packfile_t* pack_open(const char* packfile, const char* filename) { } return file; } -#undef READ /** * @brief Reads data from a packfile. @@ -368,12 +515,12 @@ Packfile_t* pack_open(const char* packfile, const char* filename) { * @return Bytes to read or -1 on error. */ ssize_t pack_read(Packfile_t* file, void* buf, size_t count) { + int bytes; + if(file->pos + count > file->end) count = file->end - file->pos; /* Can't go past the end! */ if(count == 0) return 0; - int bytes; - #ifdef _POSIX_SOURCE if((bytes = read(file->fd, buf, count)) == -1) { #else @@ -538,66 +685,52 @@ void* pack_readfile(const char* packfile, const char* filename, uint32_t* filesi * @param nfiles Stores the amount of files in packfile. * @return An array of filenames in packfile. */ -#ifdef _POSIX_SOURCE -#define READ(b,n) if(read(fd, (b), (n))!=(n)) { \ - ERR("Too few bytes read"); \ - return NULL; } -#else -#define READ(b,n) if(fread((b), 1, (n), fp) != (n)) { \ - ERR("Too few bytes read"); \ - return NULL; } -#endif char** pack_listfiles(const char* packfile, uint32_t* nfiles) { -#ifdef _POSIX_SOURCE - int fd; -#else - FILE* fp; -#endif int j; uint32_t i; + Packfile_t file; char** filenames; char* buf = malloc(sizeof(magic)); *nfiles = 0; #ifdef _POSIX_SOURCE - fd = open(packfile, O_RDONLY); - if(fd == -1) { + file.fd = open(packfile, O_RDONLY); + if(file.fd == -1) { #else - fp = fopen(packfile, "rb"); - if(fp == NULL) { + file.fp = fopen(packfile, "rb"); + if(file.fp == NULL) { #endif ERR("opening %s: %s", packfile, strerror(errno)); return NULL; } - READ(buf, sizeof(magic)); /* Make sure it is a packfile. */ + READ(&file, buf, sizeof(magic)); /* Make sure it's a packfile. */ if(memcmp(buf, &magic, sizeof(magic))) { ERR("File %s is not a valid packfile", packfile); return NULL; } - READ(nfiles, 4); + READ(&file, nfiles, 4); filenames = malloc(((*nfiles)+1)*sizeof(char*)); for(i = 0; i < *nfiles; i++) { /* Start searching files. */ j = 0; filenames[i] = malloc(PATH_MAX * sizeof(char)); - READ(&filenames[i][j], 1); /* Get the name. */ + READ(&file, &filenames[i][j], 1); /* Get the name. */ while(filenames[i][j++] != '\0') - READ(&filenames[i][j], 1); - READ(buf, 4); /* skip the location. */ + READ(&file, &filenames[i][j], 1); + READ(&file, buf, 4); /* skip the location. */ } free(buf); #ifdef _POSIX_SOURCE - close(fd); + close(file.fd); #else - fclose(fp); + fclose(file.fp); #endif return filenames; } -#undef READ /** * @brief Closes a packfile. @@ -609,7 +742,10 @@ int pack_close(Packfile_t* file) { #ifdef _POSIX_SOURCE i = close(file->fd); #else - i = fclose(file->fp); + if(file->flags & PACKFILE_FROMCACHE) + i = 0; + else + i = fclose(file->fp); #endif return (i) ? -1 : 0; } diff --git a/src/pack.h b/src/pack.h index 2be5f36..22e7372 100644 --- a/src/pack.h +++ b/src/pack.h @@ -9,6 +9,16 @@ struct Packfile_s; typedef struct Packfile_s Packfile_t; +struct Packcache_s; +typedef struct Packcache_s Packcache_t; + +/* + * Packcache manipulation. + */ +Packcache_t* pack_openCache(const char* packfile); +void pack_closeCache(Packcache_t* cache); +Packfile_t* pack_openFromCache(Packcache_t* cache, const char* filename); + /* Packfile manipulation. Automatically allocated and freed (with open and close). */ /* Basic. */