diff options
| author | Markus Mittendrein <git@maxmitti.tk> | 2020-04-24 22:03:13 +0200 |
|---|---|---|
| committer | Markus Mittendrein <git@maxmitti.tk> | 2020-04-24 22:03:13 +0200 |
| commit | 578a2f3725b3ad221442763f450e8e239a5422d0 (patch) | |
| tree | 61ad666be22b750fdcc78e71fdc01fb6d553ee82 /src | |
| parent | 0e367485f950b2ab7b9bd1b6c5e4a9b5d328a5dc (diff) | |
| download | cc4group-578a2f3725b3ad221442763f450e8e239a5422d0.tar.gz cc4group-578a2f3725b3ad221442763f450e8e239a5422d0.zip | |
Treat packed groups like normal subfolders when opening a folder containing packed groups
Diffstat (limited to 'src')
| -rw-r--r-- | src/cc4group.c | 222 | ||||
| -rw-r--r-- | src/cc4group.h | 4 | ||||
| -rw-r--r-- | src/cppc4group.cpp | 6 | ||||
| -rw-r--r-- | src/cppc4group.hpp | 4 |
4 files changed, 191 insertions, 45 deletions
diff --git a/src/cc4group.c b/src/cc4group.c index 76531f1..c8dde19 100644 --- a/src/cc4group.c +++ b/src/cc4group.c @@ -56,8 +56,10 @@ static bool cc4group_getEntryData(CC4Group* const this, const char* const entryP typedef enum { CC4Group_AllowedEntryTypes_File = 0x01, CC4Group_AllowedEntryTypes_Directory = 0x02, + CC4Group_AllowedEntryTypes_HandleGroups = 0x04, - CC4Group_AllowedEntryTypes_All = CC4Group_AllowedEntryTypes_File | CC4Group_AllowedEntryTypes_Directory + CC4Group_AllowedEntryTypes_NoGroupHandling = CC4Group_AllowedEntryTypes_File | CC4Group_AllowedEntryTypes_Directory, + CC4Group_AllowedEntryTypes_All = CC4Group_AllowedEntryTypes_File | CC4Group_AllowedEntryTypes_Directory | CC4Group_AllowedEntryTypes_HandleGroups } CC4Group_AllowedEntryTypes; struct list_GroupEntryList; @@ -167,6 +169,8 @@ static GroupEntryList* cc4group_getChildren(CC4Group* const this, const C4GroupE static C4GroupHeader* cc4group_getHeader(CC4Group* const this, const C4GroupEntryData* const entry); static C4GroupEntryData* cc4group_createEntry(CC4Group* const this, const char* const path, C4GroupEntryData* const parent); static C4GroupEntryData* cc4group_createDirectory(CC4Group* const this, const char* const path, C4GroupEntryData* const parent); +static void cc4group_unreference(CC4Group* const this); +static bool cc4group_getParentAndChildEntries(CC4Group* const this, const char* const path, const C4GroupEntryData* const inParent, const C4GroupEntryData** parentEntry, GroupEntryListEntry** const childEntry); static char* cc4group_strerrorFormatter(int32_t const code, const char* const method, const char* const causer, void* const data) { @@ -1538,8 +1542,145 @@ static char* cc4group_pathCombine(const char* const firstPart, const char* secon return result; } +static void* cc4group_memoryManagementOtherGroupStart(void* const data, size_t const size, void* const arg) +{ + (void)data; + (void)size; + ++((CC4Group*)arg)->referenceCounter; + return data; +} + +static void cc4group_memoryManagementOtherGroupEnd(void* const data, void* const arg) +{ + (void)data; + cc4group_unreference(arg); +} + +static const CC4Group_MemoryManagement_t otherGroupMemoryManagement = { + .start = cc4group_memoryManagementOtherGroupStart, + .end = cc4group_memoryManagementOtherGroupEnd, +}; + +static bool cc4group_deleteEntryFromParent(CC4Group* const this, GroupEntryListEntry* const deleteEntry, const C4GroupEntryData* const parent, bool recursive) +{ + if(deleteEntry->value.core.Directory) + { + if(!recursive) + { + SET_MESSAGE_ERROR("The desired entry is a subgroup but a non-recursive deletion was requested"); + return false; + } + + // if they are not loaded yet, there is no need to delete them + if(deleteEntry->value.children != NULL) + { + deleteChildren(deleteEntry->value.children); + } + } + + if(deleteEntry->value.data != NULL) + { + cc4group_applyMemoryManagementEnd(deleteEntry->value.memoryManagement, deleteEntry->value.data); + } + if(deleteEntry->value.path) + { + free((void*)deleteEntry->value.path); + } + + GroupEntryList* parentChildren = cc4group_getChildren(this, parent); + assert(parentChildren); // if there was an error, it should have really been catched already in cc4group_getParentAndChildEntries + + GroupEntryListRemove(parentChildren, deleteEntry); + parent->header->Entries = GroupEntryListSize(parentChildren); // header is loaded already for the children + + return true; +} + +static bool cc4group_addOtherGroupContents(CC4Group* const this, C4GroupEntryData* const contentsRoot, CC4Group* const group, GroupEntryList* entries) +{ + if(entries == NULL) + { + SET_MESSAGE_ERROR("Could not retrieve children list of other group."); + return false; + } + CC4Group_MemoryManagement memoryMgmt = otherGroupMemoryManagement; + memoryMgmt.arg = group; + ForeachGroupEntry(entries) + { + C4GroupEntryData* newEntry; + if(entry->value.core.Directory) + { + newEntry = cc4group_createDirectory(this, entry->value.core.FileName, contentsRoot); + newEntry->children = GroupEntryListNew(); + if(newEntry == NULL) + { + return false; + } + C4GroupHeader_setCreation(newEntry->header, entry->value.header->Creation); + C4GroupHeader_setOfficial(newEntry->header, entry->value.header->Official); + C4GroupHeader_setMaker(newEntry->header, entry->value.header->Maker); + if(!cc4group_addOtherGroupContents(this, newEntry, group, cc4group_getChildren(group, &entry->value))) + { + return false; + } + } + else + { + newEntry = cc4group_createEntry(this, entry->value.core.FileName, contentsRoot); + if(newEntry == NULL) + { + return false; + } + if(entry->value.data != NULL) + { + newEntry->data = entry->value.data; + if(!cc4group_applyMemoryManagementStart(memoryMgmt, (const uint8_t**)&newEntry->data, newEntry->core.Size)) + { + assert(!"This must not happen."); + } + newEntry->memoryManagement = memoryMgmt; + } + } + newEntry->core.HasCRC = C4GroupEntryCore_NoCRC; + newEntry->core.Executable = entry->value.core.Executable; + newEntry->core.Modified = entry->value.core.Modified; + newEntry->core.Size = entry->value.core.Size; + } + return true; +} + +static C4GroupEntryData* cc4group_addGroup(CC4Group* const this, CC4Group* const group, const char* const targetPath, C4GroupEntryData* const parent) +{ + assert(this); + C4GroupEntryData* const contentsRoot = cc4group_createDirectory(this, targetPath, parent); + if(!contentsRoot) + { + return NULL; + } + contentsRoot->children = GroupEntryListNew(); + C4GroupHeader_setCreation(contentsRoot->header, group->root.header->Creation); + C4GroupHeader_setOfficial(contentsRoot->header, group->root.header->Official); + C4GroupHeader_setMaker(contentsRoot->header, group->root.header->Maker); + contentsRoot->core.Size = group->uncompressedSize; + + if(cc4group_addOtherGroupContents(this, contentsRoot, group, group->root.children)) + { + return contentsRoot; + } + + const char* errorMessage = cc4group.getErrorMessage(this); + const C4GroupEntryData* deleteEntryparent; + GroupEntryListEntry* deleteEntry; + if(!cc4group_getParentAndChildEntries(this, targetPath, parent, &deleteEntryparent, &deleteEntry) && cc4group_deleteEntryFromParent(this, deleteEntry, deleteEntryparent, true)) + { + cc4group_warn(this, "Loading contents from \"%s\" into this group \"%s\" failed with error: %s\n" + "Additionally, removing the directory which should have held the other group's contents failed. The error information stored in the group is about the failed deletion.", cc4group.getFullName(group), cc4group.getFullName(this), errorMessage); + } + return NULL; +} + // takes over memory management of filePath -static C4GroupEntryData* cc4group_addFileFromDisk(CC4Group* const this, const char* const filePath, const char* const targetPath, C4GroupEntryData* const parent) +static C4GroupEntryData* cc4group_addFileFromDisk(CC4Group* const this, const char* const filePath, const char* const targetPath, C4GroupEntryData* const parent, bool handleGroup) { assert(this); assert(filePath); @@ -1554,6 +1695,27 @@ static C4GroupEntryData* cc4group_addFileFromDisk(CC4Group* const this, const ch goto ret; } + CC4Group* group = NULL; + if(handleGroup) + { + group = cc4group_new(); + cc4group_setLazy(group, false); + if(!cc4group_openExisting(group, filePath)) + { + cc4group_unreference(group); + } + else + { + C4GroupEntryData* contentsRoot = cc4group_addGroup(this, group, targetPath, parent); + if(contentsRoot != NULL) + { + free((void*)filePath); + cc4group_unreference(group); + return contentsRoot; + } + } + } + result = cc4group_createEntry(this, targetPath, parent); if(result == NULL) { @@ -1678,7 +1840,7 @@ static bool cc4group_populateDirectoryFromDisk(CC4Group* const this, C4GroupEntr } else { - if(!cc4group_addFileFromDisk(this, directoryEntryPath, dirEntry->d_name, entry)) + if(!cc4group_addFileFromDisk(this, directoryEntryPath, dirEntry->d_name, entry, true)) { success = false; break; @@ -1780,6 +1942,11 @@ static void cc4group_delete(CC4Group* const this) free(this->error.lastFormattedMessage); } + if(this->root.path != NULL) + { + free((void*)this->root.path); + } + if(this->path != NULL) { free((void*)this->path); @@ -2922,10 +3089,10 @@ static char* cc4group_splitParentAndChildPaths(char* const combinedPath, const c } } -static bool cc4group_getParentAndChildEntries(CC4Group* const this, const char* const path, const C4GroupEntryData** parentEntry, GroupEntryListEntry** const childEntry) +static bool cc4group_getParentAndChildEntries(CC4Group* const this, const char* const path, const C4GroupEntryData* const inParent, const C4GroupEntryData** parentEntry, GroupEntryListEntry** const childEntry) { bool error = false; - GroupEntryListEntry* listEntry = cc4group_getChildListEntryByPath(this, path, &error, NULL); + GroupEntryListEntry* listEntry = cc4group_getChildListEntryByPath(this, path, &error, inParent); if(error) { return false; @@ -2950,42 +3117,12 @@ static bool cc4group_deleteEntry(CC4Group* const this, const char* const path, b const C4GroupEntryData* parent; GroupEntryListEntry* deleteEntry = NULL; - if(!cc4group_getParentAndChildEntries(this, path, &parent, &deleteEntry)) + if(!cc4group_getParentAndChildEntries(this, path, NULL, &parent, &deleteEntry)) { return false; } - if(deleteEntry->value.core.Directory) - { - if(!recursive) - { - SET_MESSAGE_ERROR("The desired entry is a subgroup but a non-recursive deletion was requested"); - return false; - } - - // if they are not loaded yet, there is no need to delete them - if(deleteEntry->value.children != NULL) - { - deleteChildren(deleteEntry->value.children); - } - } - - if(deleteEntry->value.data != NULL) - { - cc4group_applyMemoryManagementEnd(deleteEntry->value.memoryManagement, deleteEntry->value.data); - } - if(deleteEntry->value.path) - { - free((void*)deleteEntry->value.path); - } - - GroupEntryList* parentChildren = cc4group_getChildren(this, parent); - assert(parentChildren); // if there was an error, it should have really been catched already in cc4group_getParentAndChildEntries - - GroupEntryListRemove(parentChildren, deleteEntry); - parent->header->Entries = GroupEntryListSize(parentChildren); // header is loaded already for the children - - return true; + return cc4group_deleteEntryFromParent(this, deleteEntry, parent, recursive); } static C4GroupEntryData* cc4group_addEntryToDirectory(CC4Group* const this, C4GroupEntryData* const directory, const C4GroupEntryData* const entry) @@ -3026,7 +3163,7 @@ static bool cc4group_renameEntry(CC4Group* const this, const char* const oldPath const C4GroupEntryData* oldParent; GroupEntryListEntry* entry = NULL; - if(!cc4group_getParentAndChildEntries(this, oldPath, &oldParent, &entry)) + if(!cc4group_getParentAndChildEntries(this, oldPath, NULL, &oldParent, &entry)) { return false; } @@ -3161,15 +3298,14 @@ static bool cc4group_addFromDisk(CC4Group* const this, const char* const path, c SET_MESSAGE_ERROR("The specified path doesn't match the specified allowed entry type"); return false; } - switch(type) { case CC4Group_AllowedEntryTypes_File: - return cc4group_addFileFromDisk(this, strdup(path), targetEntryPath, NULL) != NULL; + return cc4group_addFileFromDisk(this, strdup(path), targetEntryPath, NULL, allowedEntryTypes & CC4Group_AllowedEntryTypes_HandleGroups) != NULL; case CC4Group_AllowedEntryTypes_Directory: return cc4group_addDirectoryFromDisk(this, strdup(path), targetEntryPath, NULL); - case CC4Group_AllowedEntryTypes_All: - assert(!"All is not a valid type determined for the path; This code should never be reached"); + case CC4Group_AllowedEntryTypes_All: case CC4Group_AllowedEntryTypes_NoGroupHandling: case CC4Group_AllowedEntryTypes_HandleGroups: + assert(!"Invalid type determined for the path; This code should never be reached"); return false; } @@ -3423,6 +3559,8 @@ CC4Group_API cc4group = { .AllowedEntryTypes = { .File = CC4Group_AllowedEntryTypes_File, .Directory = CC4Group_AllowedEntryTypes_Directory, + .HandleGroups = CC4Group_AllowedEntryTypes_HandleGroups, + .NoGroupHandling = CC4Group_AllowedEntryTypes_NoGroupHandling, .All = CC4Group_AllowedEntryTypes_All }, diff --git a/src/cc4group.h b/src/cc4group.h index 7e871a2..d06f636 100644 --- a/src/cc4group.h +++ b/src/cc4group.h @@ -192,7 +192,9 @@ typedef struct { struct { int File; // only a single regular file is allowed int Directory; // only a directory is allowed, contents are handled recursively - int All; // currently File | Directory + int HandleGroups; // groups are opened and contents are handled recursively + int NoGroupHandling; // currently File | Directory + int All; // currently File | Directory | HandleGroups } const AllowedEntryTypes; diff --git a/src/cppc4group.cpp b/src/cppc4group.cpp index 2c69b98..b76767b 100644 --- a/src/cppc4group.cpp +++ b/src/cppc4group.cpp @@ -164,7 +164,11 @@ namespace { { result |= cc4group.AllowedEntryTypes.Directory; } - // All is handled by the above two + if(allowedEntryTypes & CppC4Group::AllowedEntryTypes::HandleGroups) + { + result |= cc4group.AllowedEntryTypes.HandleGroups; + } + // All and NoGroupHandling are handled by the above two return result; } diff --git a/src/cppc4group.hpp b/src/cppc4group.hpp index ae8b3b3..e8f7ba1 100644 --- a/src/cppc4group.hpp +++ b/src/cppc4group.hpp @@ -125,7 +125,9 @@ public: enum class AllowedEntryTypes { File = 0x1, Directory = 0x2, - All = File | Directory + HandleGroups = 0x4, + NoGroupHandling = File | Directory, + All = File | Directory | HandleGroups }; // use this to set one of the predefined strategies |
