diff options
author | Rangi <remy.oukaour+rangi42@gmail.com> | 2020-09-15 11:52:41 -0400 |
---|---|---|
committer | Rangi <remy.oukaour+rangi42@gmail.com> | 2020-09-15 11:52:41 -0400 |
commit | 8b055201c01d3ac5816b687792ce2f179d1bf9ef (patch) | |
tree | 548b547e27a0d4781256e3343d2e1b46df34092d /tools/xor_compress.c | |
parent | cfa63ef5e073da0d3eb6964917665f7e948c0313 (diff) |
Implement xor_compress in C
Diffstat (limited to 'tools/xor_compress.c')
-rw-r--r-- | tools/xor_compress.c | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/tools/xor_compress.c b/tools/xor_compress.c new file mode 100644 index 0000000..03ea451 --- /dev/null +++ b/tools/xor_compress.c @@ -0,0 +1,115 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#define PROGRAM_NAME "xor_compress" + +extern int errno; + +size_t file_size(FILE *f, int *err) { + int check = fseek(f, 0, SEEK_END); + if (check == -1) goto failure; + + long f_size = ftell(f); + if (f_size == -1) goto failure; + + check = fseek(f, 0, SEEK_SET); + if (check == -1) goto failure; + + *err = 0; + return (size_t)f_size; + +failure: + *err = errno; + return 0; +} + +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, err); + if (*err > 0) goto failure; + if (f_size == 0) 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); + if (read_size != f_size) { + // fread does not set errno + fprintf(stderr, PROGRAM_NAME ": %s: Read error\n", filename); + *err = 1; + return buffer; + } + } + + *err = 0; + return buffer; + +failure: + fprintf(stderr, PROGRAM_NAME ": %s: %s\n", filename, strerror(errno)); + *err = errno; + return buffer; +} + +int compress_files(char *filenames[], int num_files) { + int err = 0; + size_t n = 0; + unsigned char *data = read_files(filenames, num_files, &n, &err); + if (err > 0) { + if (data) free(data); + return err; + } + + unsigned char v = 0x00; + for (size_t i = 0; i < n;) { + unsigned char byte = data[i++]; + unsigned char size = 0; + if (data[i] == v) { + // 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++); + putchar(size + 0x7f); + putchar(v ^ byte); + if (size % 2 == 0) v = byte; + } else { + // Sequential (< 0x80) + // Run stops at 0x80 bytes or when the value two ahead is equal to v + unsigned char buffer[256] = {0}; + 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; + } + putchar(size - 1); + fwrite(buffer, 1, size, stdout); + } + } + + fflush(stdout); + free(data); + return 0; +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + fputs("Usage: " PROGRAM_NAME " file... > files.xor\n", stderr); + exit(1); + } + argv++; + argc--; + return compress_files(argv, argc); +} |