summaryrefslogtreecommitdiffstats
path: root/src/cc4group.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cc4group.c')
-rw-r--r--src/cc4group.c476
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,