X-Git-Url: http://git.draconx.ca/gitweb/liblbx.git/blobdiff_plain/f58512db610bb5ce272a142a552b8bf75b8c2f6e..85ab8496645e958bbc36b64cd2827e0c47c761eb:/src/lbximg.c diff --git a/src/lbximg.c b/src/lbximg.c index 0fd3693..dc4697b 100644 --- a/src/lbximg.c +++ b/src/lbximg.c @@ -1,7 +1,7 @@ /* * 2ooM: The Master of Orion II Reverse Engineering Project * Simple command-line tool to convert an LBX image to other formats. - * Copyright © 2006-2011, 2013 Nick Bowler + * Copyright © 2006-2011, 2013-2014 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 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,8 @@ #include "imgoutput.h" +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + /* Global flags */ static int verbose = 0; static char *outname = "out"; @@ -97,13 +100,21 @@ static int lookup_format(const char *fmt) return -1; } -bool img_is_masked(unsigned char **mask, unsigned width, unsigned height) +bool img_is_masked(unsigned char *mask, unsigned width, unsigned height) { - unsigned x, y; - - for (x = y = 0; y < height; ++x < width || (x = 0, y++)) { - if (mask[y][x] == 0) - return true; + unsigned long npixels = (unsigned long) width * height; + unsigned long mask_sz = npixels / CHAR_BIT + (npixels % CHAR_BIT != 0); + + for (unsigned long i = 0; i < mask_sz; i++) { + if (i+1 < mask_sz) { + if (mask[i] != (unsigned char)-1) + return true; + } else { + unsigned char test = (1u << npixels % CHAR_BIT) - 1; + + if ((mask[i] & test) != test) + return true; + } } return false; @@ -158,7 +169,7 @@ int parserange(unsigned frames, char *str, unsigned char *bits) } int output(unsigned int frameno, const struct img_format *fmt, - unsigned char **framedata, unsigned char **mask, + unsigned char *pixels, unsigned char *pixel_mask, unsigned int width, unsigned int height, struct lbx_colour palette[static 256]) { @@ -176,7 +187,7 @@ int output(unsigned int frameno, const struct img_format *fmt, return -1; } - rc = fmt->output(of, name, width, height, framedata, mask, palette); + rc = fmt->output(of, name, width, height, pixels, pixel_mask, palette); if (rc < 0) { fclose(of); return -1; @@ -186,7 +197,7 @@ int output(unsigned int frameno, const struct img_format *fmt, tool_err(0, "error writing %s", name); return -1; } - + if (verbose) printf("wrote %s\n", name); @@ -257,23 +268,117 @@ static int loadpalette(struct lbx_image *img, struct lbx_imginfo *info, return 0; } +/* Return true iff a divides b. */ +static bool divides(unsigned a, unsigned b) +{ + if (b == 0) + return true; + if (a == 0) + return false; + + return b % a == 0; +} + +/* Set n bits starting from offset in the bitmap. */ +static void +set_bits(unsigned char *bitmap, unsigned long offset, unsigned long n) +{ + if (offset % CHAR_BIT) { + bitmap[offset/CHAR_BIT] |= (n >= CHAR_BIT ? -1u : (1u << n) - 1) + << offset%CHAR_BIT; + + n -= MIN(n, CHAR_BIT - offset%CHAR_BIT); + offset += CHAR_BIT - offset%CHAR_BIT; + } + + if (n > CHAR_BIT) { + memset(&bitmap[offset/CHAR_BIT], -1, n/CHAR_BIT); + + offset += n - n%CHAR_BIT; + n %= CHAR_BIT; + } + + if (n) { + bitmap[offset/CHAR_BIT] |= (1u << n) - 1; + } +} + +static int decode_frame(struct lbx_image *img, unsigned n, + unsigned char *pixels, unsigned char *pixel_mask) +{ + unsigned x, y; + long rc; + + rc = lbx_img_seek(img, n); + if (rc < 0) { + tool_err(-1, "frame %u: invalid frame: %s\n", n, lbx_errmsg()); + return -1; + } + + while ((rc = lbx_img_read_row_header(img, &x, &y)) != 0) { + unsigned long offset; + + if (rc < 0) { + tool_err(-1, "frame %u: invalid row: %s", n, lbx_errmsg()); + return -1; + } + + offset = (unsigned long) y * img->width + x; + rc = lbx_img_read_row_data(img, pixels+offset); + if (rc < 0) { + tool_err(-1, "frame %u: error reading row: %s\n", n, lbx_errmsg()); + return -1; + } + + set_bits(pixel_mask, offset, rc); + } + + return 0; +} + static int decode(struct lbx_image *img, FILE *palf, FILE *override, int fmt, char **argv) { - unsigned char *framebits; + unsigned char *pixels = NULL, *pixel_mask = NULL, *framebits = NULL; struct lbx_colour palette[256]; struct lbx_imginfo info; - int ret = EXIT_SUCCESS; + int rc, ret = EXIT_FAILURE; int extracted = 0; unsigned int i; + size_t npixels, mask_sz; assert(fmt >= 0 && fmt < sizeof formats / sizeof formats[0]); lbx_img_getinfo(img, &info); + npixels = img->width; + if (img->height && npixels >= SIZE_MAX / img->height) { + tool_err(-1, "image too large"); + goto err; + } + npixels *= img->height; + + /* Ensure there is at least 1 byte to allocate */ + if (npixels == 0) + npixels = 1; + framebits = calloc(1, img->frames / CHAR_BIT + 1); if (!framebits) { - return EXIT_FAILURE; + tool_err(0, "failed to allocate memory"); + goto err; + } + + pixels = calloc(img->width, img->height); + if (!pixels) { + tool_err(0, "failed to allocate memory"); + goto err; + } + + mask_sz = npixels / CHAR_BIT + (npixels % CHAR_BIT != 0); + pixel_mask = malloc(mask_sz); + if (!pixel_mask) { + tool_err(0, "failed to allocate memory"); + goto err; } /* Figure out what images we're extracting. */ @@ -294,26 +399,25 @@ decode(struct lbx_image *img, FILE *palf, FILE *override, int fmt, char **argv) } /* Extract the images, in order. */ + ret = EXIT_SUCCESS; for (i = 0; i < img->frames; i++) { - unsigned char **data; - unsigned char **mask; - - if (!(framebits[i / CHAR_BIT] & (1 << (i % CHAR_BIT)))) - continue; + if (divides(img->chunk, i)) + memset(pixel_mask, 0, mask_sz); - data = lbx_img_getframe(img, i); - if (!data) { - tool_err(-1, "error in frame %u: %s", i, lbx_errmsg()); + rc = decode_frame(img, i, pixels, pixel_mask); + if (rc < 0) { ret = EXIT_FAILURE; - continue; + goto err; } - mask = lbx_img_getmask(img); + if (framebits[i / CHAR_BIT] & (1u << (i % CHAR_BIT))) { + rc = output(i, &formats[fmt], pixels, pixel_mask, + img->width, img->height, + usepalette ? palette : NULL); - if (!output(i, &formats[fmt], data, mask, - img->width, img->height, - usepalette ? palette : NULL)) { - extracted = 1; + if (rc == 0) { + extracted = 1; + } } } @@ -322,6 +426,8 @@ decode(struct lbx_image *img, FILE *palf, FILE *override, int fmt, char **argv) ret = EXIT_FAILURE; } err: + free(pixels); + free(pixel_mask); free(framebits); return ret; } @@ -331,6 +437,7 @@ int main(int argc, char **argv) int mode = MODE_NONE, fmt, opt, rc = EXIT_FAILURE; struct lbx_pipe_state stdin_handle = { .f = stdin }; const char *file = NULL, *fmtstring = NULL; + const char *ext_palette = NULL, *ovr_palette = NULL; FILE *palf = NULL, *overf = NULL; struct lbx_image *img; @@ -376,19 +483,11 @@ int main(int argc, char **argv) usepalette = 0; break; case 'p': - palf = fopen(optarg, "rb"); - if (!palf) { - tool_err(0, "failed to open %s", optarg); - return EXIT_FAILURE; - } + ext_palette = optarg; break; case 'O': - overf = fopen(optarg, "rb"); - if (!overf) { - tool_err(0, "failed to open %s", optarg); - return EXIT_FAILURE; - } + ovr_palette = optarg; break; case OPT_PREFIX: outname = optarg; @@ -426,6 +525,16 @@ int main(int argc, char **argv) return EXIT_FAILURE; } + if (ext_palette && !(palf = fopen(ext_palette, "rb"))) { + tool_err(0, "failed to open %s", optarg); + return EXIT_FAILURE; + } + + if (ovr_palette && !(overf = fopen(ovr_palette, "rb"))) { + tool_err(0, "failed to open %s", optarg); + return EXIT_FAILURE; + } + if (verbose || mode == MODE_IDENT) { struct lbx_imginfo info; @@ -450,5 +559,9 @@ int main(int argc, char **argv) } lbx_img_close(img); + if (palf) + fclose(palf); + if (overf) + fclose(overf); return rc; }