#include #include #include #include #include char *program_name; size_t file_size(FILE *f) { if (fseek(f, 0, SEEK_END) == -1) return 0; long f_size = ftell(f); if (f_size == -1) return 0; if (fseek(f, 0, SEEK_SET) == -1) return 0; return (size_t)f_size; } unsigned char *read_files(char *filenames[], int num_files, size_t *buf_size, int *err) { *buf_size = 0; unsigned char *buffer = NULL; const char *filename = NULL; for (int i = 0; i < num_files; i++) { filename = filenames[i]; FILE *f = fopen(filename, "rb"); if (!f) goto failure; size_t f_size = file_size(f); if (errno) goto failure; if (!f_size) continue; *buf_size += f_size; buffer = realloc(buffer, *buf_size); if (!buffer) goto failure; unsigned char *buf_end = buffer + (*buf_size - f_size); size_t read_size = fread(buf_end, 1, f_size, f); fclose(f); if (read_size != f_size) { // fread does not set errno fprintf(stderr, "%s: %s: Read error\n", program_name, filename); *err = 1; return buffer; } } *err = 0; return buffer; failure: fprintf(stderr, "%s: %s: %s\n", program_name, filename, strerror(errno)); *err = errno; return buffer; } int write_compressed(const char *filename, unsigned char *data, size_t n, int *err) { FILE *f = fopen(filename, "wb"); if (!f) { fprintf(stderr, "%s: %s: %s\n", program_name, filename, strerror(errno)); *err = errno; return 0; } int runs = 0; unsigned char v = 0x00; for (size_t i = 0; i < n; runs++) { unsigned char byte = data[i++]; unsigned char size = 0; if (i == n || data[i] != v) { // Sequential (< 0x80) // Run stops at 0x80 bytes or when the value two ahead is equal to v unsigned char buffer[0x80]; buffer[size++] = v ^ byte; for (; i < n; i++) { v = byte; if (size > 0x7f || (i + 1 < n && data[i + 1] == v)) break; byte = data[i]; buffer[size++] = v ^ byte; } fputc(size - 1, f); fwrite(buffer, 1, size, f); } else { // Alternating (>= 0x80) // Run stops at 0x80 bytes or when the values stop alternating for (; i < n && size < 0x80 && data[i] == ((size % 2) ? byte : v); size++, i++); fputc(size + 0x7f, f); fputc(v ^ byte, f); if (size % 2 == 0) v = byte; } } fflush(f); fclose(f); *err = 0; return runs; } int main(int argc, char *argv[]) { program_name = argc ? argv[0] : "xor_compress"; bool verbose = argc > 1 && !strcmp(argv[1], "-v"); if (verbose) { argv++; argc--; } if (argc < 3) { fprintf(stderr, "Usage: %s [-v] file... files.xor\n", program_name); exit(1); } argv++; argc -= 2; const char *out_filename = argv[argc]; int err = 0; size_t data_size = 0; unsigned char *data = read_files(argv, argc, &data_size, &err); if (!err) { int runs = write_compressed(out_filename, data, data_size, &err); if (!err && verbose) { printf("%s: %s: ld bc, $%x\n", program_name, out_filename, runs); } } free(data); return err; }