#include #include #include #include #include #include "screen.h" #include "palette.h" #include "debug4g.h" #include "textio.h" #include "inifile.h" #include "getopt.h" #include "misc.h" #define kMistLevels 32 #define kMistValue 180 RGB gamePalette[256]; int rdist[256], gdist[256], bdist[256]; int nPalLookups; int nWeightR, nWeightG, nWeightB; int nMaxDark; IniFile paltoolINI("PALTOOL.INI"); /******************************************************************************* FUNCTION: ShowBanner() DESCRIPTION: Show application banner *******************************************************************************/ void ShowBanner( void ) { tioPrint("ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ"); tioPrint(" PalTool Version 1.0 Copyright (c) 1995 Q Studios Corporation"); tioPrint("ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ"); } /******************************************************************************* FUNCTION: ShowUsage() DESCRIPTION: Display command-line parameter usage, then exit *******************************************************************************/ void ShowUsage(void) { tioPrint("Syntax: PALTOOL destfile command"); tioPrint("-c filename create color LUT (CLU) using specified palette file"); tioPrint("-f N[,O] create fog LUT (FLU) with gray level of N, opacity O"); tioPrint("-s filename create shade LUT (PLU) using specified palette file"); tioPrint("-p filename create palette (PAL) from specified lookup table (CLU/PLU)"); tioPrint("-t N create translucency LUT (TLU) with opacity of N%"); tioPrint("-? this help"); exit(0); } /******************************************************************************* FUNCTION: QuitMessage() DESCRIPTION: Display a printf() style message, the exit with code 1 *******************************************************************************/ void QuitMessage(char * fmt, ...) { char msg[80]; va_list argptr; va_start( argptr, fmt ); vsprintf( msg, fmt, argptr ); va_end(argptr); tioPrint(msg); exit(1); } /******************************************************************************* FUNCTION: FindClosestColor() DESCRIPTION: Finds the closest color in RGB using the weights in nWeightR, nWeightG, and nWeightB. RETURNS: Index of the closest color *******************************************************************************/ BYTE FindClosestColor( int r, int g, int b ) { int i; int dist, matchDist, match; matchDist = 0x7FFFFFFF; for ( i = 0; i < 256; i++ ) { dist = 0; dist += gdist[qabs((int)gamePalette[i].g - g)]; if ( dist >= matchDist ) continue; dist += rdist[qabs((int)gamePalette[i].r - r)]; if ( dist >= matchDist ) continue; dist += bdist[qabs((int)gamePalette[i].b - b)]; if ( dist >= matchDist ) continue; matchDist = dist; match = i; if (dist == 0) break; } return (BYTE)match; } void BuildCLU( char *filename, RGB *palette) { int i; char cluName[_MAX_PATH]; BYTE *table = (BYTE *)malloc(256); for ( i = 0; tioGauge(i, 256); i++ ) table[i] = FindClosestColor(palette[i].r, palette[i].g, palette[i].b); strcpy(cluName, filename); ChangeExtension(cluName, ".CLU"); FileSave(cluName, table, 256); free(table); } void BuildPLU( char *filename, RGB *palette, int grayLevel) { int i, j; int r, g, b; char pluName[_MAX_PATH]; BYTE *table = (BYTE *)malloc(256 * nPalLookups); BYTE *p = table; for ( i = 0; tioGauge(i, nPalLookups); i++ ) { for ( j = 0; j < 256; j++ ) { int n = muldiv(i, nMaxDark, nPalLookups); r = dmulscale16r(0x10000 - n, palette[j].r, n, grayLevel); g = dmulscale16r(0x10000 - n, palette[j].g, n, grayLevel); b = dmulscale16r(0x10000 - n, palette[j].b, n, grayLevel); p[j] = FindClosestColor(r, g, b); } p += 256; } strcpy(pluName, filename); ChangeExtension(pluName, ".PLU"); FileSave(pluName, table, 256 * nPalLookups); free(table); } void BuildFLU( char *filename, RGB *palette, int grayLevel, int nOpacity) { int i, j, k; int r, g, b; char fluName[_MAX_PATH]; int nDepthLevels = 16; int nBrightLevels = 16; // convert to 16:16 fixed point fraction nOpacity = divscale16(nOpacity, 100); BYTE *table = (BYTE *)malloc(nDepthLevels * nBrightLevels * 256); BYTE *p = table; for ( i = 0; tioGauge(i, nDepthLevels); i++ ) { int nDepth = muldiv(i, nOpacity, nDepthLevels); for ( j = 0; j < nBrightLevels; j++ ) { int nShade = muldiv(j, 0x0F000, nBrightLevels); for ( k = 0; k < 256; k++ ) { r = mulscale16r(0x10000 - nShade, palette[k].r); r = dmulscale16r(0x10000 - nDepth, r, nDepth, grayLevel); g = mulscale16r(0x10000 - nShade, palette[k].g); g = dmulscale16r(0x10000 - nDepth, g, nDepth, grayLevel); b = mulscale16r(0x10000 - nShade, palette[k].b); b = dmulscale16r(0x10000 - nDepth, b, nDepth, grayLevel); p[k] = FindClosestColor(r, g, b); } p += 256; } } strcpy(fluName, filename); ChangeExtension(fluName, ".FLU"); FileSave(fluName, table, nDepthLevels * nBrightLevels * 256); free(table); } /* ** lucWeight range = 0 - 256 */ void BuildTLU( char *filename, RGB *palette, int nPercent) { char tluName[_MAX_PATH]; int i, j; int r, g, b; int lucWeight = divscale16(nPercent, 100); BYTE *table = (BYTE *)malloc(65536); BYTE *p = table; for ( i = 0; tioGauge(i, 256); i++ ) { for ( j = 0; j < 256; j++ ) { r = dmulscale16r(0x10000 - lucWeight, palette[i].r, lucWeight, palette[j].r); g = dmulscale16r(0x10000 - lucWeight, palette[i].g, lucWeight, palette[j].g); b = dmulscale16r(0x10000 - lucWeight, palette[i].b, lucWeight, palette[j].b); p[j] = FindClosestColor(r, g, b); } p += 256; } strcpy(tluName, filename); ChangeExtension(tluName, ".TLU"); FileSave(tluName, table, 65536); free(table); } void LUTtoPAL( BYTE *table, RGB *palette ) { dassert(table != NULL); dassert(palette != NULL); for ( int i = 0; i < 256; i++ ) { palette[i] = gamePalette[ table[i] ]; } } /*********************************************************************** * Process command line arguments **********************************************************************/ void ParseOptions( void ) { enum { kSwitchColor, kSwitchFog, kSwitchPal, kSwitchShade, kSwitchTrans, kSwitchHelp, }; static SWITCH switches[] = { { "?", kSwitchHelp, FALSE }, { "C", kSwitchColor, TRUE }, { "F", kSwitchFog, TRUE }, { "P", kSwitchPal, TRUE }, { "S", kSwitchShade, TRUE }, { "T", kSwitchTrans, TRUE }, { NULL, 0, FALSE }, }; char destName[_MAX_PATH] = ""; int r; while ( (r = GetOptions(switches)) != GO_EOF ) { switch (r) { case GO_INVALID: QuitMessage("Invalid argument: %s", OptArgument); case GO_FULL: if (destName[0] != '\0') QuitMessage("Invalid argument: %s", OptArgument); strcpy(destName, OptArgument); break; case kSwitchColor: { char palName[_MAX_PATH]; RGB palette[256]; strcpy(palName, OptArgument); AddExtension(palName, "PAL"); if ( !FileLoad(palName, palette, sizeof(palette)) ) QuitMessage("Error loading %s", palName); tioPrint("Building color LUT from %s", palName); BuildCLU(destName, palette); break; } case kSwitchFog: { int nGrayLevel = 0, nOpacity = 100; sscanf(OptArgument, "%d,%d", &nGrayLevel, &nOpacity); tioPrint("Building fog LUT, gray level = %d, opacity = %d%%", nGrayLevel, nOpacity); BuildFLU(destName, gamePalette, nGrayLevel, nOpacity); break; } case kSwitchPal: { char zName[_MAX_PATH]; char *ext; char buffer[_MAX_PATH2]; RGB palette[256]; strcpy( zName, OptArgument ); _splitpath2(zName, buffer, NULL, NULL, NULL, &ext); if ( strcmpi(ext, ".CLU") == 0 || strcmpi(ext, ".PLU") == 0 ) { BYTE *table = (BYTE *)malloc(256); if ( !FileLoad(zName, table, 256) ) QuitMessage("Error loading %s", zName); LUTtoPAL(table, palette); ChangeExtension(zName, ".PAL"); FileSave(zName, palette, sizeof(palette)); free(table); } else QuitMessage(".PLU or .CLU extension required for parameter %s", zName); break; } case kSwitchShade: { char palName[_MAX_PATH]; RGB palette[256]; strcpy(palName, OptArgument); AddExtension(palName, "PAL"); if ( !FileLoad(palName, palette, sizeof(palette)) ) QuitMessage("Error loading %s", palName); tioPrint("Building shade LUT from %s", palName); BuildPLU(destName, palette, 0); break; } case kSwitchTrans: { int nPercent = strtol(OptArgument, NULL, 0); tioPrint("Building translucency LUT, opacity = %d%%", nPercent); BuildTLU(destName, gamePalette, nPercent); break; } case kSwitchHelp: ShowUsage(); break; } } } void main( int argc ) { char sGamePal[_MAX_PATH]; tioInit(0); ShowBanner(); nPalLookups = paltoolINI.GetKeyInt(NULL, "NumPalLookups", 32); nWeightR = paltoolINI.GetKeyInt(NULL, "WeightR", 1); nWeightG = paltoolINI.GetKeyInt(NULL, "WeightG", 1); nWeightB = paltoolINI.GetKeyInt(NULL, "WeightB", 1); strcpy(sGamePal, paltoolINI.GetKeyString(NULL, "GamePal", "")); nMaxDark = paltoolINI.GetKeyInt(NULL, "MaxDark", 0xE000); if ( !FileLoad(sGamePal, gamePalette, sizeof(gamePalette)) ) QuitMessage("Error loading %s", sGamePal); if (argc == 1) ShowUsage(); tioPrint("nPalLookups = %d, WeightR = %d, WeightG = %d, WeightB = %d", nPalLookups, nWeightR, nWeightG, nWeightB); tioPrint("Creating distance lookup table"); for (int i = 0; tioGauge(i, 256); i++) { int square = i * i; rdist[i] = nWeightR * square; gdist[i] = nWeightG * square; bdist[i] = nWeightB * square; } ParseOptions(); tioPrint(""); tioTerm(); } /* void InverseColor(int *r, int *g, int *b) { int gray = (*r * 2 + *g * 5 + *b) / 8; *r = 255 - gray; *g = 255 - gray; *b = 255 - gray; } void BeastColor(int *r, int *g, int *b) { *r = gammaTable[1][*r]; *g = gammaTable[1][*g / 2]; *b = gammaTable[1][*b / 2]; } void ScopeColor(int *r, int *g, int *b) { int i; i = ClipLow(*r - *g / 4 - *b / 2, 0); *r = gammaTable[3][i / 2]; *g = gammaTable[3][i]; *b = gammaTable[3][i / 2]; } void WaterColor(int *r, int *g, int *b) { *r = gammaTable[1][*r / 2]; *g = gammaTable[1][*g / 2]; *b = gammaTable[1][*b]; } void GrayColor(int *r, int *g, int *b) { int gray = (*r * 2 + *g * 5 + *b) / 8; *r = gray; *g = gray; *b = gray; } void GrayishColor(int *r, int *g, int *b) { int gray = (*r * 2 + *g * 5 + *b) / 8; *r = (*r + gray) / 2; *g = (*g + gray) / 2; *b = (*b + gray) / 2; } */