a82bcf1338
git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2661 8092fc87-c524-0410-9efc-e669fe64eaf9
418 lines
8.2 KiB
C
418 lines
8.2 KiB
C
/*
|
|
* DOS SC2 palette tab convertor (to UQM format)
|
|
* By Alex Volkov (codepro@usa.net), 20051222
|
|
*
|
|
* -- Some code borrowed from 'unani' by Serge van den Boom (svdb@stack.nl)
|
|
* -- Some more stuff borrowed from FSCP by Mudry (mudronyl@dragon.klte.hu)
|
|
*
|
|
* The GPL applies (to this file).
|
|
*
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <malloc.h>
|
|
#if defined(WIN32) && defined(_MSC_VER)
|
|
# include <getopt.h>
|
|
# include <direct.h>
|
|
# define makedir(d) _mkdir(d)
|
|
# define inline __inline
|
|
#else
|
|
# include <unistd.h>
|
|
# define makedir(d) mkdir(d, 0777)
|
|
#endif
|
|
#if defined(__GNUC__)
|
|
# include <alloca.h>
|
|
# define inline __inline__
|
|
#endif
|
|
|
|
#define countof(a) ( sizeof(a)/sizeof(*a) )
|
|
#ifndef offsetof
|
|
# define offsetof(s,m) ( (size_t)(&((s*)0)->m) )
|
|
#endif
|
|
|
|
#if defined(_MSC_VER)
|
|
# define PACKED
|
|
# pragma pack(push, 1)
|
|
#elif defined(__GNUC__)
|
|
# define PACKED __attribute__((packed))
|
|
#endif
|
|
|
|
// File structure types
|
|
typedef struct
|
|
{
|
|
int32_t PACKED compsize; // size after decompression, or -1 if already
|
|
uint16_t PACKED recs; // Number of records
|
|
uint16_t PACKED cbextra; // Size of extra data after rec sizes
|
|
} dos_header_t;
|
|
|
|
typedef uint16_t dos_recsize_t;
|
|
|
|
typedef struct
|
|
{
|
|
int32_t PACKED compsize; // size after decompression, or -1 if already
|
|
uint32_t PACKED recs; // Number of records
|
|
uint32_t PACKED cbextra; // Size of extra data after rec sizes
|
|
} uqm_header_t;
|
|
|
|
typedef uint32_t uqm_recsize_t;
|
|
|
|
// Palette types
|
|
typedef uint8_t channel_t;
|
|
|
|
typedef struct
|
|
{
|
|
channel_t r, g, b;
|
|
} color_t;
|
|
|
|
typedef struct
|
|
{
|
|
uint8_t first;
|
|
uint8_t last;
|
|
union
|
|
{
|
|
color_t colors[1];
|
|
channel_t chans[3];
|
|
} pal;
|
|
} colormap_t;
|
|
|
|
#define DOS_SIG_BITS 6
|
|
|
|
#define MAP_COLORS 0x100
|
|
|
|
typedef union
|
|
{
|
|
color_t colors[MAP_COLORS];
|
|
channel_t chans[MAP_COLORS * 3];
|
|
} uqm_palette_t;
|
|
|
|
typedef struct
|
|
{
|
|
uint8_t first;
|
|
uint8_t last;
|
|
uqm_palette_t pal;
|
|
} uqm_colormap_t;
|
|
|
|
#if defined(_MSC_VER)
|
|
# pragma pack(pop)
|
|
#endif
|
|
|
|
struct options
|
|
{
|
|
char *infile;
|
|
char *outfile;
|
|
int verbose;
|
|
int plut;
|
|
int num_palettefiles;
|
|
char **palettefiles;
|
|
};
|
|
|
|
|
|
int verbose_level = 0;
|
|
void verbose(int level, const char* fmt, ...);
|
|
void parse_arguments(int argc, char* argv[], struct options* opts);
|
|
int loadPalette(uqm_palette_t* pal, const char* filename);
|
|
void setPalette(uqm_palette_t* pal, colormap_t* map);
|
|
dos_recsize_t* processHeader(dos_header_t* h);
|
|
int writeColortab(dos_header_t* h, const char* filename);
|
|
|
|
inline channel_t conv_chan(uint8_t c);
|
|
|
|
inline uint16_t get_16_be(uint16_t* val);
|
|
inline uint32_t get_32_be(uint32_t* val);
|
|
inline void put_16_be(uint16_t* into, uint16_t val);
|
|
inline void put_32_be(uint32_t* into, uint32_t val);
|
|
|
|
uqm_colormap_t basemap;
|
|
int num_maps = 0;
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
struct options opts;
|
|
FILE* in;
|
|
size_t inlen;
|
|
uint8_t* buf;
|
|
dos_header_t* h;
|
|
dos_recsize_t* sizes;
|
|
int ret;
|
|
|
|
parse_arguments(argc, argv, &opts);
|
|
verbose_level = opts.verbose;
|
|
|
|
in = fopen(opts.infile, "rb");
|
|
if (!in)
|
|
{
|
|
verbose(1, "Error: Could not open file %s: %s\n",
|
|
opts.infile, strerror(errno));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
fseek(in, 0, SEEK_END);
|
|
inlen = ftell(in);
|
|
fseek(in, 0, SEEK_SET);
|
|
|
|
buf = malloc(inlen);
|
|
if (!buf)
|
|
{
|
|
verbose(1, "Out of memory reading file\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (inlen != fread(buf, 1, inlen, in))
|
|
{
|
|
verbose(1, "Cannot read file '%s'\n", opts.infile);
|
|
return EXIT_FAILURE;
|
|
}
|
|
fclose(in);
|
|
|
|
h = (dos_header_t*)buf;
|
|
sizes = processHeader(h);
|
|
|
|
{
|
|
int i;
|
|
|
|
memset(&basemap, 0, sizeof(basemap));
|
|
for (i = 0; i < opts.num_palettefiles; i++)
|
|
loadPalette(&basemap.pal, opts.palettefiles[i]);
|
|
}
|
|
basemap.first = opts.plut;
|
|
basemap.last = opts.plut;
|
|
|
|
ret = writeColortab(h, opts.outfile);
|
|
|
|
free(buf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void verbose(int level, const char* fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (verbose_level < level)
|
|
return;
|
|
|
|
va_start(args, fmt);
|
|
vfprintf(stderr, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void usage()
|
|
{
|
|
fprintf(stderr,
|
|
"pal2uqm [-m <plutind>] [-c <basepal>] [-o <outfile>] <infile>\n"
|
|
"Options:\n"
|
|
"\t-c load base palette in dos format (multiple -c allowed)\n"
|
|
"\t-o output .ct filename\n"
|
|
"\t-v increase verbosity level (use more than once)\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, "h?c:o:m:v")))
|
|
{
|
|
switch (ch)
|
|
{
|
|
case 'c':
|
|
opts->palettefiles = realloc(opts->palettefiles,
|
|
(opts->num_palettefiles + 1) * sizeof (char *));
|
|
opts->palettefiles[opts->num_palettefiles] = optarg;
|
|
opts->num_palettefiles++;
|
|
break;
|
|
case 'o':
|
|
opts->outfile = optarg;
|
|
break;
|
|
case 'm':
|
|
opts->plut = atoi(optarg);
|
|
break;
|
|
case 'v':
|
|
opts->verbose++;
|
|
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->outfile)
|
|
opts->outfile = opts->infile;
|
|
}
|
|
|
|
dos_recsize_t* processHeader(dos_header_t* h)
|
|
{
|
|
dos_recsize_t* sizes;
|
|
unsigned i;
|
|
|
|
h->compsize = get_32_be(&h->compsize);
|
|
h->recs = get_16_be(&h->recs);
|
|
h->cbextra = get_16_be(&h->cbextra);
|
|
|
|
sizes = (dos_recsize_t*)((uint8_t*)h + sizeof(*h));
|
|
|
|
for (i = 0; i < h->recs; ++i)
|
|
sizes[i] = get_16_be(sizes + i);
|
|
|
|
return sizes;
|
|
}
|
|
|
|
int loadPalette(uqm_palette_t* pal, const char* filename)
|
|
{
|
|
FILE* in;
|
|
size_t inlen;
|
|
uint8_t *buf;
|
|
|
|
in = fopen(filename, "rb");
|
|
if (!in)
|
|
{
|
|
verbose(1, "Error: Could not open file %s: %s\n", filename,
|
|
strerror(errno));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
fseek(in, 0, SEEK_END);
|
|
inlen = ftell(in);
|
|
fseek(in, 0, SEEK_SET);
|
|
|
|
buf = malloc(inlen);
|
|
if (!buf)
|
|
{
|
|
verbose(1, "Out of memory reading file\n");
|
|
fclose(in);
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (inlen != fread(buf, 1, inlen, in))
|
|
{
|
|
verbose(1, "Cannot read file '%s'\n", filename);
|
|
fclose(in);
|
|
return EXIT_FAILURE;
|
|
}
|
|
fclose(in);
|
|
|
|
verbose(2, "%s contains %d palette entries\n",
|
|
filename, (int)buf[1] - buf[0] + 1);
|
|
|
|
setPalette(pal, (colormap_t*)buf);
|
|
|
|
free(buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void setPalette(uqm_palette_t* pal, colormap_t* map)
|
|
{
|
|
int i;
|
|
color_t* sclr;
|
|
color_t* dclr;
|
|
|
|
for (i = map->first, sclr = map->pal.colors, dclr = pal->colors + map->first;
|
|
i <= (int)map->last;
|
|
++i, ++sclr, ++dclr)
|
|
{
|
|
dclr->r = conv_chan(sclr->r);
|
|
dclr->g = conv_chan(sclr->g);
|
|
dclr->b = conv_chan(sclr->b);
|
|
}
|
|
}
|
|
|
|
int writeColortab(dos_header_t* h, const char* filename)
|
|
{
|
|
FILE* f;
|
|
uqm_header_t uqmh;
|
|
dos_recsize_t* sizes;
|
|
uqm_recsize_t uqmsize;
|
|
colormap_t* map;
|
|
uqm_colormap_t uqmmap;
|
|
int i;
|
|
|
|
put_32_be(&uqmh.compsize, -1);
|
|
put_32_be(&uqmh.recs, h->recs);
|
|
put_32_be(&uqmh.cbextra, 0);
|
|
// colortab sizes are fixed in uqm
|
|
put_32_be(&uqmsize, sizeof(uqmmap));
|
|
|
|
sizes = (dos_recsize_t*)((uint8_t*)h + sizeof(*h));
|
|
|
|
f = fopen(filename, "wb");
|
|
if (!f)
|
|
{
|
|
verbose(1, "Error: Could not open file %s: %s\n", filename,
|
|
strerror(errno));
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (1 != fwrite(&uqmh, sizeof(uqmh), 1, f))
|
|
{
|
|
verbose(1, "Cannot write file '%s'\n", filename);
|
|
fclose(f);
|
|
return EXIT_FAILURE;
|
|
}
|
|
for (i = 0; i < h->recs; ++i)
|
|
fwrite(&uqmsize, sizeof(uqmsize), 1, f);
|
|
|
|
map = (colormap_t*)(sizes + h->recs);
|
|
for (i = 0; i < h->recs; ++i)
|
|
{
|
|
memcpy(&uqmmap, &basemap, sizeof(basemap));
|
|
setPalette(&uqmmap.pal, map);
|
|
|
|
if (1 != fwrite(&uqmmap, sizeof(uqmmap), 1, f))
|
|
{
|
|
verbose(1, "Cannot write file '%s'\n", filename);
|
|
fclose(f);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
map = (colormap_t*)((uint8_t*)map + sizes[i]);
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
inline channel_t conv_chan(uint8_t c)
|
|
{ // this uniformly distributes the lower 'unknown' bits
|
|
return (c << (8 - DOS_SIG_BITS)) | (c >> (DOS_SIG_BITS - (8 - DOS_SIG_BITS)));
|
|
}
|
|
|
|
inline uint16_t get_16_be(uint16_t* val)
|
|
{
|
|
return (uint16_t)(((uint8_t*)val)[0] << 8) | ((uint8_t*)val)[1];
|
|
}
|
|
|
|
inline uint32_t get_32_be(uint32_t* val)
|
|
{
|
|
return (uint32_t)(get_16_be((uint16_t*)val) << 16) | get_16_be((uint16_t*)val + 1);
|
|
}
|
|
|
|
inline void put_16_be(uint16_t* into, uint16_t val)
|
|
{
|
|
((uint8_t*)into)[0] = val >> 8;
|
|
((uint8_t*)into)[1] = val & 0xff;
|
|
}
|
|
|
|
inline void put_32_be(uint32_t* into, uint32_t val)
|
|
{
|
|
put_16_be(((uint16_t*)into) + 0, val >> 16);
|
|
put_16_be(((uint16_t*)into) + 1, val & 0xffff);
|
|
}
|