diff options
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 376 |
1 files changed, 0 insertions, 376 deletions
@@ -1,376 +0,0 @@ -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <string.h> -#include <errno.h> -#include <sys/mman.h> -#include <sys/time.h> -#include <time.h> - -#include <zlib.h> - -#include <assert.h> - -#include "GenericList.h" - -#define C4GroupMaxMaker 30 -#define C4GroupMaxPassword 30 - -typedef struct { - char id[25]; - uint8_t Reserved1[3]; - int32_t Ver1, Ver2; - int32_t Entries; - char Maker[C4GroupMaxMaker + 2]; - char Password[C4GroupMaxPassword + 2]; - int32_t Creation; - int32_t Original; - uint8_t Reserved2[92]; -} __attribute__((__packed__)) C4GroupHeader; - -typedef struct { - char FileName[257]; - uint8_t Reserved1[3]; - int32_t Packed; - int32_t Directory; - int32_t Size; - int32_t Reserved2; - int32_t Offset; - int32_t Modified; - uint8_t HasCRC; - uint32_t CRC; - uint8_t Executable; - uint8_t Reserved3[26]; -} __attribute__((__packed__)) C4GroupEntryCore; - -struct list_GroupEntryList; -typedef struct { - C4GroupEntryCore core; - uint8_t* data; - struct list_GroupEntryList* children; -} C4GroupEntryData; - -LIST_AUTO(C4GroupEntryData, GroupEntryList) -#define ForeachGroupEntry(list) LIST_FOREACH(GroupEntryList, list, entry) - -off_t fileSizeFd(int fd) -{ - struct stat st; - - if(fstat(fd, &st) == -1) - { - return -1; - } - - return st.st_size; -} - -void memScrambleHeader(uint8_t* data) -{ - // XOR deface - for(size_t i = 0; i < sizeof(C4GroupHeader); i++) - data[i] ^= 237; - // byte swap - for(size_t i = 0; i + 2 < sizeof(C4GroupHeader); i += 3) - { - uint8_t temp = data[i]; - data[i] = data[i + 2]; - data[i + 2] = temp; - } -} - -void buildChildren(C4GroupEntryData* entry) -{ - C4GroupHeader* header = (C4GroupHeader*)entry->data; - - entry->children = GroupEntryListNew(); - - uint8_t* data = entry->data + sizeof(C4GroupHeader); - uint8_t* childData = entry->data + sizeof(C4GroupHeader) + sizeof(C4GroupEntryCore) * header->Entries; - - for(size_t i = 0; i < (size_t)header->Entries; ++i) - { - C4GroupEntryCore* core = (C4GroupEntryCore*)(data + sizeof(C4GroupEntryCore) * i); - C4GroupEntryData* childEntry = &GroupEntryListAppend(entry->children, (C4GroupEntryData){.core = *core, .data = childData + core->Offset, .children = NULL})->value; - - if(core->Directory) - { - memScrambleHeader(childEntry->data); - buildChildren(childEntry); - } - } -} - -const char* formatTime(int32_t time) -{ - time_t tTime = time; - static char ret[128]; - strftime(ret, sizeof(ret), "%c", localtime(&tTime)); - return ret; -} - -void handleChildren(GroupEntryList* entries, size_t indent, const char* path) -{ - size_t pathLen = strlen(path); - char* targetPath = malloc(pathLen + 260 + 1); - - strcpy(targetPath, path); - strcpy(targetPath + pathLen++, "/"); - - if(mkdir(path, 0755) == -1) - { - fprintf(stderr, "ERROR: Creating target directory \"%s\": %s\n", path, strerror(errno)); - goto ret; - } - - ForeachGroupEntry(entries) - { - for(size_t i = 0; i < indent; ++i) - { - putchar('\t'); - } - - strcpy(targetPath + pathLen, entry->value.core.FileName); - -// printf("%s\t%s\t%d B\n", targetPath, formatTime(entry->value.core.Modified), entry->value.core.Size); - - if(!entry->value.core.Directory) - { - int file = open(targetPath, O_WRONLY | O_CREAT, entry->value.core.Executable ? 0755 : 0644); - if(file == -1) - { - fprintf(stderr, "ERROR: Creating target file \"%s\": %s\n", targetPath, strerror(errno)); - goto ret; - } - write(file, entry->value.data, entry->value.core.Size); - - if(close(file) == -1) - { - fprintf(stderr, "ERROR: Closing file \"%s\": %s\n", targetPath, strerror(errno)); - } - - struct timeval tv[2] = {{.tv_usec = 0, .tv_sec = entry->value.core.Modified}, {.tv_usec = 0, .tv_sec = entry->value.core.Modified}}; - if(utimes(targetPath, tv) == -1) - { - fprintf(stderr, "ERROR: Setting modification time for \"%s\": %s\n", targetPath, strerror(errno)); - } - } - - if(entry->value.core.Directory) - { - handleChildren(entry->value.children, indent + 1, targetPath); - } - } - -ret: - free(targetPath); -} - -void deleteChildren(GroupEntryList* entries) -{ - ForeachGroupEntry(entries) - { - if(entry->value.core.Directory) - { - deleteChildren(entry->value.children); - } - } - - GroupEntryListDestroy(entries); -} - -int main(int argc, char* argv[]) -{ - if(argc != 3) - { - fprintf(stderr, "USAGE: %s <group> <target>\n", argv[0]); - return EXIT_FAILURE; - } - - int file = open(argv[1], O_RDONLY); - if(file == -1) - { - fprintf(stderr, "ERROR: Opening file \"%s\": %s\n", argv[1], strerror(errno)); - return EXIT_FAILURE; - } - - __off_t size = fileSizeFd(file); - if(size == -1) - { - fprintf(stderr, "ERROR: Getting file size: %s\n", strerror(errno)); - return EXIT_FAILURE; - } - else - { - printf("Size: %ld\n", size); - } - - uint8_t* mappedFile = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, file, 0); - - if(mappedFile == MAP_FAILED) - { - fprintf(stderr, "ERROR: Mapping file: %s\n", strerror(errno)); - return EXIT_FAILURE; - } - - if(close(file) == -1) - { - fprintf(stderr, "ERROR: Closing file: %s\n", strerror(errno)); - return EXIT_FAILURE; - } - - if((mappedFile[0] != 0x1e && mappedFile[0] != 0x1f) || (mappedFile[1] != 0x8c && mappedFile[1] != 0x8b)) - { - fprintf(stderr, "ERROR: The file is not a valid group file. Magic bytes don't match.\n"); - return EXIT_FAILURE; - } - - mappedFile[0] = 0x1f; - mappedFile[1] = 0x8b; - - z_stream strm = { - .zalloc = NULL, - .zfree = NULL, - .opaque = NULL, - .next_in = mappedFile, - .avail_in = size - }; - - int ret = inflateInit2(&strm, 15 + 16); // window size 15 + automatic gzip - - if(ret != Z_OK) - { - fprintf(stderr, "ERROR: inflateInit2: %s\n", zError(ret)); - return EXIT_FAILURE; - } - - size_t currentSize = sizeof(C4GroupHeader); - C4GroupHeader* header = malloc(currentSize); - - strm.next_out = (Bytef*)header; - strm.avail_out = currentSize; - - ret = inflate(&strm, Z_SYNC_FLUSH); - if(ret != Z_OK) - { - fprintf(stderr, "ERROR: inflating header: %s\n", zError(ret)); - return EXIT_FAILURE; - } - - memScrambleHeader((uint8_t*)header); - - printf("Version %d.%d\n", header->Ver1, header->Ver2); - printf("%d Entries\n", header->Entries); - printf("%sOriginal\n", header->Original == 1234567 ? "" : "Not "); - printf("Created %s\n", formatTime(header->Creation)); - puts(header->id); - puts(header->Maker); - - assert(header->Ver1 == 1); - assert(header->Ver2 == 2); - - currentSize += sizeof(C4GroupEntryCore) * header->Entries; - header = realloc(header, currentSize); - C4GroupEntryCore* cores = (C4GroupEntryCore*)((void*)(header) + sizeof(C4GroupHeader)); - - GroupEntryList* entries = GroupEntryListNew(); - - strm.next_out = (Bytef*)cores; - strm.avail_out = sizeof(C4GroupEntryCore) * header->Entries; - - ret = inflate(&strm, Z_SYNC_FLUSH); - if(ret != Z_OK) - { - fprintf(stderr, "ERROR: inflating header: %s\n", zError(ret)); - return EXIT_FAILURE; - } - - C4GroupEntryCore* last = cores + header->Entries - 1; - size_t uncompressedSize = last->Offset + last->Size; - - currentSize += uncompressedSize; - -#define TMP_FILE "cc4group.tmp" - int tmpFile = open(TMP_FILE, O_CREAT | O_RDWR | O_TRUNC | O_EXCL, 0600); - if(tmpFile == -1) - { - fprintf(stderr, "ERROR: Opening tmp file \"%s\": %s\n", "cc4group.tmp", strerror(errno)); - return EXIT_FAILURE; - } - - if(unlink(TMP_FILE) == -1) - { - fprintf(stderr, "ERROR: Deleting tmp file \"%s\". Manual deletion is required: %s\n", TMP_FILE, strerror(errno)); - } -#undef TMP_FILE - - // allocate file size - // https://gist.github.com/marcetcheverry/991042 - if(lseek(tmpFile, currentSize - 1, SEEK_SET) == -1) - { - fprintf(stderr, "ERROR: Seeking the tmp file to the end: %s\n", strerror(errno)); - return EXIT_FAILURE; - } - - if(write(tmpFile, "", 1) == -1) - { - fprintf(stderr, "ERROR: Writing to the tmp file's end: %s\n", strerror(errno)); - return EXIT_FAILURE; - } - - uint8_t* mmappedHeader = mmap(NULL, currentSize, PROT_READ | PROT_WRITE, MAP_SHARED, tmpFile, 0); - if(mmappedHeader == MAP_FAILED) - { - fprintf(stderr, "ERROR: Mapping tmp file: %s\n", strerror(errno)); - return EXIT_FAILURE; - } - - if(close(tmpFile) == -1) - { - fprintf(stderr, "ERROR: Closing tmp file \"cc4group.tmp\": %s\n", strerror(errno)); - return EXIT_FAILURE; - } - - uint8_t* data = (void*)(mmappedHeader) + sizeof(C4GroupHeader) + sizeof(C4GroupEntryCore) * header->Entries; - - // write alredy decompressed header and cores into the file - memcpy(mmappedHeader, header, data - mmappedHeader); - - strm.next_out = data; - strm.avail_out = uncompressedSize; - - ret = inflate(&strm, Z_SYNC_FLUSH); - if(ret != Z_STREAM_END) - { - fprintf(stderr, "ERROR: inflating header: %s\n", zError(ret)); - return EXIT_FAILURE; - } - - inflateEnd(&strm); - - if(munmap(mappedFile, size) == -1) - { - fprintf(stderr, "ERROR: Unmapping file: %s\n", strerror(errno)); - return EXIT_FAILURE; - } - - C4GroupEntryData root = {.data = (uint8_t*)mmappedHeader, .children = NULL}; - buildChildren(&root); - - handleChildren(root.children, 0, argv[2]); - - deleteChildren(entries); - - free(header); - - if(munmap(mmappedHeader, size) == -1) - { - fprintf(stderr, "ERROR: Unmapping tmp file: %s\n", strerror(errno)); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} |
