]> git.draconx.ca Git - upkg.git/blob - test/pcxrle.c
tests: Add --version output to the common library.
[upkg.git] / test / pcxrle.c
1 /*
2  * Tool for testing PCX run-length encoding.
3  * Copyright © 2012, 2022 Nick Bowler
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18
19 #include <config.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <getopt.h>
24 #include <errno.h>
25 #include <assert.h>
26
27 #include <engine/pcx.h>
28 #include "common.h"
29
30 #define PROGNAME "pcxrle"
31 static const char *progname = PROGNAME;
32 static const char sopts[] = "VH";
33 static const struct option lopts[] = {
34         { "version", 0, NULL, 'V' },
35         { "help",    0, NULL, 'H' },
36         { 0 }
37 };
38
39 static void print_usage(FILE *f)
40 {
41         fprintf(f, "Usage: %s [options] bytes [bytes ...]\n", progname);
42 }
43
44 static void print_help(void)
45 {
46         print_usage(stdout);
47         test_print_options(lopts);
48 }
49
50 static void rle_error(const unsigned char *raw, size_t n, const char *str)
51 {
52         fprintf(stderr, "%s: %s\n", progname, str);
53         fprintf(stderr, "%s: raw input was:\n", progname);
54
55         fprintf(stderr, "%*s", 4, "");
56         for (size_t i = 0; i < n; i++)
57                 printf("%.2hhx", raw[i]);
58         putchar('\n');
59 }
60
61 static int compare_rle(const unsigned char *rle_buf, size_t rle_len,
62                        const unsigned char *raw_buf, size_t raw_len)
63 {
64         unsigned char tmp[raw_len];
65         size_t offset = 0;
66
67         for (size_t i = 0; i < rle_len; i++) {
68                 size_t count = 1;
69
70                 if ((rle_buf[i] & 0xc0) == 0xc0) {
71                         count = rle_buf[i] & 0x3f;
72                         if (++i >= rle_len) {
73                                 rle_error(raw_buf, raw_len, "invalid RLE encoding");
74                                 return -1;
75                         }
76                 }
77
78                 if (offset + count > raw_len) {
79                         rle_error(raw_buf, raw_len, "RLE expansion exceeds raw size");
80                         return -1;
81                 }
82
83                 memset(tmp+offset, rle_buf[i], count);
84                 offset += count;
85         }
86
87         if (offset < raw_len) {
88                 rle_error(raw_buf, raw_len, "RLE expansion falls short of raw size");
89                 return -1;
90         }
91
92         if (memcmp(raw_buf, tmp, raw_len) != 0) {
93                 rle_error(raw_buf, raw_len, "RLE expansion does not match raw data");
94                 return -1;
95         }
96
97         return 0;
98 }
99
100 static int encode_rle(FILE *tmp, const char *hex)
101 {
102         unsigned char buf[256], rle_buf[sizeof buf*2];
103         struct pcx_head head;
104         int rc, ret = -1;
105         long offset;
106         size_t n;
107
108         n = test_decode_hex(hex, buf, sizeof buf);
109         if (n == -1) {
110                 fprintf(stderr, "%s: invalid hex sequence: %s\n",
111                                 progname, hex);
112                 return -1;
113         } else if (n == 0) {
114                 fprintf(stderr, "%s: empty argument\n", progname);
115                 return -1;
116         } else if (n > sizeof buf) {
117                 fprintf(stderr, "%s: hex sequence too long: %s\n",
118                                 progname, hex);
119                 return -1;
120         }
121
122         rewind(tmp);
123         head.width = head.height = n;
124         rc = pcx_write_scanline(&head, buf, tmp);
125         if (rc != 0) {
126                 fprintf(stderr, "%s: failed to encode data: %s\n",
127                                 progname, strerror(errno));
128                 return -1;
129         }
130
131         offset = ftell(tmp);
132         if (offset < 0) {
133                 fprintf(stderr, "%s: failed to get file offset: %s\n",
134                                 progname, strerror(errno));
135                 return -1;
136         }
137         rewind(tmp);
138
139         if (fread(rle_buf, offset, 1, tmp) != 1) {
140                 fprintf(stderr, "%s: failed to read RLE data: %s\n",
141                                 progname, strerror(errno));
142                 return -1;
143         }
144
145         ret = compare_rle(rle_buf, offset, buf, n);
146         for (long i = 0; i < offset; i++)
147                 printf("%.2hhx", rle_buf[i]);
148         putchar('\n');
149         return ret;
150 }
151
152 int main(int argc, char **argv)
153 {
154         int opt, ret = EXIT_SUCCESS;
155         FILE *tmp;
156
157         if (argc > 0)
158                 progname = argv[0];
159
160         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
161                 switch (opt) {
162                 case 'V':
163                         test_print_version(PROGNAME);
164                         return EXIT_SUCCESS;
165                 case 'H':
166                         print_help();
167                         return EXIT_SUCCESS;
168                 default:
169                         print_usage(stderr);
170                         return EXIT_FAILURE;
171                 }
172         }
173
174         if (!argv[optind]) {
175                 print_usage(stderr);
176                 return EXIT_FAILURE;
177         }
178
179         tmp = tmpfile();
180         if (!tmp) {
181                 fprintf(stderr, "%s: failed to create temporary file: %s\n",
182                                 progname, strerror(errno));
183                 return EXIT_FAILURE;
184         }
185
186         for (int i = optind; i < argc; i++) {
187                 if (encode_rle(tmp, argv[i]) != 0) {
188                         ret = EXIT_FAILURE;
189                 }
190         }
191
192         return ret;
193 }