REMAP.CPP 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include <stdio.h>
  4. #include <stdarg.h>
  5. #include <dos.h>
  6. #include <io.h>
  7. #include <fcntl.h>
  8. #include <conio.h>
  9. #include <helix.h>
  10. #include "error.h"
  11. #include "getopt.h"
  12. #include "palette.h"
  13. #include "inifile.h"
  14. #include "pcx.h"
  15. #include "lbm.h"
  16. #include "misc.h"
  17. #include "gfx.h"
  18. #include "debug4g.h"
  19. #include "textio.h"
  20. #define kMaxTiles 4096
  21. short tilesizx[kMaxTiles];
  22. short tilesizy[kMaxTiles];
  23. long picanm[kMaxTiles];
  24. struct FNODE
  25. {
  26. FNODE *next;
  27. char name[1];
  28. };
  29. FNODE head = { &head, "" };
  30. FNODE *tail = &head;
  31. enum FILE_TYPE
  32. {
  33. FT_NONE,
  34. FT_PAL,
  35. FT_PCX,
  36. FT_LBM,
  37. FT_ART,
  38. };
  39. static struct
  40. {
  41. char *ext;
  42. FILE_TYPE fileType;
  43. } ftTable[] =
  44. {
  45. { ".ART", FT_ART },
  46. { ".LBM", FT_LBM },
  47. { ".PAL", FT_PAL },
  48. { ".PCX", FT_PCX },
  49. { ".PCC", FT_PCX },
  50. };
  51. int nWeightR, nWeightG, nWeightB;
  52. BOOL ExcludeColor[256];
  53. BYTE RemapColor[256];
  54. int URemapColor[256];
  55. PALETTE srcPal, destPal, thisPal;
  56. IniFile paltoolINI("PALTOOL.INI");
  57. char palName[_MAX_PATH];
  58. char bakname[_MAX_PATH], tempName[_MAX_PATH];
  59. BOOL showImages = FALSE;
  60. /*******************************************************************************
  61. FUNCTION: ShowBanner()
  62. DESCRIPTION: Show application banner
  63. *******************************************************************************/
  64. void ShowBanner( void )
  65. {
  66. tioPrint("ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ");
  67. tioPrint(" Image Remap Tool Version 2.0 Copyright (c) 1995 Q Studios Corporation");
  68. tioPrint("ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ");
  69. }
  70. /*******************************************************************************
  71. FUNCTION: ShowUsage()
  72. DESCRIPTION: Display command-line parameter usage, then exit
  73. *******************************************************************************/
  74. void ShowUsage(void)
  75. {
  76. tioPrint("Syntax: REMAP [options] files");
  77. tioPrint(" /Fxxx Load palette from file [.PAL]");
  78. tioPrint(" /Mx,y Map color x to color y");
  79. tioPrint(" /Oa[-b] Color(s) in dest palette is off limits");
  80. tioPrint("");
  81. tioPrint("Supported file types are: PCX, LBM, ART");
  82. tioPrint("");
  83. exit(0);
  84. }
  85. /*******************************************************************************
  86. FUNCTION: QuitMessage()
  87. DESCRIPTION: Display a printf() style message, the exit with code 1
  88. *******************************************************************************/
  89. void QuitMessage(char * fmt, ...)
  90. {
  91. char msg[80];
  92. va_list argptr;
  93. va_start( argptr, fmt );
  94. vsprintf( msg, fmt, argptr );
  95. va_end(argptr);
  96. tioPrint(msg);
  97. exit(1);
  98. }
  99. /*******************************************************************************
  100. FUNCTION: FindClosestColor()
  101. DESCRIPTION: Finds the closest color in RGB using the weights in
  102. nWeightR, nWeightG, and nWeightB.
  103. RETURNS: Index of the closest color
  104. NOTES: Colors marked with a TRUE in ExcludeColor[n] are not
  105. considered.
  106. *******************************************************************************/
  107. BYTE FindClosestColor( int r, int g, int b )
  108. {
  109. int i;
  110. int dr, dg, db, dist, matchDist, match;
  111. matchDist = 0x7FFFFFFF;
  112. for ( i = 0; i < 256; i++ )
  113. {
  114. if ( ExcludeColor[i] )
  115. continue;
  116. dist = 0;
  117. dg = (int)destPal[i].g - g;
  118. dist += nWeightG * dg * dg;
  119. if ( dist >= matchDist )
  120. continue;
  121. dr = (int)destPal[i].r - r;
  122. dist += nWeightR * dr * dr;
  123. if ( dist >= matchDist )
  124. continue;
  125. db = (int)destPal[i].b - b;
  126. dist += nWeightB * db * db;
  127. if ( dist >= matchDist )
  128. continue;
  129. matchDist = dist;
  130. match = i;
  131. if (dist == 0)
  132. break;
  133. }
  134. return (BYTE)match;
  135. }
  136. /*******************************************************************************
  137. FUNCTION: BuildRemapTable()
  138. DESCRIPTION: Create the lookup table to remap from srcPal to destPal
  139. *******************************************************************************/
  140. void BuildRemapTable(void)
  141. {
  142. for (int i = 0; i < 256; i++)
  143. {
  144. if ( URemapColor[i] < 0 )
  145. RemapColor[i] = FindClosestColor(srcPal[i].r, srcPal[i].g, srcPal[i].b);
  146. else
  147. RemapColor[i] = (BYTE)URemapColor[i];
  148. }
  149. }
  150. void RemapBuffer( BYTE *buffer, int length, BYTE *remapTable );
  151. #pragma aux RemapBuffer =\
  152. " xor eax,eax",\
  153. "loop1:",\
  154. " mov al,[edx]",\
  155. " mov al,[ebx+eax]",\
  156. " mov [edx],al",\
  157. " inc edx",\
  158. " dec ecx",\
  159. " jnz loop1",\
  160. parm [edx][ecx][ebx]\
  161. modify [eax edx]\
  162. FILE_TYPE GetFileType( char *filename )
  163. {
  164. char ext[_MAX_EXT];
  165. _splitpath(filename, NULL, NULL, NULL, ext);
  166. for ( int i = 0; i < LENGTH(ftTable); i++ )
  167. {
  168. if ( stricmp(ext, ftTable[i].ext) == 0 )
  169. return ftTable[i].fileType;
  170. }
  171. return FT_NONE;
  172. }
  173. void GetNewPalette(void)
  174. {
  175. int width = 0, height = 0; // not really used, but necessary for ReadXXX call
  176. switch( GetFileType(palName) )
  177. {
  178. case FT_PAL:
  179. if ( !FileLoad(palName, destPal, sizeof(destPal)) )
  180. QuitMessage("Couldn't open palette file '%s'", palName);
  181. break;
  182. case FT_PCX:
  183. {
  184. int r = ReadPCX(palName, destPal, &width, &height, NULL);
  185. if ( r != PCX_OKAY )
  186. QuitMessage("Error reading palette from %s: code = %d", palName, r);
  187. break;
  188. }
  189. case FT_LBM:
  190. {
  191. int r = ReadLBM(palName, destPal, &width, &height, NULL);
  192. if ( r != LBM_OKAY )
  193. QuitMessage("Error reading palette from %s: code = %d", palName, r);
  194. break;
  195. }
  196. }
  197. }
  198. void DisplayImage( BYTE *bits, int width, int height )
  199. {
  200. int size = width * height;
  201. QBITMAP *qbmSource = (QBITMAP *)malloc(sizeof(QBITMAP) + size);
  202. dassert(qbmSource != NULL);
  203. memcpy(qbmSource->data, bits, size);
  204. qbmSource->bitModel = BM_RAW;
  205. qbmSource->tcolor = 0;
  206. qbmSource->cols = (short)width;
  207. qbmSource->rows = (short)height;
  208. qbmSource->stride = (short)width;
  209. gfxDrawBitmap(qbmSource, 160 - width / 2, 100 - height / 2);
  210. free(qbmSource);
  211. }
  212. void RemapART( char *filename )
  213. {
  214. int hInFile, hOutFile;
  215. int tileStart, tileEnd;
  216. long artversion, numtiles;
  217. char artPalName[_MAX_PATH];
  218. strcpy(artPalName, paltoolINI.GetKeyString(NULL, "GamePal", ""));
  219. if ( !FileLoad(artPalName, thisPal, sizeof(thisPal)) )
  220. QuitMessage("Couldn't open palette file '%s'", artPalName);
  221. if ( memcmp(srcPal, thisPal, sizeof(PALETTE)) != 0 )
  222. {
  223. memcpy(srcPal, thisPal, sizeof(PALETTE));
  224. BuildRemapTable();
  225. }
  226. hInFile = open(filename, O_BINARY | O_RDWR);
  227. if (hInFile == -1)
  228. QuitMessage("Error opening %s", filename);
  229. tmpnam(tempName);
  230. hOutFile = open(tempName, O_CREAT | O_WRONLY | O_BINARY | O_TRUNC, S_IWUSR);
  231. if ( hOutFile == -1 )
  232. QuitMessage("Error creating temporary file");
  233. read(hInFile, &artversion, sizeof(artversion));
  234. read(hInFile, &numtiles, sizeof(numtiles));
  235. read(hInFile, &tileStart, sizeof(tileStart));
  236. read(hInFile, &tileEnd, sizeof(tileEnd));
  237. int nTiles = tileEnd - tileStart + 1;
  238. read(hInFile, &tilesizx[tileStart], nTiles * sizeof(tilesizx[0]));
  239. read(hInFile, &tilesizy[tileStart], nTiles * sizeof(tilesizy[0]));
  240. read(hInFile, &picanm[tileStart], nTiles * sizeof(picanm[0]));
  241. write(hOutFile, &artversion, sizeof(artversion));
  242. write(hOutFile, &numtiles, sizeof(numtiles));
  243. write(hOutFile, &tileStart, sizeof(tileStart));
  244. write(hOutFile, &tileEnd, sizeof(tileEnd));
  245. write(hOutFile, &tilesizx[tileStart], nTiles * sizeof(tilesizx[0]));
  246. write(hOutFile, &tilesizy[tileStart], nTiles * sizeof(tilesizy[0]));
  247. write(hOutFile, &picanm[tileStart], nTiles * sizeof(picanm[0]));
  248. for (int i = tileStart; tioGauge(i - tileStart, nTiles); i++)
  249. {
  250. int nSize = tilesizx[i] * tilesizy[i];
  251. if (nSize == 0)
  252. continue;
  253. BYTE *pBits = (BYTE *)malloc(nSize);
  254. dassert(pBits != NULL);
  255. read(hInFile, pBits, nSize);
  256. RemapBuffer(pBits, nSize, RemapColor);
  257. write(hOutFile, pBits, nSize);
  258. if ( showImages )
  259. DisplayImage(pBits, tilesizy[i], tilesizx[i]);
  260. free(pBits);
  261. }
  262. close(hInFile);
  263. close(hOutFile);
  264. // backup the existing sequence
  265. strcpy(bakname, filename);
  266. ChangeExtension(bakname, ".BAK");
  267. unlink(bakname);
  268. rename(filename, bakname);
  269. rename(tempName, filename);
  270. }
  271. void RemapPCX( char *filename )
  272. {
  273. int srcWidth = 0, srcHeight = 0;
  274. int nResult;
  275. nResult = ReadPCX(filename, NULL, &srcWidth, &srcHeight, NULL);
  276. if (nResult != 0)
  277. QuitMessage("Problem reading PCX file %s: error=%d", filename, nResult);
  278. BYTE *pBits = NULL;
  279. nResult = ReadPCX(filename, thisPal, &srcWidth, &srcHeight, &pBits);
  280. if (nResult != 0)
  281. QuitMessage("Problem reading PCX file %s: error=%d", filename, nResult);
  282. if ( memcmp(srcPal, thisPal, sizeof(PALETTE)) != 0 )
  283. {
  284. memcpy(srcPal, thisPal, sizeof(PALETTE));
  285. BuildRemapTable();
  286. }
  287. RemapBuffer(pBits, srcWidth * srcHeight, RemapColor);
  288. if ( showImages )
  289. DisplayImage(pBits, srcWidth, srcHeight);
  290. tmpnam(tempName);
  291. nResult = WritePCX(tempName, destPal, srcWidth, srcHeight, pBits);
  292. if (nResult != 0)
  293. QuitMessage("Problem writing temporary PCX: error=%d", nResult);
  294. // backup the existing sequence
  295. strcpy(bakname, filename);
  296. ChangeExtension(bakname, ".BAK");
  297. unlink(bakname);
  298. rename(filename, bakname);
  299. rename(tempName, filename);
  300. free(pBits);
  301. }
  302. void RemapLBM( char *filename )
  303. {
  304. int srcWidth = 0, srcHeight = 0;
  305. int nResult;
  306. nResult = ReadLBM(filename, NULL, &srcWidth, &srcHeight, NULL);
  307. if (nResult != 0)
  308. QuitMessage("Problem reading LBM file %s: error=%d", filename, nResult);
  309. BYTE *pBits = NULL;
  310. nResult = ReadLBM(filename, thisPal, &srcWidth, &srcHeight, &pBits);
  311. if (nResult != 0)
  312. QuitMessage("Problem reading LBM file %s: error=%d", filename, nResult);
  313. if ( memcmp(srcPal, thisPal, sizeof(PALETTE)) != 0 )
  314. {
  315. memcpy(srcPal, thisPal, sizeof(PALETTE));
  316. BuildRemapTable();
  317. }
  318. RemapBuffer(pBits, srcWidth * srcHeight, RemapColor);
  319. if ( showImages )
  320. DisplayImage(pBits, srcWidth, srcHeight);
  321. free(pBits);
  322. }
  323. void ProcessFile( char *filename )
  324. {
  325. tioPrint("Processing %s", filename);
  326. FILE_TYPE fileType = GetFileType(filename);
  327. if ( fileType == FT_NONE )
  328. QuitMessage("Don't know how to handle file %s", filename);
  329. // determine the size of the source image
  330. switch ( fileType )
  331. {
  332. case FT_ART:
  333. RemapART(filename);
  334. break;
  335. case FT_LBM:
  336. RemapLBM(filename);
  337. break;
  338. case FT_PCX:
  339. RemapPCX(filename);
  340. break;
  341. }
  342. }
  343. void Initialize(void)
  344. {
  345. for (int i = 0; i < 256; i++)
  346. {
  347. ExcludeColor[i] = FALSE;
  348. URemapColor[i] = -1;
  349. }
  350. }
  351. void InsertFilename( char *filename )
  352. {
  353. FNODE *n = (FNODE *)malloc(sizeof(FNODE) + strlen(filename));
  354. strcpy(n->name, filename);
  355. // insert the node at the tail, so it stays in order
  356. n->next = tail->next;
  357. tail->next = n;
  358. tail = n;
  359. }
  360. void ProcessArgument(char *s)
  361. {
  362. char filespec[_MAX_PATH];
  363. char buffer[_MAX_PATH2];
  364. char path[_MAX_PATH];
  365. strcpy(filespec, s);
  366. char *drive, *dir;
  367. // separate the path from the filespec
  368. _splitpath2(s, buffer, &drive, &dir, NULL, NULL);
  369. _makepath(path, drive, dir, NULL, NULL);
  370. struct find_t fileinfo;
  371. unsigned r = _dos_findfirst(s, _A_NORMAL, &fileinfo);
  372. if (r != 0)
  373. tioPrint("%s not found", s);
  374. while ( r == 0 )
  375. {
  376. strcpy(filespec, path);
  377. strcat(filespec, fileinfo.name);
  378. InsertFilename(filespec);
  379. r = _dos_findnext( &fileinfo );
  380. }
  381. _dos_findclose(&fileinfo);
  382. }
  383. /***********************************************************************
  384. * Process command line arguments
  385. **********************************************************************/
  386. void ParseOptions( void )
  387. {
  388. enum {
  389. kSwitchHelp,
  390. kSwitchFile,
  391. kSwitchMap,
  392. kSwitchOmit,
  393. };
  394. static SWITCH switches[] = {
  395. { "?", kSwitchHelp, FALSE },
  396. { "F", kSwitchFile, TRUE },
  397. { "M", kSwitchMap, TRUE },
  398. { "O", kSwitchOmit, TRUE },
  399. { NULL, 0, FALSE },
  400. };
  401. char buffer[256];
  402. int r;
  403. while ( (r = GetOptions(switches)) != GO_EOF )
  404. {
  405. switch (r)
  406. {
  407. case GO_INVALID:
  408. sprintf(buffer, "Invalid argument: %s", OptArgument);
  409. ThrowError(buffer, ES_ERROR);
  410. break;
  411. case GO_FULL:
  412. ProcessArgument(OptArgument);
  413. break;
  414. case kSwitchHelp:
  415. ShowUsage();
  416. break;
  417. case kSwitchFile:
  418. strcpy(palName, OptArgument);
  419. AddExtension(palName, ".PAL");
  420. break;
  421. case kSwitchMap:
  422. {
  423. int nFrom, nTo;
  424. if ( sscanf(OptArgument, "%i,%i", &nFrom, &nTo) != 2 )
  425. QuitMessage("Syntax error: %s", OptArgument);
  426. URemapColor[nFrom] = nTo;
  427. break;
  428. }
  429. case kSwitchOmit:
  430. {
  431. int nFrom, nTo;
  432. switch ( sscanf(OptArgument, "%i-%i", &nFrom, &nTo) )
  433. {
  434. case 1:
  435. ExcludeColor[nFrom] = TRUE;
  436. break;
  437. case 2:
  438. while (nFrom <= nTo)
  439. ExcludeColor[nFrom++] = TRUE;
  440. break;
  441. default:
  442. QuitMessage("Syntax error: %s", OptArgument);
  443. }
  444. break;
  445. }
  446. }
  447. }
  448. }
  449. void main( int argc /*, char *argv[]*/)
  450. {
  451. tioInit(0);
  452. ShowBanner();
  453. if (argc < 2)
  454. ShowUsage();
  455. nWeightR = paltoolINI.GetKeyInt(NULL, "WeightR", 1);
  456. nWeightG = paltoolINI.GetKeyInt(NULL, "WeightG", 1);
  457. nWeightB = paltoolINI.GetKeyInt(NULL, "WeightB", 1);
  458. Initialize();
  459. ParseOptions();
  460. if ( head.next == &head )
  461. QuitMessage("No source file specified");
  462. if ( palName[0] == '\0' )
  463. QuitMessage("You must specify a new palette.");
  464. GetNewPalette();
  465. int oldMode = gGetMode();
  466. if (!gFindMode(320, 200, 256, CHAIN256))
  467. QuitMessage("Helix driver not found");
  468. if ( showImages )
  469. {
  470. Video.SetMode();
  471. gfxSetClip(0, 0, 320, 200);
  472. gSetDACRange(0, 256, destPal);
  473. }
  474. // process the file list
  475. for (FNODE *n = head.next; n != &head; n = n->next)
  476. ProcessFile(n->name);
  477. if ( showImages )
  478. gSetMode(oldMode);
  479. }