Basic ani file extracting.

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@158 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
meep-eep
2002-10-27 03:46:29 +00:00
parent e07a2e5df4
commit 5a7e5e7754
3 changed files with 660 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
unani: unani.c unani.h
gcc -W -Wall -g -O0 unani.c -o unani -lpng
clean:
rm unani unani.tgz
tgz: unani.c unani.h Makefile
tar -cvzf unani.tgz unani.c unani.h Makefile
+595
View File
@@ -0,0 +1,595 @@
/*
* .ani file extractor
* By Serge van den Boom (svdb@stack.nl), 20021012
* The GPL applies.
*
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <string.h>
#include <errno.h>
#include "unani.h"
#include <png.h>
struct options {
char *infile;
char *outdir;
char *prefix;
char makeani;
char list;
char print;
char verbose;
int num_palettefiles;
char **palettefiles;
};
int max_palettes;
png_color palettes[256][256];
index_header *readIndex(const uint8 *buf);
void printIndex(const index_header *h, const uint8 *buf, FILE *out);
void generateAni(const index_header *h, const uint8 *buf, const char *prefix,
FILE *out);
void writeFiles(const index_header *h, const uint8 *data, const char *path,
const char *prefix);
void parse_arguments(int argc, char *argv[], struct options *opts);
void listFrames(const index_header *h, const uint8 *buf, FILE *out);
void loadPalette(const char *filename);
int
main(int argc, char *argv[]) {
int in;
struct stat sb;
uint8 *buf;
index_header *h;
struct options opts;
char *prefix;
parse_arguments(argc, argv, &opts);
in = open(opts.infile, O_RDONLY);
if (in == -1) {
fprintf(stderr, "Error: Could not open file %s: %s\n", opts.infile,
strerror(errno));
return EXIT_FAILURE;
}
if (fstat(in, &sb) == -1) {
perror("stat() failed");
return EXIT_FAILURE;
}
buf = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, in, 0);
if (buf == MAP_FAILED) {
perror("mmap() failed");
return EXIT_FAILURE;
}
close(in);
{
int i;
memset(palettes, '\0', sizeof (palettes));
max_palettes = 0;
for (i = 0; i < opts.num_palettefiles; i++)
loadPalette(opts.palettefiles[i]);
}
h = readIndex(buf);
if (opts.print)
printIndex(h, buf, stdout);
if (opts.list) {
listFrames(h, buf, stdout);
}
if (opts.prefix == NULL) {
char *start, *end;
start = strrchr(opts.infile, '/');
if (start == NULL) {
start = opts.infile;
} else
start++;
end = strrchr(start, '.');
if (end == NULL)
end = start + strlen(start);
prefix = alloca(end - start + 1);
memcpy(prefix, start, end - start);
prefix[end - start] = '\0';
} else
prefix = opts.prefix;
if (opts.makeani) {
generateAni(h, buf, prefix, stdout);
}
if (!opts.print && !opts.list && !opts.makeani) {
size_t len;
struct stat sb;
if (access(opts.outdir, W_OK) == -1) {
fprintf(stderr, "Error: Invalid destination dir %s: %s\n",
opts.outdir, strerror(errno));
exit(EXIT_FAILURE);
}
if (stat(opts.outdir, &sb) == -1) {
fprintf(stderr, "Error: Could not stat %s: %s\n", opts.outdir,
strerror(errno));
exit(EXIT_FAILURE);
}
if (!S_ISDIR(sb.st_mode)) {
fprintf(stderr, "Error: %s is not a directory.\n",
opts.outdir);
exit(EXIT_FAILURE);
}
len = strlen(opts.outdir);
if (opts.outdir[len - 1] == '/')
opts.outdir[len - 1] = '\0';
writeFiles(h, buf, opts.outdir, prefix);
}
// freeIndex(h);
munmap(buf, sb.st_size);
return EXIT_SUCCESS;
}
void
usage() {
fprintf(stderr,
"unani -a <infile>\n"
"unani -p <infile>\n"
"unani [-c <clutfile>] [-o <outdir>] [-n <prefix>] <infile>\n");
}
void
parse_arguments(int argc, char *argv[], struct options *opts) {
char ch;
memset(opts, '\0', sizeof (struct options));
while (1) {
ch = getopt(argc, argv, "ac:hln:o:pv");
if (ch == -1)
break;
switch(ch) {
case 'a':
opts->makeani = 1;
break;
case 'c':
opts->palettefiles = realloc(opts->palettefiles,
(opts->num_palettefiles + 1) * sizeof (char *));
opts->palettefiles[opts->num_palettefiles] = optarg;
opts->num_palettefiles++;
break;
case 'l':
opts->list = 1;
break;
case 'o':
opts->outdir = optarg;
break;
case 'n':
opts->prefix = optarg;
break;
case 'p':
opts->print = 1;
break;
case 'v':
opts->verbose = 1;
break;
case '?':
case 'h':
default:
usage();
exit(EXIT_FAILURE);
}
}
argc -= optind;
argv += optind;
if (argc != 1) {
usage();
exit(EXIT_FAILURE);
}
opts->infile = argv[0];
if (opts->outdir == NULL)
opts->outdir = ".";
}
index_header *
readIndex(const uint8 *buf) {
index_header *h;
const uint8 *bufptr;
uint16 i;
h = malloc(sizeof (index_header));
if ((uint32) MAKE_DWORD(buf[3], buf[2], buf[1], buf[0]) != 0xffffffff ||
MAKE_DWORD(buf[7], buf[6], buf[5], buf[4]) != 0x00000000) {
fprintf(stderr, "File is not a valid .ani file.\n");
exit(EXIT_FAILURE);
}
h->num_frames =
INDEX_GET(MAKE_DWORD(buf[11], buf[10], buf[9], buf[8])) + 1;
bufptr = buf + 12;
h->frames = malloc(h->num_frames * sizeof (frame_desc));
for (i = 0; i < h->num_frames; i++) {
uint32 temp;
temp = MAKE_DWORD(bufptr[3], bufptr[2], bufptr[1], bufptr[0]);
h->frames[i].index = INDEX_GET(temp);
h->frames[i].plut = (temp >> 16) & 0xff;
h->frames[i].flags = temp >> 24;
temp = MAKE_DWORD(bufptr[7], bufptr[6], bufptr[5], bufptr[4]);
h->frames[i].hotspot.x = temp & 0xffff;
h->frames[i].hotspot.y = temp >> 16;
temp = MAKE_DWORD(bufptr[11], bufptr[10], bufptr[9], bufptr[8]);
h->frames[i].bounds.w = temp & 0xffff;
h->frames[i].bounds.h = temp >> 16;
h->frames[i].start = bufptr +
MAKE_DWORD(bufptr[15], bufptr[14], bufptr[13], bufptr[12]);
bufptr += 16;
}
return h;
}
void
printIndex(const index_header *h, const uint8 *buf, FILE *out) {
const uint8 *bufptr;
fprintf(out, "0x00000008 Number of frames: %d\n",
h->num_frames);
bufptr = buf + 0x0c;
fprintf(out, "0x0000000c frame info:\n");
{
int i;
for (i = 0; i < h->num_frames; i++) {
fprintf(out, "0x%08x Frame %d:\n", bufptr - buf,
h->frames[i].index);
fprintf(out, "0x%08x Plut index: %d\n", bufptr - buf,
h->frames[i].plut);
fprintf(out, "0x%08x Flags: 0x%02x - %s%s%s%s\n",
bufptr - buf,
h->frames[i].flags,
(h->frames[i].flags & FLAG_DATA_HARDWARE) ?
"DATA_HARDWARE " : "",
(h->frames[i].flags & FLAG_DATA_COPY) ?
"DATA_COPY " : "",
(h->frames[i].flags & FLAG_DATA_PACKED) ?
"DATA_PACKED " : "",
(h->frames[i].flags & FLAG_X_FLIP) ?
"X_FLIP" : "");
bufptr += 4;
fprintf(out, "0x%08x Hotspot: (%d, %d)\n", bufptr - buf,
h->frames[i].hotspot.x, h->frames[i].hotspot.y);
bufptr += 4;
fprintf(out, "0x%08x Size: %dx%d\n", bufptr - buf,
h->frames[i].bounds.w, h->frames[i].bounds.h);
bufptr += 8;
}
}
}
void
listFrames(const index_header *h, const uint8 *buf, FILE *out) {
int i;
for (i = 0; i < h->num_frames; i++) {
fprintf(out, "0x%04x %dx%d image, %s%s%s%s\n",
h->frames[i].index,
h->frames[i].bounds.w, h->frames[i].bounds.h,
(h->frames[i].flags & FLAG_DATA_HARDWARE) ?
"DATA_HARDWARE " : "",
(h->frames[i].flags & FLAG_DATA_COPY) ?
"DATA_COPY " : "",
(h->frames[i].flags & FLAG_DATA_PACKED) ?
"DATA_PACKED " : "",
(h->frames[i].flags & FLAG_X_FLIP) ?
"X_FLIP" : "");
}
(void) buf;
}
void
generateAni(const index_header *h, const uint8 *buf, const char *prefix,
FILE *out) {
int i;
for (i = 0; i < h->num_frames; i++) {
fprintf(out, "%s.%d.%s %d %d %d %d %d %d %d %d\n",
prefix, h->frames[i].index,
(h->frames[i].flags & FLAG_DATA_HARDWARE) ? "cel" :
(h->frames[i].flags & FLAG_DATA_PACKED) ? "png" : "???",
-1, h->frames[i].plut, 0, 0,
h->frames[i].bounds.w, h->frames[i].bounds.h,
h->frames[i].hotspot.x, h->frames[i].hotspot.y);
}
(void) buf;
}
void
writeFile(const char *file, const uint8 *data, size_t len) {
int fd;
// check if all the path components exist
{
const char *ptr;
char path[MAXPATHLEN];
ptr = file;
if (ptr[0] == '/')
ptr++;
while (1) {
ptr = strchr(ptr, '/');
if (ptr == NULL)
break;
ptr++;
memcpy(path, file, ptr - file);
path[ptr - file] = '\0';
if (mkdir(path, 0777) == -1) {
if (errno == EEXIST)
continue;
fprintf(stderr, "Error: Could not make directory %s: %s\n",
path, strerror(errno));
exit(EXIT_FAILURE);
}
}
}
fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd == -1) {
perror("Could not open file for writing");
exit(EXIT_FAILURE);
}
while (len > 0) {
ssize_t written;
written = write(fd, data, len);
if (written < 0) {
if (errno != EINTR) {
perror("write failed");
exit(1);
}
}
len -= written;
data += written;
}
close(fd);
}
void
writeCel(const char *filename, const frame_desc *frame, const uint8 *data) {
writeFile(filename, data + 4,
MAKE_DWORD(data[3], data[2], data[1], data[0]));
(void) frame;
}
void
writePacked(const char *filename, const frame_desc *f,
const uint8 *data) {
// information on the packed format can be found in 3do/imageint.c
uint8 *buf;
const uint8 *src, *srclineend;
uint8 *dst, *dstlineend;
uint8 **lines;
uint8 pack_type, num_pixels;
int i;
buf = alloca(f->bounds.w * f->bounds.h * sizeof (uint8));
memset(buf, '\0', f->bounds.w * f->bounds.h * sizeof (uint8));
dst = buf;
src = data;
lines = alloca(f->bounds.h * sizeof (uint8 *));
for (i = 0; i < f->bounds.h; i++) {
lines[i] = dst;
srclineend = src + ((MAKE_WORD(src[1], src[0]) + 2) << 2);
dstlineend = dst + f->bounds.w;
src += 2;
while (src < srclineend) {
pack_type = PACK_TYPE(*src);
num_pixels = PACK_COUNT(*src);
src++;
switch (pack_type) {
case PACK_EOL:
while (dst != dstlineend)
*(dst++) = 0; // rest is transparant
break;
case PACK_LITERAL:
memcpy(dst, src, num_pixels);
src += num_pixels;
dst += num_pixels;
break;
case PACK_TRANS:
memset(dst, '\0', num_pixels);
dst += num_pixels;
break;
case PACK_REPEAT:
memset(dst, *src, num_pixels);
src++;
dst += num_pixels;
break;
}
}
}
{
FILE *file;
png_structp png_ptr;
png_infop info_ptr;
png_color_8 sig_bit;
png_byte trans[1];
png_ptr = png_create_write_struct
(PNG_LIBPNG_VER_STRING, (png_voidp) NULL /* user_error_ptr */,
NULL /* user_error_fn */, NULL /* user_warning_fn */);
if (!png_ptr) {
fprintf(stderr, "png_create_write_struct failed.\n");
exit(EXIT_FAILURE);
}
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
fprintf(stderr, "png_create_info_struct failed.\n");
exit(EXIT_FAILURE);
}
if (setjmp(png_jmpbuf(png_ptr))) {
fprintf(stderr, "png error.\n");
png_destroy_write_struct(&png_ptr, &info_ptr);
exit(EXIT_FAILURE);
}
file = fopen(filename, "wb+");
if (file == NULL) {
fprintf(stderr, "Could not open file '%s': %s\n",
filename, strerror(errno));
png_destroy_write_struct(&png_ptr, &info_ptr);
exit(EXIT_FAILURE);
}
png_init_io(png_ptr, file);
png_set_IHDR(png_ptr, info_ptr, f->bounds.w, f->bounds.h,
8 /* bit_depth per channel */, PNG_COLOR_TYPE_PALETTE,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
#if 0
sig_bit.red = 8;
sig_bit.green = 8;
sig_bit.blue = 8;
png_set_sBIT(png_ptr, info_ptr, &sig_bit);
png_set_shift(png_ptr, &sig_bit);
#endif
png_set_PLTE(png_ptr, info_ptr, palettes[f->plut], 256);
#if 0
trans[0] = 0;
png_set_tRNS(png_ptr, info_ptr, trans, 1, NULL);
#endif
png_write_info(png_ptr, info_ptr);
png_set_packing(png_ptr);
png_write_image(png_ptr, (png_byte **) lines);
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
}
}
void
writeFiles(const index_header *h, const uint8 *data, const char *path,
const char *prefix) {
int i;
char filename[MAXPATHLEN];
for (i = 0; i < h->num_frames; i++) {
sprintf(filename, "%s/%s.%d.",
path, prefix, h->frames[i].index);
if (h->frames[i].flags & FLAG_DATA_HARDWARE) {
strcat(filename, "cel");
writeCel(filename, &h->frames[i], h->frames[i].start);
} if (h->frames[i].flags & FLAG_DATA_PACKED) {
strcat(filename, "png");
writePacked(filename, &h->frames[i], h->frames[i].start);
} else {
fprintf(stderr, "Frame %d skipped - not supported.\n", i);
}
}
(void) data;
}
void
loadPalette(const char *filename) {
int in;
char *buf;
struct stat sb;
uint16 num_entries;
uint8 *bufptr, *pptr;
uint8 *sizeptr;
uint32 size;
uint8 startpalette, endpalette;
uint32 i, pali, k, l, m;
uint16 cval;
uint16 r, g, b, rtot, gtot, btot;
in = open(filename, O_RDONLY);
if (in == -1) {
fprintf(stderr, "Error: Could not open file %s: %s\n", filename,
strerror(errno));
exit(EXIT_FAILURE);
}
if (fstat(in, &sb) == -1) {
perror("stat() failed");
exit(EXIT_FAILURE);
}
buf = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, in, 0);
if (buf == MAP_FAILED) {
perror("mmap() failed");
exit(EXIT_FAILURE);
}
close(in);
if ((uint32) MAKE_DWORD(buf[3], buf[2], buf[1], buf[0]) != 0xffffffff ||
MAKE_WORD(buf[5], buf[4]) != 0x0000) {
fprintf(stderr, "File is not a valid .ct file.\n");
exit(EXIT_FAILURE);
}
num_entries = MAKE_WORD(buf[7], buf[6]);
fprintf(stderr, "%s contains %d palette entries\n",
filename, num_entries);
sizeptr = buf + 8;
bufptr = sizeptr + (num_entries + 1) * 4;
for (i = 0; i < num_entries; i++) {
size = MAKE_DWORD(sizeptr[3], sizeptr[2], sizeptr[1], sizeptr[0]);
startpalette = bufptr[0];
endpalette = bufptr[1];
fprintf(stderr, "Entry #%d contains palette %d-%d.\n",
i, startpalette, endpalette);
pptr = bufptr + 2;
for (pali = startpalette; pali <= endpalette; pali++) {
for (k = 0; k < 32; k++) {
cval = MAKE_WORD(pptr[1], pptr[0]);
rtot = r = (cval >> 10) & 0x1f; // bits 10-14
gtot = g = (cval >> 5) & 0x1f; // bits 5-9
btot = b = cval & 0x1f; // bits 0-4
for (l = 0; l < 8; l++) {
m = ((l << 5) + k);
palettes[pali][m].red = rtot;
rtot += r;
palettes[pali][m].green = gtot;
gtot += g;
palettes[pali][m].blue = btot;
btot += b;
#if 0
if (rtot > 0xff)
rtot = 0xff;
if (gtot > 0xff)
gtot = 0xff;
if (btot > 0xff)
btot = 0xff;
#endif
}
pptr += 2;
}
}
sizeptr += 4;
bufptr += size;
}
munmap(buf, sb.st_size);
}
+56
View File
@@ -0,0 +1,56 @@
#include <stdint.h>
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef short int16;
#define MAKE_WORD(x1, x2) (((x2) << 8) | (x1))
#define MAKE_DWORD(x1, x2, x3, x4) (((x4) << 24) | ((x3) << 16) | \
((x2) << 8) | (x1))
typedef struct {
int16 x;
int16 y;
} hotspot_type;
typedef struct {
uint16 w;
uint16 h;
} bounds_type;
typedef struct {
uint16 index; // only first 12 bits used
uint8 plut;
uint8 flags;
hotspot_type hotspot;
bounds_type bounds;
// uint32 len;
const uint8 *start;
} frame_desc;
typedef struct {
uint16 num_frames; // only first 12 bits used
frame_desc *frames;
} index_header;
#define FTYPE_SHIFT 12
#define FINDEX_MASK ((1 << FTYPE_SHIFT) - 1)
#define FTYPE_MASK (0xffff & ~FINDEX_MASK)
#define TYPE_GET(f) ((f) & FTYPE_MASK)
#define INDEX_GET(f) ((f) & FINDEX_MASK)
#define FLAG_DATA_HARDWARE (1 << 4)
#define FLAG_DATA_COPY (1 << 5)
#define FLAG_DATA_PACKED (1 << 6)
#define FLAG_X_FLIP (1 << 7)
#define PACK_EOL 0
#define PACK_LITERAL 1
#define PACK_TRANS 2
#define PACK_REPEAT 3
#define PACK_SHIFT 6
#define PACK_TYPE(c) ((uint8) ((c) >> PACK_SHIFT))
#define PACK_COUNT(c) ((uint8) (((c) & ((1 << PACK_SHIFT) - 1)) + 1))