typedef int img_output_func(FILE *f, const char *filename,
unsigned width, unsigned height,
- unsigned char **framedata, unsigned char **mask,
+ unsigned char *pixels, unsigned char *pixel_mask,
struct lbx_colour *palette);
img_output_func img_output_pbm, img_output_ppm, img_output_pam;
img_output_func img_output_png;
-_Bool img_is_masked(unsigned char **mask, unsigned width, unsigned height);
+_Bool img_is_masked(unsigned char *mask, unsigned width, unsigned height);
#endif
/*
* 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
#include <stdbool.h>
#include <string.h>
#include <limits.h>
+#include <stdint.h>
#include <assert.h>
#include <getopt.h>
#include <errno.h>
#include "imgoutput.h"
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
/* Global flags */
static int verbose = 0;
static char *outname = "out";
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;
}
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])
{
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;
tool_err(0, "error writing %s", name);
return -1;
}
-
+
if (verbose)
printf("wrote %s\n", name);
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. */
}
/* 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;
+ }
}
}
ret = EXIT_FAILURE;
}
err:
+ free(pixels);
+ free(pixel_mask);
free(framebits);
return ret;
}
/*
* 2ooM: The Master of Orion II Reverse Engineering Project
* PNG output routines for lbximg extration.
- * Copyright © 2013 Nick Bowler
+ * Copyright © 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
static void
write_rgba_frame(png_structp png, png_infop info,
- unsigned char **framedata, unsigned char **mask,
+ unsigned char *pixels, unsigned char *mask,
struct lbx_colour *lbx_palette)
{
png_uint_32 width = png_get_image_width(png, info);
fill_png_palette(png_palette, lbx_palette);
for (png_uint_32 y = 0; y < height; y++) {
for (png_uint_32 x = 0; x < width; x++) {
- row[x][0] = png_palette[framedata[y][x]].red;
- row[x][1] = png_palette[framedata[y][x]].green;
- row[x][2] = png_palette[framedata[y][x]].blue;
- row[x][3] = mask[y][x] ? -1 : 0;
+ unsigned long offset = (unsigned long) y * width + x;
+ png_color *c = &png_palette[pixels[offset]];
+ bool vis;
+
+ vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT);
+ row[x][0] = c->red;
+ row[x][1] = c->green;
+ row[x][2] = c->blue;
+ row[x][3] = -vis;
}
png_write_row(png, (void *)row);
}
static void
write_masked_index_frame(png_structp png, png_infop info,
- unsigned char **framedata, unsigned char **mask)
+ unsigned char *pixels, unsigned char *mask)
{
png_uint_32 width = png_get_image_width(png, info);
png_uint_32 height = png_get_image_height(png, info);
png_write_info(png, info);
for (png_uint_32 y = 0; y < height; y++) {
for (png_uint_32 x = 0; x < width; x++) {
- row[x][0] = framedata[y][x];
- row[x][1] = mask[y][x] ? -1 : 0;
+ unsigned long offset = (unsigned long) y * width + x;
+ bool vis;
+
+ vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT);
+ row[x][0] = pixels[offset];
+ row[x][1] = -vis;
}
png_write_row(png, (void *)row);
}
}
static void
-write_palette_frame(png_structp png, png_infop info,
- unsigned char **framedata, struct lbx_colour *lbx_palette)
+write_index_frame(png_structp png, png_infop info, unsigned char *pixels)
{
- png_color png_palette[256];
+ png_uint_32 width = png_get_image_width(png, info);
+ png_uint_32 height = png_get_image_height(png, info);
- fill_png_palette(png_palette, lbx_palette);
- png_set_PLTE(png, info, png_palette, 256);
- png_set_rows(png, info, framedata);
- png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL);
+ png_write_info(png, info);
+ for (png_uint_32 y = 0; y < height; y++) {
+ unsigned long offset = (unsigned long) y * width;
+
+ png_write_row(png, (void *)(pixels+offset));
+ }
+ png_write_end(png, NULL);
}
static void
-write_index_frame(png_structp png, png_infop info, unsigned char **framedata)
+write_palette_frame(png_structp png, png_infop info,
+ unsigned char *pixels, struct lbx_colour *lbx_palette)
{
- png_set_rows(png, info, framedata);
- png_write_png(png, info, PNG_TRANSFORM_IDENTITY, NULL);
+ png_color png_palette[256];
+ fill_png_palette(png_palette, lbx_palette);
+ png_set_PLTE(png, info, png_palette, 256);
+
+ write_index_frame(png, info, pixels);
}
int img_output_png(FILE *f, const char *filename,
unsigned width, unsigned height,
- unsigned char **framedata, unsigned char **mask,
- struct lbx_colour *palette)
+ unsigned char *pixels, unsigned char *mask,
+ struct lbx_colour *palette)
{
bool masked = img_is_masked(mask, width, height);
struct file_info file = { filename, f };
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
- write_rgba_frame(png, info, framedata, mask, palette);
+ write_rgba_frame(png, info, pixels, mask, palette);
} else if (palette) {
png_set_IHDR(png, info, width, height, 8,
PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
- write_palette_frame(png, info, framedata, palette);
+ write_palette_frame(png, info, pixels, palette);
} else if (masked) {
png_set_IHDR(png, info, width, height, 8,
PNG_COLOR_TYPE_GRAY_ALPHA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
- write_masked_index_frame(png, info, framedata, mask);
+ write_masked_index_frame(png, info, pixels, mask);
} else {
png_set_IHDR(png, info, width, height, 8,
PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
- write_index_frame(png, info, framedata);
+ write_index_frame(png, info, pixels);
}
png_destroy_write_struct(&png, &info);
/*
* 2ooM: The Master of Orion II Reverse Engineering Project
* Netpbm output routines for lbximg extration.
- * Copyright © 2013 Nick Bowler
+ * Copyright © 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
#include <stdarg.h>
#include <assert.h>
#include <inttypes.h>
+#include <limits.h>
#include "image.h"
#include "tools.h"
*/
int img_output_pbm(FILE *f, const char *filename,
unsigned width, unsigned height,
- unsigned char **framedata, unsigned char **mask,
- struct lbx_colour *palette)
+ unsigned char *pixels, unsigned char *mask,
+ struct lbx_colour *palette)
{
unsigned x, y;
goto err;
for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
+ unsigned long offset = (unsigned long) y * width + x;
+ bool vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT);
+
if (fputc(to_ascii(x > 0 ? ' ' : '\n'), f) == EOF)
goto err;
- if (mask[y][x]) {
- if (fputc(to_ascii('0'), f) == EOF)
- goto err;
- } else {
- if (fputc(to_ascii('1'), f) == EOF)
- goto err;
- }
+ if (fputc(to_ascii(vis ? '0' : '1'), f) == EOF)
+ goto err;
}
if (fputc(to_ascii('\n'), f) == EOF)
* normal output format.
*/
static int write_pgm(FILE *f, unsigned width, unsigned height,
- unsigned char **framedata, unsigned char **mask)
+ unsigned char *pixels, unsigned char *mask)
{
unsigned x, y;
return -1;
for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
+ unsigned long offset = (unsigned long) y * width + x;
+ bool vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT);
+
if (fputc(to_ascii(x > 0 ? ' ' : '\n'), f) == EOF)
return -1;
- if (!mask[y][x]) {
- if (fprintf_ascii(f, " 0") < 0)
- return -1;
- } else {
- if (fprintf_ascii(f, "%3hhu", framedata[y][x]) < 0)
- return -1;
- }
+ if (fprintf_ascii(f, "%3hhu", vis ? pixels[offset] : 0) < 0)
+ return -1;
}
if (fputc(to_ascii('\n'), f) == EOF)
*/
int img_output_ppm(FILE *f, const char *filename,
unsigned width, unsigned height,
- unsigned char **framedata, unsigned char **mask,
- struct lbx_colour *palette)
+ unsigned char *pixels, unsigned char *mask,
+ struct lbx_colour *palette)
{
unsigned x, y;
* For no-palette mode, write a PGM instead which is basically
* the same format but has only one value per pixel.
*/
- if (write_pgm(f, width, height, framedata, mask) < 0)
+ if (write_pgm(f, width, height, pixels, mask) < 0)
goto err;
return 0;
}
goto err;
for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
+ unsigned long offset = (unsigned long) y * width + x;
+ bool vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT);
+ struct lbx_colour c = { 0 };
+
if (fputc(to_ascii(x > 0 ? ' ' : '\n'), f) == EOF)
goto err;
- if (!mask[y][x]) {
- if (fprintf_ascii(f, " 0 0 0") < 0)
- goto err;
- } else {
- struct lbx_colour *c = &palette[framedata[y][x]];
+ if (vis)
+ c = palette[pixels[offset]];
- if (fprintf_ascii(f, "%2d %2d %2d",
- c->red, c->green, c->blue) < 0)
- goto err;
- }
+ if (fprintf_ascii(f, "%2d %2d %2d", c.red, c.green, c.blue) < 0)
+ goto err;
}
if (fputc(to_ascii('\n'), f) == EOF)
*/
int img_output_pam(FILE *f, const char *filename,
unsigned width, unsigned height,
- unsigned char **framedata, unsigned char **mask,
- struct lbx_colour *palette)
+ unsigned char *pixels, unsigned char *mask,
+ struct lbx_colour *palette)
{
bool masked = img_is_masked(mask, width, height);
unsigned x, y;
goto err;
for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
- struct lbx_colour *c = &palette[framedata[y][x]];
+ unsigned long offset = (unsigned long) y * width + x;
+ struct lbx_colour *c = &palette[pixels[offset]];
unsigned char buf[4];
+ bool vis;
+ vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT);
buf[0] = c->red;
buf[1] = c->green;
buf[2] = c->blue;
- buf[3] = mask[y][x] ? 63 : 0;
+ buf[3] = vis ? 63 : 0;
if (fwrite(buf, 1, depth, f) < depth)
goto err;
goto err;
for (x = y = 0; y < height; ++x < width || (x = 0, y++)) {
+ unsigned long offset = (unsigned long) y * width + x;
unsigned char buf[2];
+ bool vis;
- buf[0] = framedata[y][x];
- buf[1] = mask[y][x] ? 0xff : 0;
+ vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT);
+ buf[0] = pixels[offset];
+ buf[1] = vis ? 255 : 0;
if (fwrite(buf, 1, depth, f) < depth)
goto err;