/****************************\
**                          **
*  BMPtoHIP Converter V.1.5  *
* -------------------------- *
*                            *
*  (C)1996 by HARD Software  *
*                            *
*  Written by S.Teli&T.Bene  *
**                          **
\****************************/


#include <stdio.h>
#include <stdlib.h>
#include <alloc.h>

#define ERROR08 max(abs(c+t16[x]-line[x*2+1]), abs(c+t16[x+1]-line[x*2+2]))
#define ERROR16 max(abs(c+(t08[x-1]-1)*2-line[x*2]), abs(c+(t08[x]-1)*2-line[x*2+1]))

typedef unsigned char byte;
typedef unsigned int word;


static byte p08[30] = {
	1, 1, 1, 2, 2, 2, 2, 3, 3, 3,
	3, 4, 4, 4, 4, 5, 5, 5, 5, 6,
	6, 6, 6, 7, 7, 7, 7, 8, 8, 8};

static byte p16[30] = {
	0, 1, 2, 1, 2, 3, 4, 3, 4, 5,
	6, 5, 6, 7, 8, 7, 8, 9, 10,9,
	10,11,12,11,12,13,14,13,14,15};

static byte hdr08[6] = { 0xff, 0xff, 0x10, 0x60, 0x4f, 0x7f };
static byte hdr16[6] = { 0xff, 0xff, 0x10, 0x80, 0x4f, 0x9f };

byte line[320], tt08[160], tt16[160], t08[80], t16[80];
byte *out08, *out16, *out08p, *out16p;
word header[27];


void crash(byte err)
{
	switch(err)
	{
		case 1: puts("Usage: BMP2HIP [-onwxy] bmpfile [hipfile]");
				puts("       -o: optimization on");
				puts("       -n: read 160 pixels wide BMP (default)");
				puts("       -w: read 320 pixels wide BMP");
				puts("       -x<n>: set addr. of 1st segment to n (0-F)");
				puts("       -y<n>: set addr. of 2nd segment to n (0-F)");
				break;
		case 2: puts("Error opening BMP."); break;
		case 3: puts("Error creating HIP."); break;
		case 4: puts("Out of memory."); break;
		case 5: puts("Load error."); break;
		case 6: puts("Source is not a BMP file."); break;
		case 7: puts("Palette size must be = 256.");
	}
	exit(0);
}


char *GenExt(char *fname, char *ext)
{
	static char buff[80];
	char c;
	byte i;

	for(i = 0; i < 75; i++)
	{
		c = buff[i] = fname[i];
		if(c == '.' || c == '\0') break;
	}
	if(i == 75) return NULL;

	buff[i++] = '.';
	buff[i++] = ext[0];
	buff[i++] = ext[1];
	buff[i++] = ext[2];
	buff[i] = '\0';

	return buff;
}


int HexDigit(char c)
{
	if(c >= '0' && c <= '9')
		return c - '0';
	else if(c >= 'a' && c <= 'f')
		return c - 'a' + 10;
	else if(c >= 'A' && c <= 'F')
		return c - 'A' + 10;
	else
		return -1;
}


void main(int argc, char *argv[])
{
	word cols, rows, shades, change;
	word width=160;
	int i, j;
	byte y, x, z, e0, e1, e2, c;
	byte optimize=0, src_found=0, dest_found=0;
	FILE *is, *os;

	puts("\nBMPtoHIP Converter V1.5 --- (c)1996 Hard Software\n");

	if(argc < 2)
		crash(1);

	for(i = 1; i < argc; i++)
	{
		if(argv[i][0] == '-')
		{
			switch(argv[i][1])
			{
				case 'o': case 'O':
					optimize = 1; break;
				case 'n': case 'N':
					width = 160; break;
				case 'w': case 'W':
					width = 320; break;
				case 'x': case 'X':
					j = HexDigit(argv[i][2]);
					if(j >= 0)
					{
						hdr08[3] = j*16;
						hdr08[5] = (j + 1)*16 + 15;
					}
					break;
				case 'y': case 'Y':
					j = HexDigit(argv[i][2]);
					if(j >= 0)
					{
						hdr16[3] = j*16;
						hdr16[5] = (j + 1)*16 + 15;
					}
					break;
			}
		}
		else if(!src_found)
		{
			src_found = i;
			if((is = fopen(GenExt(argv[i], "BMP"), "rb")) == NULL)
				crash(2);
		}
		else
		{
			dest_found = i;
			if((os = fopen(GenExt(argv[i], "HIP"), "wb")) == NULL)
				crash(3);
		}
	}

	if(!src_found)
		crash(1);
	if(!dest_found)
		if((os = fopen(GenExt(argv[src_found], "HIP"), "wb")) == NULL)
			crash(3);

	if((out08p = out08 = malloc(8000)) == NULL ||
	   (out16p = out16 = malloc(8000)) == NULL)
		crash(4);
	for(i = 0; i < 8000; i++)
		out08[i] = out16[i] = 0;

	fread(header, 54, 1, is);
	if(header[0] != 0x4d42)
		crash(6);
	if(header[14] != 8)
		crash(7);
	rows = header[11]; if(rows > 200) rows = 200;
	cols = header[9]; cols = (cols + 3)&0xfffc;
	shades = header[23]; if(shades == 0) shades = 256;
	fseek(is, 4*shades, SEEK_CUR); /* Skip RGB table */

	printf("\nConverting");

	for(y = 0; y < rows; y++)
	{
		putchar('.');
		if(fread(line, (cols > width) ?width :cols, 1, is) != 1)
			crash(5);
		if(cols < width)
			for(i = cols; i < width; i++)
				line[i] = 0;
		else if(cols > width)
			if(fseek(is, cols - width, SEEK_CUR))
				crash(5);

		if(width == 320)
			for(x = 0; x < 160; x++)
				line[x] = (line[2*x] + line[2*x+1] + 1)/2;

		for(x = 0; x < 160; x++)
		{
			z = line[x] = line[x]*30/shades;
			tt16[x] = p16[z];
			tt08[x] = p08[z];
		}

		for(x = 0; x < 80; x++)
		{
			t16[x] = (tt16[x*2] + tt16[x*2+1] + 1)/2;
			z = (x == 79) ?1 :2;
			t08[x] = (tt08[x*2+1] + tt08[x*2+z] + 1)/2;
		}

		if(optimize) do
		{
			change = 0;
			for(x = 0; x < 79; x++)
			{
				c = (t08[x] - 1)*2;
				e0 = ERROR08;
				c += 2;
				if(c < 16) e1 = ERROR08; else e1 = e0;
				c -= 4;
				if(c < 16) e2 = ERROR08; else e2 = e0;

				if(e1 < e0 || e2 < e0)
				{
					change++;
					if(e1 < e2)
						t08[x]++;
					else
						t08[x]--;
				}
			}
			for(x = 1; x < 80; x++)
			{
				c = t16[x];
				e0 = ERROR16;
				c += 1;
				if(c < 16) e1 = ERROR16; else e1 = e0;
				c -= 2;
				if(c < 16) e2 = ERROR16; else e2 = e0;

				if(e1 < e0 || e2 < e0)
				{
					change++;
					if(e1 < e2)
						t16[x]++;
					else
						t16[x]--;
				}
			}
		} while(change);

		for(x = 0; x < 40; x++)
		{
			*out08p++ = t08[x*2 + 1] + 16*t08[x*2];
			*out16p++ = t16[x*2 + 1] + 16*t16[x*2];
		}
	}
	fwrite(hdr08, 6, 1, os);
	fwrite(out08, 8000, 1, os);
	fwrite(hdr16, 6, 1, os);
	fwrite(out16, 8000, 1, os);

	fclose(is);
	fclose(os);
	puts("\nDone.");
}
