diff options
Diffstat (limited to 'src/cc4group.c')
| -rw-r--r-- | src/cc4group.c | 476 |
1 files changed, 362 insertions, 114 deletions
diff --git a/src/cc4group.c b/src/cc4group.c index 5b3e888..500d377 100644 --- a/src/cc4group.c +++ b/src/cc4group.c @@ -61,6 +61,8 @@ typedef struct C4GroupEntryData_t { struct list_GroupEntryList* children; struct C4GroupEntryData_t* parent; + + size_t absolutePosition; } C4GroupEntryData; LIST_AUTO(C4GroupEntryData, GroupEntryList) @@ -95,6 +97,23 @@ struct CC4Group_t { size_t referenceCounter; CC4Group* parent; + + struct { + struct { + CC4Group_ReadCallback read; + CC4Group_ReadSetupCallback setup; + CC4Group_ReadSetupCallback deinit; + CC4Group_MemoryManagement memoryManagement; + void* arg; + } callback; + bool active; + size_t position; + z_stream gzStrm; + const uint8_t* lastData; + CleanUpJobList* cleanupJobs; + } readState; + + bool lazy; }; typedef struct { @@ -109,7 +128,7 @@ typedef struct { static bool cc4group_applyMemoryManagementStart(CC4Group_MemoryManagement const management, const uint8_t** data, size_t size) { - void* newData = management->start((void*)*data, size, management->arg); + void* newData = management->start(data == NULL ? NULL : (void*)*data, size, management->arg); if(newData == NULL) { return false; @@ -223,10 +242,48 @@ static void memScrambleHeader(uint8_t* const data) } } +static bool cc4group_checkHeader(CC4Group* const this, C4GroupHeader* const header) +{ + if(header->Ver1 != 1 || header->Ver2 > 2) + { + SET_MESSAGE_ERROR("Unsupported group version. Only versions 1.0 up to 1.2 are supported."); + return false; + } + + if(memcmp(C4GroupId, header->id, sizeof(header->id)) != 0) + { + SET_MALFORMED_MESSAGE_ERROR("The group id does not match."); + return false; + } + + // make sure the maker is null terminated + header->Maker[C4GroupMaxMaker + 1] = '\0'; + + return true; +} + static bool buildChildren(CC4Group* const this, C4GroupEntryData* const entry, size_t const dataSize) { + // only if it's read already + if(this->readState.position < entry->absolutePosition + sizeof(C4GroupHeader)) + { + entry->header = NULL; + return true; + } + + if(!cc4group_checkHeader(this, entry->header)) + { + return false; + } C4GroupHeader* header = entry->header; + // only if it's read already + if(this->readState.position < entry->absolutePosition + sizeof(C4GroupHeader) + sizeof(C4GroupEntryCore) * header->Entries) + { + entry->children = NULL; + return true; + } + entry->children = GroupEntryListNew(); uint8_t* data = entry->data + sizeof(C4GroupHeader); @@ -243,9 +300,14 @@ static bool buildChildren(CC4Group* const this, C4GroupEntryData* const entry, s return false; } - C4GroupEntryData* childEntry = &GroupEntryListAppend(entry->children, (C4GroupEntryData){.core = *core, .data = childData + core->Offset, .memoryManagement = cc4group.MemoryManagement.Reference, .children = NULL, .parent = entry})->value; + C4GroupEntryData* childEntry = &GroupEntryListAppend(entry->children, (C4GroupEntryData){.core = *core, .data = childData + core->Offset, .memoryManagement = cc4group.MemoryManagement.Reference, .children = NULL, .parent = entry, .absolutePosition = entry->absolutePosition + childDataOffset + core->Offset})->value; + + if(this->readState.position < childEntry->absolutePosition + sizeof(C4GroupHeader)) + { + childEntry->data = NULL; - if(core->Directory) + } + else if(core->Directory) { memScrambleHeader(childEntry->data); if(!buildChildren(this, childEntry, core->Size)) @@ -424,6 +486,7 @@ static CC4Group_TmpMemoryStrategy cc4group_tmpMemoryStrategy = cc4group_createTm static bool cc4group_buildGroupHierarchy(CC4Group* const this) { this->root.data = this->uncompressedData; + this->root.absolutePosition = 0; bool ret = buildChildren(this, &this->root, this->uncompressedSize); this->root.core.Directory = 1; this->root.core.FileName[0] = '\0'; @@ -503,6 +566,77 @@ static bool cc4group_inflateFillOutput(z_stream* const strm, CC4Group_ReadCallba return false; } +static void cc4group_setLazy(CC4Group* const this, const bool lazy) +{ + assert(this->root.children == NULL); + + this->lazy = lazy; +} + +static void cc4group_cleanupReadState(CC4Group* const this) +{ + ForeachCleanupJob(this->readState.cleanupJobs) + { + job->value.func(job->value.data); + } + this->readState.cleanupJobs = NULL; + + if(this->readState.lastData != NULL && this->lazy) + { + cc4group_applyMemoryManagementEnd(this->readState.callback.memoryManagement, this->readState.lastData); + } + + // indicate end of prolonged use (for reference counting) + cc4group_applyMemoryManagementEnd(this->readState.callback.memoryManagement, NULL); + + if(this->readState.callback.deinit != NULL) + { + if(!this->readState.callback.deinit(this->readState.callback.arg)) + { + fprintf(stderr, "WARNING: cc4group_cleanupReadState: the deinitCallback failed\n"); + } + } + + inflateEnd(&this->readState.gzStrm); + + this->readState.active = false; +} + +static bool cc4group_uncompressUntilPosition(CC4Group* const this, size_t const position) +{ + CC4Group* rootGroup = this; + while(rootGroup->parent != NULL) + { + rootGroup = rootGroup->parent; + } + + assert(rootGroup); + assert(rootGroup->readState.active); + assert(position <= rootGroup->uncompressedSize); + + if(rootGroup->readState.position < position) + { + rootGroup->readState.gzStrm.avail_out = position - rootGroup->readState.position; + + bool eof = false; + int inflateRet; + if(!cc4group_inflateFillOutput(&rootGroup->readState.gzStrm, rootGroup->readState.callback.read, rootGroup->readState.callback.arg, rootGroup->readState.callback.memoryManagement, &eof, &rootGroup->readState.lastData, &inflateRet)) + { + SET_ZERROR_ERROR("inflate: inflating the group contents", inflateRet); + cc4group_cleanupReadState(rootGroup); + return false; + } + + if(position == rootGroup->uncompressedSize) + { + cc4group_cleanupReadState(rootGroup); + } + + rootGroup->readState.position = position; + } + return true; +} + static bool cc4group_uncompressGroup(CC4Group* const this, CC4Group_ReadCallback const readCallback, void* const callbackArg, CC4Group_MemoryManagement const memoryManagement, CC4Group_ReadSetupCallback const initCallback, CC4Group_ReadSetupCallback const deinitCallback) { assert(this); @@ -531,7 +665,6 @@ static bool cc4group_uncompressGroup(CC4Group* const this, CC4Group_ReadCallback size_t totalReadSize = 0; - const uint8_t* readData = NULL; const uint8_t* readDataAfterMagic = NULL; size_t readSize = 0; bool eof = false; @@ -540,21 +673,21 @@ static bool cc4group_uncompressGroup(CC4Group* const this, CC4Group_ReadCallback while(!eof && totalReadSize < 2) { - if(readData != NULL) + if(this->readState.lastData != NULL) { - cc4group_applyMemoryManagementEnd(memoryManagement, readData); + cc4group_applyMemoryManagementEnd(memoryManagement, this->readState.lastData); } - readData = NULL; + this->readState.lastData = NULL; readSize = 0; - eof = readCallback((const void**)&readData, &readSize, callbackArg); + eof = readCallback((const void**)&this->readState.lastData, &readSize, callbackArg); if(readSize == 0) { continue; } - if(!cc4group_applyMemoryManagementStart(memoryManagement, &readData, readSize)) + if(!cc4group_applyMemoryManagementStart(memoryManagement, &this->readState.lastData, readSize)) { SET_ZERROR_ERROR("reading the group magic", CC4GROUP_MEM_ERROR); goto ret; @@ -563,13 +696,13 @@ static bool cc4group_uncompressGroup(CC4Group* const this, CC4Group_ReadCallback size_t newTotalReadSize = totalReadSize + readSize; if(totalReadSize < 1 && newTotalReadSize > 0) { - magic1 = readData[0]; + magic1 = this->readState.lastData[0]; } if(totalReadSize < 2 && newTotalReadSize > 1) { - magic2 = readData[1 - totalReadSize]; - readDataAfterMagic = readData + (2 - totalReadSize); + magic2 = this->readState.lastData[1 - totalReadSize]; + readDataAfterMagic = this->readState.lastData + (2 - totalReadSize); } totalReadSize = newTotalReadSize; @@ -589,15 +722,13 @@ static bool cc4group_uncompressGroup(CC4Group* const this, CC4Group_ReadCallback goto ret; } - z_stream strm = { - .zalloc = NULL, - .zfree = NULL, - .opaque = NULL, - .next_in = magic, - .avail_in = 2 - }; + this->readState.gzStrm.zalloc = NULL; + this->readState.gzStrm.zfree = NULL; + this->readState.gzStrm.opaque = NULL; + this->readState.gzStrm.next_in = magic; + this->readState.gzStrm.avail_in = 2; - int ret = inflateInit2(&strm, 15 + 16); // window size 15 + automatic gzip + int ret = inflateInit2(&this->readState.gzStrm, 15 + 16); // window size 15 + automatic gzip inflateStarted = true; if(ret != Z_OK) @@ -615,11 +746,11 @@ static bool cc4group_uncompressGroup(CC4Group* const this, CC4Group_ReadCallback goto ret; } - strm.next_out = (Bytef*)header; - strm.avail_out = currentSize; + this->readState.gzStrm.next_out = (Bytef*)header; + this->readState.gzStrm.avail_out = currentSize; - ret = inflate(&strm, Z_SYNC_FLUSH); - if(ret != Z_OK && (ret != Z_STREAM_END || strm.avail_in != 0)) + ret = inflate(&this->readState.gzStrm, Z_SYNC_FLUSH); + if(ret != Z_OK && (ret != Z_STREAM_END || this->readState.gzStrm.avail_in != 0)) { SET_ZERROR_ERROR("inflate: inflating the magic", ret); goto ret; @@ -627,11 +758,11 @@ static bool cc4group_uncompressGroup(CC4Group* const this, CC4Group_ReadCallback if(totalReadSize > 2) { - strm.next_in = (uint8_t*)readDataAfterMagic; - strm.avail_in = totalReadSize - 2; + this->readState.gzStrm.next_in = (uint8_t*)readDataAfterMagic; + this->readState.gzStrm.avail_in = totalReadSize - 2; } - if(!cc4group_inflateFillOutput(&strm, readCallback, callbackArg, memoryManagement, &eof, &readData, &ret)) + if(!cc4group_inflateFillOutput(&this->readState.gzStrm, readCallback, callbackArg, memoryManagement, &eof, &this->readState.lastData, &ret)) { SET_ZERROR_ERROR("inflate: inflating the group header", ret); goto ret; @@ -643,7 +774,7 @@ static bool cc4group_uncompressGroup(CC4Group* const this, CC4Group_ReadCallback { // the group is empty // so the stream should have ended also - if(ret != Z_STREAM_END || !eof || strm.avail_in > 0) + if(ret != Z_STREAM_END || !eof || this->readState.gzStrm.avail_in > 0) { SET_MALFORMED_MESSAGE_ERROR("The group is empty but the gzip stream has not ended yet."); goto ret; @@ -655,21 +786,11 @@ static bool cc4group_uncompressGroup(CC4Group* const this, CC4Group_ReadCallback goto ret; } - if(header->Ver1 != 1 || header->Ver2 > 2) + if(!cc4group_checkHeader(this, header)) { - SET_MESSAGE_ERROR("Unsupported group version. Only versions 1.0 up to 1.2 are supported."); goto ret; } - if(memcmp(C4GroupId, header->id, sizeof(header->id)) != 0) - { - SET_MALFORMED_MESSAGE_ERROR("The group id does not match."); - goto ret; - } - - // make sure the maker is null terminated - header->Maker[C4GroupMaxMaker + 1] = '\0'; - currentSize += sizeof(C4GroupEntryCore) * header->Entries; header = realloc(header, currentSize); @@ -681,10 +802,10 @@ static bool cc4group_uncompressGroup(CC4Group* const this, CC4Group_ReadCallback C4GroupEntryCore* cores = (C4GroupEntryCore*)((uint8_t*)(header) + sizeof(C4GroupHeader)); - strm.next_out = (Bytef*)cores; - strm.avail_out = sizeof(C4GroupEntryCore) * header->Entries; + this->readState.gzStrm.next_out = (Bytef*)cores; + this->readState.gzStrm.avail_out = sizeof(C4GroupEntryCore) * header->Entries; - if(!cc4group_inflateFillOutput(&strm, readCallback, callbackArg, memoryManagement, &eof, &readData, &ret)) + if(!cc4group_inflateFillOutput(&this->readState.gzStrm, readCallback, callbackArg, memoryManagement, &eof, &this->readState.lastData, &ret)) { SET_ZERROR_ERROR("inflate: inflating the group entry dictionary", ret); goto ret; @@ -712,55 +833,82 @@ static bool cc4group_uncompressGroup(CC4Group* const this, CC4Group_ReadCallback // write alredy decompressed header and cores into the new tmp memory memcpy(uncompressedData, header, data - uncompressedData); - strm.next_out = data; - strm.avail_out = uncompressedSize; - - if(!cc4group_inflateFillOutput(&strm, readCallback, callbackArg, memoryManagement, &eof, &readData, &ret)) - { - SET_ZERROR_ERROR("inflate: inflating the group contents", ret); - goto ret; - } + this->readState.gzStrm.next_out = data; + this->readState.gzStrm.avail_out = uncompressedSize; - if(strm.avail_in > 0) + if(!this->lazy) { - // NOTE: This may miss additional garbage data at the end if the read callback happens to read exactly the right amount of data - SET_MALFORMED_MESSAGE_ERROR("The group contents are read completely but more data is left to read"); - goto ret; - } - - retData = uncompressedData; + if(!cc4group_inflateFillOutput(&this->readState.gzStrm, readCallback, callbackArg, memoryManagement, &eof, &this->readState.lastData, &ret)) + { + SET_ZERROR_ERROR("inflate: inflating the group contents", ret); + goto ret; + } -ret: - if(deinitCallback != NULL) - { - if(!deinitCallback(callbackArg)) + if(this->readState.gzStrm.avail_in > 0) { - fprintf(stderr, "WARNING: cc4group_uncompressGroup: the deinitCallback failed\n"); + // NOTE: This may miss additional garbage data at the end if the read callback happens to read exactly the right amount of data + SET_MALFORMED_MESSAGE_ERROR("The group contents are read completely but more data is left to read"); + goto ret; } } - if(readData != NULL) - { - cc4group_applyMemoryManagementEnd(memoryManagement, readData); - } + retData = uncompressedData; +ret: if(header != NULL) { free(header); } - if(inflateStarted) + if(retData == NULL) { - inflateEnd(&strm); - } + if(deinitCallback != NULL) + { + if(!deinitCallback(callbackArg)) + { + fprintf(stderr, "WARNING: cc4group_uncompressGroup: the deinitCallback failed\n"); + } + } - if(retData == NULL && uncompressedData != NULL) - { - tmpCleanup.func(tmpCleanup.data); + if(this->readState.lastData != NULL) + { + cc4group_applyMemoryManagementEnd(memoryManagement, this->readState.lastData); + } + + if(inflateStarted) + { + inflateEnd(&this->readState.gzStrm); + } + + if(uncompressedData != NULL) + { + tmpCleanup.func(tmpCleanup.data); + } } - else if(uncompressedData != NULL) + else { - CleanUpJobListPrepend(this->cleanupJobs, tmpCleanup); + if(uncompressedData != NULL) + { + CleanUpJobListPrepend(this->cleanupJobs, tmpCleanup); + } + + this->readState.position = this->readState.gzStrm.next_out - retData; + this->readState.callback.setup = initCallback; + this->readState.callback.read = readCallback; + this->readState.callback.deinit = deinitCallback; + this->readState.callback.arg = callbackArg; + this->readState.callback.memoryManagement = memoryManagement; + if(this->lazy) + { + this->readState.active = true; + + // indicate prolonged use (for reference counting) + cc4group_applyMemoryManagementStart(memoryManagement, NULL, 0); + } + else + { + cc4group_cleanupReadState(this); + } } this->uncompressedData = retData; @@ -808,14 +956,20 @@ static bool cc4group_initChunkBufferCallback(void* callbackArg) static bool cc4group_deinitChunkBufferCallback(void* callbackArg) { free(((ChunkedReadData*)callbackArg)->buffer); + free(callbackArg); return true; } +typedef struct { + ChunkedReadData chunkedData; + int fd; +} FdReadData; + static bool cc4group_readFdReadCallback(const void** const data, size_t* const size, void* callbackArg) { - ChunkedReadData* arg = callbackArg; - void* buffer = arg->buffer; - ssize_t count = read(*(int*)arg->arg, buffer, CHUNK_SIZE); + FdReadData* arg = callbackArg; + void* buffer = arg->chunkedData.buffer; + ssize_t count = read(arg->fd, buffer, CHUNK_SIZE); if(count > 0) { @@ -856,21 +1010,33 @@ static bool cc4group_setSubRoot(CC4Group* const this, const char* const subPath) { if(subPath != NULL && *subPath != '\0') { - this->realRoot = this->root; - const C4GroupEntryData* subRoot = cc4group_getDirectoryByPath(this, subPath, false); - if(subRoot == NULL) + if(subRoot == NULL || cc4group_getChildren(this, subRoot) == NULL) { return false; } + this->realRoot = this->root; this->root = *subRoot; } return true; } +static bool cc4group_cleanupMappedGroupFile(void* const arg) +{ + CompleteDataReadCallbackArg* data = arg; + if(cc4group_munmap((void*)data->data, data->size) == -1) + { + fprintf(stderr, "WARNING: munmap: Unmapping the group file failed: %s\n", strerror(errno)); + } + + free(data); + + return true; +} + static bool cc4group_uncompressGroupFromFile(CC4Group* const this, const char* const path) { uint8_t* mappedFile = MAP_FAILED; @@ -946,8 +1112,17 @@ static bool cc4group_uncompressGroupFromFile(CC4Group* const this, const char* c goto ret; } - CompleteDataReadCallbackArg data = {.data = mappedFile, .size = size}; - success = cc4group_uncompressGroup(this, cc4group_completeDataReadCallback, &data, cc4group.MemoryManagement.Reference, NULL, NULL); + CompleteDataReadCallbackArg* data = malloc(sizeof(CompleteDataReadCallbackArg)); + + if(data == NULL) + { + SET_ERRNO_ERROR("malloc: allocating memory for cleanup data failed"); + goto ret; + } + + *data = (CompleteDataReadCallbackArg){.data = mappedFile, .size = size}; + success = cc4group_uncompressGroup(this, cc4group_completeDataReadCallback, data, cc4group.MemoryManagement.Reference, NULL, cc4group_cleanupMappedGroupFile); + mappedFile = MAP_FAILED; ret: if(mappedFile != MAP_FAILED) @@ -974,6 +1149,7 @@ static void cc4group_init(CC4Group* const this) this->referenceCounter = 1; this->parent = NULL; + this->lazy = true; this->uncompressedData = NULL; this->uncompressedSize = 0; @@ -991,6 +1167,11 @@ static void cc4group_init(CC4Group* const this) this->error.formatter.data = NULL; this->error.formatter.formatter = cc4group_noerrorFormatter; this->error.lastFormattedMessage = NULL; + + this->readState.active = false; + this->readState.lastData = NULL; + this->readState.cleanupJobs = CleanUpJobListNew(); + AddCleanUpJob(CleanUpJobListDestroy, this->readState.cleanupJobs); } static bool cc4group_setMakerRecursively(CC4Group* const this, const C4GroupEntryData* groupEntry, const char* const maker) @@ -1210,8 +1391,15 @@ static bool cc4group_openFd(CC4Group* const this, int fd) { // assert is in cc4group_uncompressGroup - ChunkedReadData arg = {.arg = &fd}; - return cc4group_uncompressGroup(this, cc4group_readFdReadCallback, &arg, cc4group.MemoryManagement.Reference, cc4group_initChunkBufferCallback, cc4group_deinitChunkBufferCallback); + FdReadData* arg = malloc(sizeof(FdReadData)); + if(arg == NULL) + { + SET_ERRNO_ERROR("malloc: Allocating memory for temporary data"); + return false; + } + + arg->fd = fd; + return cc4group_uncompressGroup(this, cc4group_readFdReadCallback, arg, cc4group.MemoryManagement.Reference, cc4group_initChunkBufferCallback, cc4group_deinitChunkBufferCallback); } static bool cc4group_openFilePointer(CC4Group* const this, FILE* file) @@ -1219,8 +1407,15 @@ static bool cc4group_openFilePointer(CC4Group* const this, FILE* file) // assert(this) is in cc4group_uncompressGroup assert(file); - ChunkedReadData arg = {.arg = file}; - return cc4group_uncompressGroup(this, cc4group_readFilePointerReadCallback, &arg, cc4group.MemoryManagement.Reference, cc4group_initChunkBufferCallback, cc4group_deinitChunkBufferCallback); + ChunkedReadData* arg = malloc(sizeof(ChunkedReadData)); + if(arg == NULL) + { + SET_ERRNO_ERROR("malloc: Allocating memory for temporary data"); + return false; + } + arg->arg = file; + + return cc4group_uncompressGroup(this, cc4group_readFilePointerReadCallback, arg, cc4group.MemoryManagement.Reference, cc4group_initChunkBufferCallback, cc4group_deinitChunkBufferCallback); } static bool cc4group_openExisting(CC4Group* const this, const char* const path) @@ -1251,6 +1446,11 @@ static void cc4group_delete(CC4Group* const this) { assert(this); + if(this->readState.active) + { + cc4group_cleanupReadState(this); + } + ForeachCleanupJob(this->cleanupJobs) { job->value.func(job->value.data); @@ -1331,7 +1531,6 @@ static bool cc4group_extractChildren(CC4Group* const this, const C4GroupEntryDat if(root->core.Directory) { - assert(root->children); if(cc4group_mkdir(tmpPath, 0755) == -1 /*&& errno != EEXIST*/) { SET_ERRNO_ERROR("mkdir: Creating target directory"); @@ -1547,28 +1746,63 @@ static bool cc4group_extractSingle(CC4Group* const this, const char* const entry static const uint8_t* cc4group_getOnlyEntryData(CC4Group* const this, const C4GroupEntryData* entry) { - // TODO: lazy - (void)this; + if(entry->data == NULL) + { + if(!cc4group_uncompressUntilPosition(this, entry->absolutePosition + entry->core.Size)) + { + return NULL; + } + + ((C4GroupEntryData*)entry)->data = this->uncompressedData + entry->absolutePosition; + } return entry->data; } -static GroupEntryList* cc4group_getChildren(CC4Group* const this, const C4GroupEntryData* entry) +static C4GroupHeader* cc4group_getHeader(CC4Group* const this, const C4GroupEntryData* entry) { - // TODO: lazy - (void)this; + assert(entry->core.Directory); - return entry->children; + if(entry->header == NULL) + { + if(!cc4group_uncompressUntilPosition(this, entry->absolutePosition + sizeof(C4GroupHeader))) + { + return NULL; + } + + ((C4GroupEntryData*)entry)->data = this->uncompressedData + entry->absolutePosition; + + memScrambleHeader(entry->data); + if(!cc4group_checkHeader(this, entry->header)) + { + return NULL; + } + } + + return entry->header; } -static C4GroupHeader* cc4group_getHeader(CC4Group* const this, const C4GroupEntryData* entry) +static GroupEntryList* cc4group_getChildren(CC4Group* const this, const C4GroupEntryData* entry) { - // TODO: lazy - (void)this; + if(entry->children == NULL) + { + if(cc4group_getHeader(this, entry) == NULL) + { + return NULL; + } - assert(entry->core.Directory); + if(!cc4group_uncompressUntilPosition(this, entry->absolutePosition + sizeof(C4GroupHeader) + entry->header->Entries * sizeof(C4GroupEntryCore))) + { + return NULL; + } - return entry->header; + if(!buildChildren(this, (C4GroupEntryData*)entry, entry->core.Size)) + { + return NULL; + } + } + + return entry->children; } static bool cc4group_getEntryData(CC4Group* const this, const char* const entryPath, const void** const data, size_t* size) @@ -2037,10 +2271,10 @@ static bool cc4group_saveOverwrite(CC4Group* const this, const char* const path) return cc4group_saveIt(this, path, true); } -static bool cc4group_getEntryInfoForEntry(CC4Group* const this, const C4GroupEntryData* const entry, CC4Group_EntryInfo* const info) +static bool cc4group_getEntryInfoForEntry(CC4Group* const this, const C4GroupEntryData* const entry, CC4Group_EntryInfo* const info, bool const lazy) { - C4GroupHeader* header; - if(entry->core.Directory) + C4GroupHeader* header = NULL; + if(entry->core.Directory && !(this->lazy && lazy)) { header = cc4group_getHeader(this, entry); if(header == NULL) @@ -2051,19 +2285,19 @@ static bool cc4group_getEntryInfoForEntry(CC4Group* const this, const C4GroupEnt *info = (CC4Group_EntryInfo){ .fileName = entry->core.FileName, - .modified = entry->core.Directory ? header->Creation : entry->core.Modified, - .author = entry->core.Directory ? header->Maker : entry->parent->header->Maker, - .size = entry->core.Directory ? (sizeof(C4GroupHeader) + header->Entries * sizeof(C4GroupEntryCore)) : (size_t)entry->core.Size, + .modified = header != NULL ? header->Creation : entry->core.Modified, + .author = header != NULL ? header->Maker : entry->parent->header->Maker, + .size = header != NULL ? (sizeof(C4GroupHeader) + header->Entries * sizeof(C4GroupEntryCore)) : (size_t)entry->core.Size, .totalSize = entry->core.Size, .executable = entry->core.Executable ? true : false, .directory = entry->core.Directory ? true : false, - .official = C4GroupHeader_isOfficial(entry->core.Directory ? header : entry->parent->header) // parents header is already loaded to access this child + .official = C4GroupHeader_isOfficial(header != NULL ? header : entry->parent->header) // parents header is already loaded to access this child }; return true; } -static bool cc4group_getEntryInfo(CC4Group* const this, const char* const path, CC4Group_EntryInfo* const info) +static bool cc4group_getEntryInfo(CC4Group* const this, const char* const path, CC4Group_EntryInfo* const info, bool const lazy) { assert(this); assert(info); @@ -2074,10 +2308,10 @@ static bool cc4group_getEntryInfo(CC4Group* const this, const char* const path, return false; } - return cc4group_getEntryInfoForEntry(this, entry, info); + return cc4group_getEntryInfoForEntry(this, entry, info, lazy); } -static bool cc4group_getEntryInfos(CC4Group* const this, const char* const path, CC4Group_EntryInfo** const infos, size_t* size) +static bool cc4group_getEntryInfos(CC4Group* const this, const char* const path, CC4Group_EntryInfo** const infos, size_t* size, bool const lazy) { assert(this); assert(infos); @@ -2114,17 +2348,18 @@ static bool cc4group_getEntryInfos(CC4Group* const this, const char* const path, return false; } - *infos = myInfos; + CC4Group_EntryInfo* infosStart = myInfos; ForeachGroupEntry(children) { - if(!cc4group_getEntryInfoForEntry(this, &entry->value, myInfos++)) + if(!cc4group_getEntryInfoForEntry(this, &entry->value, myInfos++, lazy)) { - free(myInfos); + free(infosStart); return false; } } + *infos = infosStart; return true; } @@ -2251,7 +2486,7 @@ static C4GroupEntryData* cc4group_addEntryToDirectory(CC4Group* const this, C4Gr GroupEntryList* directoryChildren = cc4group_getChildren(this, directory); if(directoryChildren == NULL) { - return NULL; // TODO: error checking in callees of this + return NULL; } C4GroupEntryData* newEntry = &GroupEntryListAppend(directoryChildren, *entry)->value; @@ -2492,6 +2727,7 @@ static CC4Group* cc4group_openAsChild(CC4Group* const this, const char* const pa child->root.core.Directory = 1; child->root.core.FileName[0] = '\0'; child->root.children = cc4group_getChildren(this, entry); + child->uncompressedData = this->uncompressedData; if(child->root.children == NULL) { @@ -2518,11 +2754,19 @@ static void* cc4group_memoryManagementTakeStart(void* const data, size_t const s static void cc4group_memoryManagementTakeEnd(void* const data, void* const arg) { (void)arg; - free(data); + if(data != NULL) + { + free(data); + } } static void* cc4group_memoryManagementCopyStart(void* const data, size_t const size, void* const arg) { + if(data == NULL && size == 0) + { + return NULL; + } + (void)arg; uint8_t* copy = malloc(size); if(copy == NULL) @@ -2536,7 +2780,10 @@ static void* cc4group_memoryManagementCopyStart(void* const data, size_t const s static void cc4group_memoryManagementCopyEnd(void* const data, void* const arg) { (void)arg; - free(data); + if(data != NULL) + { + free(data); + } } static void* cc4group_memoryManagementReferenceStart(void* const data, size_t const size, void* const arg) @@ -2587,6 +2834,7 @@ CC4Group_API cc4group = { .new = cc4group_new, .delete = cc4group_unreference, + .setLazy = cc4group_setLazy, .create = cc4group_create, .openExisting = cc4group_openExisting, |
