X-Git-Url: https://git.draconx.ca/gitweb/upkg.git/blobdiff_plain/94cc6656cd266c2f0d8eb5bc1201fa091744c46c..ca9488ce89bd4895db0341db393ec03696b4ebd3:/test/pcxrle.c diff --git a/test/pcxrle.c b/test/pcxrle.c new file mode 100644 index 0000000..6824fd0 --- /dev/null +++ b/test/pcxrle.c @@ -0,0 +1,196 @@ +/* + * Tool for testing PCX run-length encoding. + * Copyright © 2012 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "common.h" + +#define PROGNAME "pcxrle" +static const char *progname = PROGNAME; +static const char sopts[] = "VH"; +static const struct option lopts[] = { + { "version", 0, NULL, 'V' }, + { "help", 0, NULL, 'H' }, + { 0 } +}; + +static void print_usage(FILE *f) +{ + fprintf(f, "Usage: %s [options] bytes [bytes ...]\n", progname); +} + +static void print_version(void) +{ + printf("%s (%s) %s\n", PROGNAME, PACKAGE_NAME, PACKAGE_VERSION); + puts("Copyright (C) 2012 Nick Bowler."); + puts("License GPLv3+: GNU GPL version 3 or later ."); + puts("This is free software: you are free to change and redistribute it."); + puts("There is NO WARRANTY, to the extent permitted by law."); +} + +static void rle_error(const unsigned char *raw, size_t n, const char *str) +{ + fprintf(stderr, "%s: %s\n", progname, str); + fprintf(stderr, "%s: raw input was:\n", progname); + + fprintf(stderr, "%*s", 4, ""); + for (size_t i = 0; i < n; i++) + printf("%.2hhx", raw[i]); + putchar('\n'); +} + +static int compare_rle(const unsigned char *rle_buf, size_t rle_len, + const unsigned char *raw_buf, size_t raw_len) +{ + unsigned char tmp[raw_len]; + size_t offset = 0; + + for (size_t i = 0; i < rle_len; i++) { + size_t count = 1; + + if ((rle_buf[i] & 0xc0) == 0xc0) { + count = rle_buf[i] & 0x3f; + if (++i >= rle_len) { + rle_error(raw_buf, raw_len, "invalid RLE encoding"); + return -1; + } + } + + if (offset + count > raw_len) { + rle_error(raw_buf, raw_len, "RLE expansion exceeds raw size"); + return -1; + } + + memset(tmp+offset, rle_buf[i], count); + offset += count; + } + + if (offset < raw_len) { + rle_error(raw_buf, raw_len, "RLE expansion falls short of raw size"); + return -1; + } + + if (memcmp(raw_buf, tmp, raw_len) != 0) { + rle_error(raw_buf, raw_len, "RLE expansion does not match raw data"); + return -1; + } + + return 0; +} + +static int encode_rle(FILE *tmp, const char *hex) +{ + unsigned char buf[256], rle_buf[sizeof buf*2]; + struct pcx_head head; + int rc, ret = -1; + long offset; + size_t n; + + n = test_decode_hex(hex, buf, sizeof buf); + if (n == -1) { + fprintf(stderr, "%s: invalid hex sequence: %s\n", + progname, hex); + return -1; + } else if (n == 0) { + fprintf(stderr, "%s: empty argument\n", progname); + return -1; + } else if (n > sizeof buf) { + fprintf(stderr, "%s: hex sequence too long: %s\n", + progname, hex); + return -1; + } + + rewind(tmp); + head.width = head.height = n; + rc = pcx_write_scanline(&head, buf, tmp); + if (rc != 0) { + fprintf(stderr, "%s: failed to encode data: %s\n", + progname, strerror(errno)); + return -1; + } + + offset = ftell(tmp); + if (offset < 0) { + fprintf(stderr, "%s: failed to get file offset: %s\n", + progname, strerror(errno)); + return -1; + } + rewind(tmp); + + if (fread(rle_buf, offset, 1, tmp) != 1) { + fprintf(stderr, "%s: failed to read RLE data: %s\n", + progname, strerror(errno)); + return -1; + } + + ret = compare_rle(rle_buf, offset, buf, n); + for (long i = 0; i < offset; i++) + printf("%.2hhx", rle_buf[i]); + putchar('\n'); + return ret; +} + +int main(int argc, char **argv) +{ + int opt, ret = EXIT_SUCCESS; + FILE *tmp; + + if (argc > 0) + progname = argv[0]; + + while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) { + switch (opt) { + case 'V': + print_version(); + return EXIT_SUCCESS; + case 'H': + print_usage(stdout); + return EXIT_SUCCESS; + default: + print_usage(stderr); + return EXIT_FAILURE; + } + } + + if (!argv[optind]) { + print_usage(stderr); + return EXIT_FAILURE; + } + + tmp = tmpfile(); + if (!tmp) { + fprintf(stderr, "%s: failed to create temporary file: %s\n", + progname, strerror(errno)); + return EXIT_FAILURE; + } + + for (int i = optind; i < argc; i++) { + if (encode_rle(tmp, argv[i]) != 0) { + ret = EXIT_FAILURE; + } + } + + return ret; +}