From d47a32435dfe44203c6bc13ead7b2da96927c357 Mon Sep 17 00:00:00 2001 From: avolkov Date: Thu, 3 Feb 2005 07:57:47 +0000 Subject: [PATCH] Colormap conversion tool; from 3DO 32x15bitx8 to 256x24bit git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@1511 8092fc87-c524-0410-9efc-e669fe64eaf9 --- tools/ctconv/Makefile | 33 ++++ tools/ctconv/config.h | 15 ++ tools/ctconv/ctconv.c | 421 ++++++++++++++++++++++++++++++++++++++++ tools/ctconv/ctconv.dsp | 110 +++++++++++ tools/ctconv/ctconv.dsw | 29 +++ tools/ctconv/ctres.h | 62 ++++++ 6 files changed, 670 insertions(+) create mode 100755 tools/ctconv/Makefile create mode 100755 tools/ctconv/config.h create mode 100755 tools/ctconv/ctconv.c create mode 100755 tools/ctconv/ctconv.dsp create mode 100755 tools/ctconv/ctconv.dsw create mode 100755 tools/ctconv/ctres.h diff --git a/tools/ctconv/Makefile b/tools/ctconv/Makefile new file mode 100755 index 000000000..7713afdc0 --- /dev/null +++ b/tools/ctconv/Makefile @@ -0,0 +1,33 @@ +######################################################################## +# This is a GNU makefile - tested on CYGWIN and Linux +# +# You may need to compile or install libpng and/or zlib before +# compiling this. +# + +TARGET = ctconv +# These flags are needed to get it to compile with libpngX.dll on CYGWIN +# CYGWINFLAGS = -DPNG_USE_DLL +CYGWINFLAGS = + +CC = gcc +CFLAGS = -W -Wall -O0 # -g +LIBS = -L/usr/local/lib -lm +LDFLAGS= + +BINS = $(TARGET) $(TARGET).exe +SRCS = ctconv.c +OBJS = ctconv.o + +.SUFFIXES: .c .o + +.c.o: + $(CC) -c $(CFLAGS) -o $@ $*.c $(CYGWINFLAGS) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) -o $(TARGET) $(OBJS) $(LIBS) $(LDFLAGS) + +clean: + rm -f $(BINS) *.o diff --git a/tools/ctconv/config.h b/tools/ctconv/config.h new file mode 100755 index 000000000..0a70fe8f0 --- /dev/null +++ b/tools/ctconv/config.h @@ -0,0 +1,15 @@ +/* Dummy compile-time configuration options + * Only used when compiling in MS VC++ using UQM's getopt.c + */ + +#ifndef _CONFIG_H +#define _CONFIG_H + +/* Define if words are stored with the most significant byte first */ +#undef WORDS_BIGENDIAN + +/* Defined if your system has getopt.h */ +#undef HAVE_GETOPT_H + +#endif /* _CONFIG_H */ + diff --git a/tools/ctconv/ctconv.c b/tools/ctconv/ctconv.c new file mode 100755 index 000000000..cbf1dac88 --- /dev/null +++ b/tools/ctconv/ctconv.c @@ -0,0 +1,421 @@ +/** + * 3DO SC2 colortab files converter (.ct) + * By Alex Volkov (codepro@usa.net), 20050202 + * + * GPL applies + * +**/ + +#include +#include +#include +#include +#include +#ifdef WIN32 +# include +#else +# include +#endif +#include "ctres.h" + +int opt_verbose = 0; +char* opt_inname = 0; +char* opt_outname = 0; +char* opt_actfile = 0; + +typedef struct +{ + uint32_t size; + uint32_t ccluts; + clut_tab_t* data; +} clut_entry_t; + +typedef struct +{ + uint32_t size; + pal_tab_t* data; +} pal_entry_t; + +int ctabs = 0; +clut_entry_t* clut_entries = 0; +pal_entry_t* pal_entries = 0; + +// internals +void verbose(const char* fmt, ...); + +void verbose(const char* fmt, ...) +{ + va_list args; + + if (!opt_verbose) + return; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +uint8_t fread_8(FILE *f) +{ + return (uint8_t) fgetc(f); +} + +uint16_t fread_16_BE(FILE *f) +{ + return (fread_8(f) << 8) | fread_8(f); +} + +uint32_t fread_32_BE(FILE *f) +{ + return (fread_16_BE(f) << 16) | fread_16_BE(f); +} + +void fwrite_16_BE(FILE *f, uint16_t val) +{ + fputc((val >> 8) & 0xff, f); + fputc(val & 0xff, f); +} + +void fwrite_32_BE(FILE *f, uint32_t val) +{ + fwrite_16_BE(f, (uint16_t) ((val >> 16) & 0xffff)); + fwrite_16_BE(f, (uint16_t) (val & 0xffff)); +} + +int read_head(FILE* f) +{ + uint32_t magic; + int v; + int i; + + magic = fread_32_BE(f); + if (magic != CTTAB_MAGIC) + { + verbose("Invalid magic found: 0x%08x\n", magic); + return -10; + } + + v = fread_16_BE(f); + if (v != 0) + { + verbose("Do not know what to do with value 0x%04x in the header\n", v); + return -11; + } + + ctabs = fread_16_BE(f); + verbose("CLUT tables: %u\n", ctabs); + + v = fread_32_BE(f); + if (v != 0) + { + verbose("Do not know what to do with offset 0x%08x in the header\n", v); + return -11; + } + + clut_entries = malloc(sizeof(clut_entries[0]) * ctabs); + if (!clut_entries) + { + verbose("Not enough memory to store entries\n"); + return -12; + } + memset(clut_entries, 0, sizeof(clut_entries[0]) * ctabs); + + for (i = 0; i < ctabs; ++i) + { + clut_entries[i].size = fread_32_BE(f); + } + + return 0; +} + +int write_head(FILE* f) +{ + int i; + + fwrite_32_BE(f, CTTAB_MAGIC); // magic + fwrite_16_BE(f, 0); // unknown + fwrite_16_BE(f, (uint16_t)ctabs); // number of tabs + fwrite_32_BE(f, 0); // offset + + for (i = 0; i < ctabs; ++i) + { + fwrite_32_BE(f, pal_entries[i].size); // offset + } + + return 0; +} + +int read_cluts(FILE* f) +{ + int i; + + verbose("Reading %d entries...\n", ctabs); + + for (i = 0; i < ctabs; ++i) + { + uint32_t size = clut_entries[i].size; + clut_tab_t* p; + uint32_t j; + + if (!size) + continue; + + p = malloc(size); + if (!p) + { + verbose("Not enough memory to store entry %d, needed %u bytes\n", i, clut_entries[i].size); + return -20; + } + + p->start_clut = fread_8(f); + if (size > 1) + p->end_clut = fread_8(f); + else + p->end_clut = p->start_clut; + + clut_entries[i].ccluts = (size - 2) / sizeof(clut_t); + if (size > 1 && p->end_clut - p->start_clut + 1 != (int)clut_entries[i].ccluts) + { + verbose("Unsupported colortab format or already in new format, entry %d\n", i); + return -20; + } + + for (j = 0; j < clut_entries[i].ccluts; ++j) + { + int k; + for (k = 0; k < CLUT_COLORS; ++k) + p->cluts[j][k] = fread_16_BE(f); + } + + clut_entries[i].data = p; + } + + return 0; +} + +int write_pals(FILE *f) +{ + int i; + + verbose("Writing %d entries...\n", ctabs); + + for (i = 0; i < ctabs; ++i) + { + if (!pal_entries[i].size) + continue; + + if (1 != fwrite(pal_entries[i].data, pal_entries[i].size, 1, f)) + { + verbose("Could not write pal entry %d\n", i); + return -30; + } + } + + return 0; +} + +int convert_clut(pal_t dst, clut_t src) +{ + int i, j, k; + clut_color_t cval; + pal_channel_t r, g, b, rtot, gtot, btot; + + for (i = 0; i < CLUT_COLORS; ++i) + { + cval = src[i]; + + rtot = r = (pal_channel_t)((cval >> 10) & 0x1f); // bits 10-14 + gtot = g = (pal_channel_t)((cval >> 5) & 0x1f); // bits 5-9 + btot = b = (pal_channel_t)((cval ) & 0x1f); // bits 0-4 + + for (j = 0; j < CLUT_STEPS; ++j) + { + k = ((j << 5) + i); + dst[k].chan.r = rtot; + rtot += r; + dst[k].chan.g = gtot; + gtot += g; + dst[k].chan.b = btot; + btot += b; + } + } + return 0; +} + +int convert_cluts(pal_t* dst, clut_t* src, int cnt) +{ + int i; + + for (i = 0; i < cnt; ++i) + convert_clut(dst[i], src[i]); + + return 0; +} + +int convert_clut_tabs() +{ + int i; + + pal_entries = malloc(sizeof(pal_entries[0]) * ctabs); + if (!pal_entries) + { + verbose("Not enough memory to store entries\n"); + return -40; + } + memset(pal_entries, 0, sizeof(pal_entries[0]) * ctabs); + + for (i = 0; i < ctabs; ++i) + { + clut_tab_t* pc; + pal_tab_t* pp; + uint32_t size; + uint32_t c; + + pc = clut_entries[i].data; + size = clut_entries[i].size; + c = clut_entries[i].ccluts; + if (!size || !pc) + continue; + + if (c > 0) + { // convert colors + pal_entries[i].size = 2 + sizeof(pal_t) * c; + pp = malloc(pal_entries[i].size); + if (!pp) + { + verbose("Not enough memory to store entry %d\n", i); + return -41; + } + convert_cluts(pp->pals, pc->cluts, c); + } + else + { // just space for start and end indices + pp = malloc(size); + if (!pp) + { + verbose("Not enough memory to store entry %d\n", i); + return -41; + } + } + pp->start_clut = pc->start_clut; + if (size > 1) + pp->end_clut = pc->end_clut; + + pal_entries[i].data = pp; + } + + return 0; +} + +void free_data() +{ + int i; + + for (i = 0; i < ctabs; ++i) + { + if (clut_entries && clut_entries[i].data) + free(clut_entries[i].data); + + if (pal_entries && pal_entries[i].data) + free(pal_entries[i].data); + } + + if (clut_entries) + free(clut_entries); + + if (pal_entries) + free(pal_entries); +} + +void usage() +{ + fprintf(stderr, "usage: ctconv [-v] [-o out-file] \n"); + fprintf(stderr, "\tconverts 3DO CLUT strtab file to RRGGBB palette strtab\n"); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -v\t\t : be verbose, explains things no human should know\n"); + fprintf(stderr, " -o out-file\t : specifies output file [default: same as input]\n"); +} + +void parse_arguments(int argc, char *argv[]) +{ + char ch; + + while ((ch = getopt(argc, argv, "?hvo:")) != -1) + { + switch(ch) + { + case 'o': + opt_outname = optarg; + break; + case 'v': + opt_verbose = 1; + break; + case '?': + case 'h': + default: + usage(); + exit(EXIT_FAILURE); + } + } + + argc -= optind; + argv += optind; + if (argc != 1) + { + usage(); + exit(EXIT_FAILURE); + } + + opt_inname = argv[0]; +} + +int main(int argc, char* argv[]) +{ + FILE *f; + int ret = 0; + + parse_arguments(argc, argv); + + if (!opt_outname) + opt_outname = opt_inname; + verbose("Output name: %s\n", opt_outname); + + f = fopen(opt_inname, "rb"); + if (!f) + { + fprintf(stderr, "Cannot open %s\n", opt_inname); + return -1; + } + + verbose("Reading file %s\n", opt_inname); + + ret = read_head(f); + if (!ret) + ret = read_cluts(f); + fclose(f); + + if (!ret) + ret = convert_clut_tabs(); + + if (!ret) + { + f = fopen(opt_outname, "wb"); + if (!f) + { + fprintf(stderr, "Cannot open %s\n", opt_outname); + return -2; + } + + verbose("Writing file %s\n", opt_outname); + } + + if (!ret) + ret = write_head(f); + if (!ret) + ret = write_pals(f); + + fclose(f); + free_data(); + + return ret; +} diff --git a/tools/ctconv/ctconv.dsp b/tools/ctconv/ctconv.dsp new file mode 100755 index 000000000..fa79ba62c --- /dev/null +++ b/tools/ctconv/ctconv.dsp @@ -0,0 +1,110 @@ +# Microsoft Developer Studio Project File - Name="ctconv" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=ctconv - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ctconv.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ctconv.mak" CFG="ctconv - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ctconv - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "ctconv - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=xicl6.exe +RSC=rc.exe + +!IF "$(CFG)" == "ctconv - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\src\getopt" /I "." /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "PNG_USE_DLL" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib advapi32.lib shell32.lib libpng10.lib libz.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "ctconv - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MD /W3 /Gm /GX /ZI /Od /I "..\..\src\getopt" /I "." /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "PNG_USE_DLL" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib advapi32.lib shell32.lib libpng10.lib libz.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "ctconv - Win32 Release" +# Name "ctconv - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\config.h +# End Source File +# Begin Source File + +SOURCE=.\ctconv.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\getopt\getopt.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\getopt\getopt.h +# End Source File +# Begin Source File + +SOURCE=.\strres.h +# End Source File +# End Group +# End Target +# End Project diff --git a/tools/ctconv/ctconv.dsw b/tools/ctconv/ctconv.dsw new file mode 100755 index 000000000..b9cef4d22 --- /dev/null +++ b/tools/ctconv/ctconv.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "ctconv"=.\ctconv.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/tools/ctconv/ctres.h b/tools/ctconv/ctres.h new file mode 100755 index 000000000..aeaf015e2 --- /dev/null +++ b/tools/ctconv/ctres.h @@ -0,0 +1,62 @@ +#include + +#if defined(_MSC_VER) +# define PACKED +# pragma pack(push, 1) +#elif defined(__GNUC__) +# define PACKED __attribute__ ((packed)) +#endif + +/* string res structures from unstr.h */ + +#define CTTAB_MAGIC 0xffffffff + +typedef struct +{ + uint32_t PACKED magic; + uint16_t PACKED unknown1; // always 0 + uint16_t PACKED num_entries; + uint32_t PACKED offset; // should always be 0 + uint32_t PACKED entry_size[1]; // 1 or more entry sizes +} cttab_header_t; + +#define CLUT_COLORS 0x20 +#define CLUT_STEPS 8 + +typedef uint16_t clut_color_t; +typedef clut_color_t clut_t[CLUT_COLORS]; + +typedef struct +{ + uint8_t PACKED start_clut; + uint8_t PACKED end_clut; // inclusive + clut_t PACKED cluts[1]; // 0 or more cluts +} clut_tab_t; + +#define PAL_COLORS 0x100 +#define PAL_CHANNELS 3 + +typedef uint8_t pal_channel_t; + +typedef union +{ + struct { + pal_channel_t r, g, b; + } chan; + pal_channel_t channels[PAL_CHANNELS]; +} pal_color_t; + +typedef pal_color_t pal_t[PAL_COLORS]; + +typedef struct +{ + uint8_t PACKED start_clut; + uint8_t PACKED end_clut; // inclusive + pal_t PACKED pals[1]; // 0 or more pals +} pal_tab_t; + + +#if defined(_MSC_VER) +# pragma pack(pop) +#endif +#undef PACKED