--- /dev/null
+/*
+ * Helper functions for test programs.
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "common.h"
+
+/*
+ * Decode a hexadecimal string into a sequence of bytes. If there are an
+ * odd number of nibbles, treat the first character as the least significant
+ * nibble of the first byte. The result is written to the buffer specified by
+ * buf. At most n bytes are written to the buffer.
+ *
+ * Returns the number of bytes that would be written provided that n was large
+ * enough, or (size_t)-1 if the input is not valid.
+ */
+size_t test_decode_hex(const char *hex, unsigned char *buf, size_t n)
+{
+ size_t len, count = 0;
+ char tmp[] = "00";
+
+ for (len = 0; hex[len]; len++) {
+ if (!isxdigit(hex[len]))
+ return -1;
+ }
+
+ if (!len)
+ return 0;
+
+ switch (len % 2) {
+ while (len > 0) {
+ case 0: tmp[0] = *hex++; len--;
+ case 1: tmp[1] = *hex++; len--;
+
+ if (count < n)
+ buf[count] = strtoul(tmp, NULL, 16);
+ count++;
+ }
+ }
+
+ return count;
+}
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
-#include <ctype.h>
#include <getopt.h>
#include <upkg.h>
+#include "common.h"
#define PROGNAME "decodeindex"
static const char *progname = PROGNAME;
{ 0 }
};
-/*
- * Decode a hexadecimal string into a sequence of bytes. If there are an
- * odd number of nibbles, treat the first character as the least significant
- * nibble of the first byte. The result is written to the buffer specified by
- * buf. At most n bytes are written to the buffer.
- *
- * Returns the number of bytes that would be written provided that n was large
- * enough, or (size_t)-1 if the input is not valid.
- */
-static size_t decode_hex(const char *hex, unsigned char *buf, size_t n)
-{
- size_t len, count = 0;
- char tmp[] = "00";
-
- for (len = 0; hex[len]; len++) {
- if (!isxdigit(hex[len]))
- return -1;
- }
-
- if (!len)
- return 0;
-
- switch (len % 2) {
- while (len > 0) {
- case 0: tmp[0] = *hex++; len--;
- case 1: tmp[1] = *hex++; len--;
-
- if (count < n)
- buf[count] = strtoul(tmp, NULL, 16);
- count++;
- }
- }
-
- return count;
-}
-
static void print_usage(FILE *f)
{
fprintf(f, "Usage: %s [options] index [index ...]\n", progname);
size_t n, rc;
int ret = -1;
- n = decode_hex(hex, buf, sizeof buf);
+ n = test_decode_hex(hex, buf, sizeof buf);
if (n == -1) {
fprintf(stderr, "%s: invalid hex sequence: %s\n",
progname, hex);
--- /dev/null
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <engine/pcx.h>
+#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 <http://gnu.org/licenses/gpl.html>.");
+ 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;
+}