#include #include #include #include #include #include #include #include #define BLKSIZE 0x100 #define HEADERSIZE 0x20 #define MAX_FN_LEN 25 // 26 - null char #define MAX_FILE_SIZE (BLKSIZE * 0x100) - HEADERSIZE int is_regular_file(char *path) { struct stat path_stat; stat(path, &path_stat); return S_ISREG(path_stat.st_mode); } void spitempty() { putchar('C'); putchar('F'); putchar('S'); for (int i=0; i<0x20-3; i++) { putchar(0); } } int spitblock(char *fullpath, char *fn) { FILE *fp = fopen(fullpath, "r"); fseek(fp, 0, SEEK_END); long fsize = ftell(fp); if (fsize > MAX_FILE_SIZE) { fclose(fp); fprintf(stderr, "File too big: %s %ld\n", fullpath, fsize); return 1; } /* Compute block count. * We always have at least one, which contains 0x100 bytes - 0x20, which is * metadata. The rest of the blocks have a steady 0x100. */ unsigned char blockcount = 1; int fsize2 = fsize - (BLKSIZE - HEADERSIZE); if (fsize2 > 0) { blockcount += (fsize2 / BLKSIZE); } if (blockcount * BLKSIZE < fsize + HEADERSIZE) { blockcount++; } putchar('C'); putchar('F'); putchar('S'); putchar(blockcount); // file size is little endian putchar(fsize & 0xff); putchar((fsize >> 8) & 0xff); int fnlen = strlen(fn); for (int i=0; id_name, ".") == 0) || strcmp(ep->d_name, "..") == 0) { continue; } if (ep->d_type != DT_DIR && ep->d_type != DT_REG) { fprintf(stderr, "Only regular file or directories are supported\n"); return 1; } int slen = strlen(ep->d_name); if (prefixlen + slen> MAX_FN_LEN) { fprintf(stderr, "Filename too long: %s/%s\n", prefix, ep->d_name); return 1; } char fullpath[0x1000]; strcpy(fullpath, path); strcat(fullpath, "/"); strcat(fullpath, ep->d_name); char newprefix[MAX_FN_LEN]; strcpy(newprefix, prefix); if (prefixlen > 0) { strcat(newprefix, "/"); } strcat(newprefix, ep->d_name); if (ep->d_type == DT_DIR) { int r = spitdir(fullpath, newprefix, patterns); if (r != 0) { return r; } } else { char **p = patterns; // if we have no pattern, we match all int matches = (*p) == NULL ? 1 : 0; while (*p) { if (fnmatch(*p, ep->d_name, 0) == 0) { matches = 1; break; } p++; } if (!matches) { continue; } int r = spitblock(fullpath, newprefix); if (r != 0) { return r; } } } closedir(dp); return 0; } void usage() { fprintf(stderr, "Usage: cfspack [-p pattern] [/path/to/dir...]\n"); } int main(int argc, char *argv[]) { int patterncount = 0; char **patterns = malloc(sizeof(char**)); patterns[0] = NULL; while (1) { int c = getopt(argc, argv, "p:"); if (c < 0) { break; } switch (c) { case 'p': patterns[patterncount] = optarg; patterncount++; patterns = realloc(patterns, sizeof(char**)*(patterncount+1)); patterns[patterncount] = NULL; break; default: usage(); return 1; } } int res = 0; for (int i=optind; i