]> git.draconx.ca Git - liblbx.git/blob - src/lbximg.c
Implement decoding for lbximg.
[liblbx.git] / src / lbximg.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <limits.h>
6 #include <assert.h>
7 #include <getopt.h>
8 #include <errno.h>
9
10 #include <png.h>
11
12 #include "image.h"
13 #include "lbx.h"
14
15 /* Global flags */
16 static int verbose = 0;
17 static char *outname = "out";
18
19 static const char *progname;
20 #define errmsg(fmt, ...) (\
21         fprintf(stderr, "%s: " fmt, progname, __VA_ARGS__)\
22 )
23
24 enum {
25         MODE_NONE,
26         MODE_DECODE,
27         MODE_IDENT,
28 };
29
30 int parserange(struct lbx_imginfo *info, char *str, unsigned char *bits)
31 {
32         unsigned long start, end;
33         unsigned int i;
34         char *endptr;
35
36         start = strtoul(str, &endptr, 0);
37         if (start >= info->nframes) {
38                 errmsg("frame %lu out of range.\n", start);
39                 return -1;
40         }
41
42         if (endptr == str) {
43                 errmsg("invalid frame range: %s.\n", str);
44                 return -1;
45         }
46
47         switch (*endptr) {
48         case '\0':
49                 end = start;
50                 break;
51         case '-':
52                 end = strtoul(endptr+1, &endptr, 0);
53                 if (end >= info->nframes) {
54                         errmsg("frame %lu out of range.\n", end);
55                         return -1;
56                 }
57
58                 if (endptr == str)
59                         end = info->nframes - 1;
60                 break;
61         default:
62                 errmsg("invalid frame range: %s.\n", str);
63                 return -1;
64         }
65
66         if (end < start) {
67                 errmsg("invalid frame range: %s.\n", str);
68                 return -1;
69         }
70
71         for (i = start; i <= end; i++) {
72                 bits[i / CHAR_BIT] |= 1 << (i % CHAR_BIT);
73         }
74
75         return 0;
76 }
77
78 int outpng(unsigned int frameno, unsigned char **framedata,
79            unsigned int width,   unsigned int height,
80            struct lbx_colour palette[static 256])
81 {
82         char name[strlen(outname) + sizeof ".65535.png"];
83         FILE *of;
84
85         png_structp png;
86         png_infop   info;
87
88         assert(frameno < 65536);
89         snprintf(name, sizeof name, "%s.%03d.png", outname, frameno);
90         
91         of = fopen(name, "wb");
92         if (!of) {
93                 errmsg("failed to open %s: %s.\n", name, strerror(errno));
94                 return -1;
95         }
96
97         png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
98         if (!png) {
99                 errmsg("failed to init libpng.\n", 0);
100                 goto err;
101         }
102
103         info = png_create_info_struct(png);
104         if (!info) {
105                 errmsg("failed to init libpng.\n", 0);
106                 png_destroy_write_struct(&png, NULL);
107                 goto err;
108         }
109
110         if (setjmp(png_jmpbuf(png))) {
111                 png_destroy_write_struct(&png, &info);
112                 goto err;
113         }
114
115         png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_PALETTE,
116                      PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
117                      PNG_FILTER_TYPE_DEFAULT);
118         png_set_PLTE(png, info, (png_colorp)palette, 256);
119         png_set_rows(png, info, framedata);
120
121         png_init_io(png, of);
122         png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL);
123
124         png_destroy_write_struct(&png, &info);
125         fclose(of);
126
127         if (verbose)
128                 printf("wrote %s\n", name);
129
130         return 0;
131 err:
132         fclose(of);
133         remove(name);
134         return -1;
135 }
136
137 int decode(LBX_IMG *img, FILE *palf, char **argv)
138 {
139         unsigned char *framebits;
140         struct lbx_colour palette[256];
141         struct lbx_imginfo info;
142         int extracted = 0;
143         unsigned int i;
144
145         lbximg_getinfo(img, &info);
146
147         framebits = malloc(info.nframes / CHAR_BIT + 1);
148         if (!framebits) {
149                 return -1;
150         }
151
152         /* Figure out what images we're extracting. */
153         if (!argv[0]) {
154                 /* extract all images by default. */
155                 memset(framebits, -1, info.nframes / CHAR_BIT + 1);
156         } else {
157                 for (i = 0; argv[i]; i++) {
158                         parserange(&info, argv[i], framebits);
159                 }
160         }
161
162         /* Read image palette */
163         if (palf) {
164                 if (lbximg_loadpalette(palf, palette) == -1) {
165                         errmsg("error reading palette: %s\n", lbx_strerror());
166                         goto err;
167                 }
168         } else {
169                 fprintf(stderr, "warning: no palette specified; "
170                                 "decoded images will likely be very pink.\n");
171
172                 for (i = 0; i < 256; i++)
173                         palette[i] = (struct lbx_colour){0xff, 0x00, 0xff};
174         }
175
176         if (lbximg_getpalette(img, palette) == -1) {
177                 errmsg("error reading palette override: %s\n", lbx_strerror());
178                 goto err;
179         }
180
181         /* Extract the images, in order. */
182         for (i = 0; i < info.nframes; i++) {
183                 unsigned char **framedata;
184
185                 if (!(framebits[i / CHAR_BIT] & (1 << (i % CHAR_BIT))))
186                         continue;
187
188                 framedata = lbximg_getframe(img, i);
189                 if (!framedata) {
190                         errmsg("error in frame %u: %s\n", i, lbx_strerror());
191                         continue;
192                 }
193
194                 if (!outpng(i, framedata, info.width, info.height, palette)) {
195                         extracted = 1;
196                 }
197         }
198
199         if (!extracted) {
200                 errmsg("no frames extracted.\n", 0);
201                 goto err;
202         }
203         
204         free(framebits);
205         return EXIT_SUCCESS;
206 err:
207         free(framebits);
208         return EXIT_FAILURE;
209 }
210
211 int main(int argc, char **argv)
212 {
213         int mode = MODE_NONE;
214         FILE *inf = stdin, *palf = NULL;
215         const char *name = "stdin";
216         LBX_IMG *img;
217         int opt;
218
219         static const char *sopts = "idvf:p:";
220         static const struct option lopts[] = {
221                 { "info",    0, NULL, 'i' },
222                 { "decode",  0, NULL, 'd' },
223                 { "verbose", 0, NULL, 'v' },
224                 { "file",    1, NULL, 'f' },
225                 { "palette", 1, NULL, 'p' },
226
227                 { 0 }
228         };
229
230         progname = "lbximg"; /* argv[0]; */
231         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
232                 switch(opt) {
233                 case 'i':
234                         mode = MODE_IDENT;
235                         break;
236                 case 'd':
237                         mode = MODE_DECODE;
238                         break;
239                 case 'v':
240                         verbose = 1;
241                         break;
242                 case 'f':
243                         if (strcmp(optarg, "-") == 0)
244                                 break;
245
246                         name = strrchr(optarg, '/');
247                         name = name ? name+1 : optarg;
248
249                         inf = fopen(optarg, "rb");
250                         if (!inf) {
251                                 errmsg("failed to open %s: %m\n", optarg);
252                                 return EXIT_FAILURE;
253                         }
254                         break;
255                 case 'p':
256                         palf = fopen(optarg, "rb");
257                         if (!palf) {
258                                 errmsg("failed to open %s: %m\n", optarg);
259                                 return EXIT_FAILURE;
260                         }
261
262                         break;
263                 default:
264                         return EXIT_FAILURE;
265                 }
266         }
267
268         if (mode == MODE_NONE) {
269                 errmsg("you must specify a mode.\n", 0);
270                 return EXIT_FAILURE;
271         }
272
273         img = lbximg_fopen(inf);
274         if (!img) {
275                 errmsg("failed to open image: %s.\n", lbx_strerror());
276                 return EXIT_FAILURE;
277         }
278
279         if (verbose || mode == MODE_IDENT) {
280                 struct lbx_imginfo info;
281                 lbximg_getinfo(img, &info);
282
283                 printf("%s is %ux%u LBX image, %u frames\n",
284                        name, info.width, info.height, info.nframes);
285         }
286
287         switch (mode) {
288         case MODE_DECODE:
289                 if (decode(img, palf, &argv[optind])) {
290                         lbximg_close(img);
291                         return EXIT_FAILURE;
292                 }
293                 break;
294         }
295
296         lbximg_close(img);
297         return EXIT_SUCCESS;
298 }