#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

typedef unsigned char UCHAR;
typedef unsigned short USHORT;
typedef unsigned long ULONG;

#define PK __attribute__ ((packed))

struct FFEBitmap {
	UCHAR flags PK;
	UCHAR height PK;
	USHORT width PK;
	short xoff PK;
	short yoff PK;
	UCHAR pData[0];
};

static int ExportBitmap (FFEBitmap *pBitmap, char *pFilename);
static int ReadData (char *&pSrc, UCHAR *pData);

static UCHAR pFFEPal[128*3] = {
	0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x8, 0x8, 0x8,
	0xc, 0xc, 0xc, 0x10, 0x10, 0x10, 0x14, 0x14, 0x14,
	0x18, 0x18, 0x18, 0x1c, 0x1c, 0x1c, 0x20, 0x20, 0x20,
	0x24, 0x24, 0x24, 0x28, 0x28, 0x28, 0x2c, 0x2c, 0x2c,
	0x30, 0x30, 0x30, 0x34, 0x34, 0x34, 0x38, 0x38, 0x38,
	0x3c, 0x3c, 0x3c, 0x3c, 0x0, 0x0, 0x0, 0x3c, 0x0,
	0x3c, 0x3c, 0x0, 0x4, 0x8, 0x4, 0xa, 0x2, 0x6,
	0x12, 0x4, 0x6, 0xa, 0xa, 0x8, 0x1a, 0x2, 0x2,
	0xa, 0xa, 0x10, 0x12, 0xa, 0xa, 0x22, 0x2, 0x2,
	0x4, 0x8, 0x1c, 0xc, 0x12, 0x10, 0x2a, 0x2, 0x2,
	0x1a, 0xa, 0xc, 0x22, 0xa, 0xa, 0x32, 0x2, 0x2,
	0xe, 0x1a, 0x10, 0xc, 0x20, 0xc, 0x6, 0xa, 0x2a,
	0x14, 0x12, 0x14, 0x2a, 0xa, 0x8, 0x8, 0x2c, 0x8,
	0xc, 0x26, 0xc, 0x1a, 0x12, 0x14, 0x8, 0x30, 0x8,
	0x3a, 0x4, 0x4, 0x14, 0x1a, 0x14, 0x22, 0x12, 0xe,
	0xe, 0x2c, 0xa, 0x32, 0xa, 0x8, 0x12, 0x20, 0x14,
	0x12, 0x26, 0xe, 0x18, 0x20, 0x10, 0x2a, 0x12, 0xe,
	0x18, 0x26, 0xc, 0x1a, 0x1a, 0x16, 0x22, 0x14, 0x16,
	0x22, 0x1a, 0x10, 0x3a, 0xa, 0x8, 0x1e, 0x20, 0xe,
	0x34, 0x12, 0x6, 0x18, 0x20, 0x16, 0x2a, 0x1a, 0xc,
	0x32, 0x1a, 0x4, 0x1e, 0x26, 0xe, 0x12, 0x16, 0x2a,
	0x14, 0x26, 0x18, 0x2a, 0x14, 0x14, 0x10, 0x32, 0x10,
	0x24, 0x22, 0xe, 0xc, 0x12, 0x36, 0x3c, 0x12, 0x8,
	0x32, 0x12, 0x12, 0x22, 0x1a, 0x1a, 0x18, 0x26, 0x18,
	0x1e, 0x20, 0x18, 0x16, 0x2e, 0x14, 0x2a, 0x22, 0xe,
	0x1a, 0x1a, 0x26, 0x34, 0x20, 0x8, 0x16, 0x22, 0x24,
	0x3c, 0x1a, 0x6, 0x10, 0x14, 0x38, 0x2a, 0x1a, 0x18,
	0x1e, 0x26, 0x1a, 0x24, 0x22, 0x18, 0x3a, 0x12, 0x12,
	0x14, 0x34, 0x16, 0x30, 0x20, 0x10, 0x32, 0x1a, 0x16,
	0x32, 0x2a, 0x6, 0x3c, 0x22, 0x4, 0x1e, 0x2e, 0x18,
	0x2a, 0x22, 0x18, 0x1e, 0x20, 0x26, 0x12, 0x1a, 0x3a,
	0x34, 0x32, 0x2, 0x1a, 0x36, 0x18, 0x3c, 0x2a, 0x4,
	0x2a, 0x2a, 0x16, 0x1e, 0x26, 0x26, 0x1a, 0x1a, 0x38,
	0x3a, 0x1a, 0x18, 0x24, 0x2c, 0x1c, 0x24, 0x22, 0x26,
	0x1e, 0x30, 0x20, 0x32, 0x26, 0x16, 0x32, 0x32, 0xc,
	0x2a, 0x22, 0x24, 0x24, 0x2a, 0x26, 0x3c, 0x34, 0x4,
	0x30, 0x22, 0x22, 0x1e, 0x24, 0x36, 0x24, 0x32, 0x22,
	0x2a, 0x2a, 0x26, 0x3c, 0x3a, 0x4, 0x24, 0x24, 0x32,
	0x24, 0x28, 0x30, 0x3a, 0x22, 0x22, 0x2a, 0x30, 0x28,
	0x24, 0x24, 0x3c, 0x24, 0x28, 0x3a, 0x32, 0x2a, 0x2c,
	0x2a, 0x2a, 0x36, 0x3a, 0x2a, 0x28, 0x2c, 0x30, 0x30,
	0x32, 0x32, 0x2e, 0x2c, 0x30, 0x3c, 0x32, 0x32, 0x38,
	0x3a, 0x32, 0x32, 0x38, 0x3a, 0x38 };

static char *NextLine (const char *pData);
static int CopyLine (char *pDest, const char *pSrc);
static int LineToStr (char *pDest, const char *pSrc);
static char *FindSymbol (char *pSym, char *pData, int datasize);

static int ExportBitmap (FFEBitmap *pBitmap, char *pFilename);
static int ReadData (char *&pSrc, UCHAR *pData);


int main (int argc, char **argv)
{
	FILE *pFile;
	int filesize, i;
	char *pImage, *pCur, *pBitmap;
	static UCHAR pData[100000];
	static char pLStr[10000];
	char pFilename[20];

	if (argc!=3) {
		fprintf (stderr, "Wrong number of arguments\n");
		fprintf (stderr, "Syntax: exppics [listsym] [datafile]\n");
		return 1;
	}

	pFile = fopen (argv[2], "rt");
	if (pFile == NULL) {
		fprintf (stderr, "Couldn't open file %s\n", argv[2]);
		return 1;
	}

	fseek (pFile, 0, SEEK_END);
	filesize = ftell (pFile);
	fseek (pFile, 0, SEEK_SET);
	pImage = (char *) malloc (filesize+1);
	filesize = fread (pImage, 1, filesize, pFile);
	fclose (pFile);
	pImage[filesize] = 0;
	i=0;

	pCur = FindSymbol (argv[1], pImage, filesize);
	if (!pCur) {
		fprintf (stderr, "Symbol %s not found\n", argv[1]);
		return 1;
	}

	for (i=0; pCur-pImage < filesize; pCur = NextLine (pCur), i++)
	{
		char *pTok;
		LineToStr (pLStr, pCur);
printf ("pLStr = %s\n", pLStr);
		pTok = strtok (pLStr, " \t\r");
		if (pTok == NULL || strcmp (pTok, "dd")) {
printf ("Breaking at pTok = %s\n", pTok);
		break;
	}
		pTok = strtok (NULL, " \t\r");
		pTok = FindSymbol (pTok, pImage, filesize);
		if (!pTok) continue;
		

		sprintf (pFilename, "ffe%03i.bmp", i);
		ReadData (pTok, pData);
		int rval = ExportBitmap ((FFEBitmap *)pData, pFilename);
		if (!rval) fprintf (stderr,	"Failed to write file %s", pFilename);
	}

	free (pImage);
	return 0;
}


// ASCII line processing functions

static char *NextLine (const char *pData)
{
	char *pNext = strchr (pData, 0xa);
	return ++pNext;
}

static int CopyLine (char *pDest, const char *pSrc)
{
	char *pEnd = strchr (pSrc, 0xa);
	int strsize = int(pEnd - pSrc);
	strncpy (pDest, pSrc, strsize+1);
	return strsize;
}

static int LineToStr (char *pDest, const char *pSrc)
{
	char *pEnd = strchr (pSrc, 0xa);
	int strsize = int(pEnd - pSrc);
	strncpy (pDest, pSrc, strsize);
	pDest[strsize] = NULL;
	return strsize;
}

static char *FindSymbol (char *pSym, char *pData, int datasize)
{
	static char pLStr[100000];
	char *pCur = pData;
	int pos, len;
	int symlen = strlen (pSym);

	for (; pCur-pData < datasize; pCur = NextLine (pCur))
	{
		len = LineToStr (pLStr, pData);
printf
		pos = strspn (pLStr, " \t\r");
		if (pos >= len) continue;
		if (!strncmp (pLStr, pSym, symlen)) continue;
		return NextLine (pCur);
	}
	return NULL;
}

// Generalised NASM format data reader

static int ReadData (char *&pSrc, UCHAR *pData)
{
	int n = 0, i = 0;
	static char pLStr[100000];

	LineToStr (pLStr, pSrc);

	while (1)		// Per-line loop
	{
		i = strspn (pLStr, "\t ");
		if (strncmp (pLStr+i, "db ", 3)) break;

		i += strcspn (pLStr+i, "0'");
		while (pLStr[i] != 0)
		{
			if (pLStr[i] == '0') {
				pData[n++] = strtol (pLStr+i, NULL, 0);
				i += strcspn (pLStr+i, ",");
			} else {
				int nextq = strcspn (pLStr+i+1, "'");
				strncpy ((char *)pData+n, pLStr+i+1, nextq);
				i += nextq + 2; n += nextq;
			}
			i += strcspn (pLStr+i, "0'");
		}	

		pSrc = NextLine (pSrc);
		LineToStr (pLStr, pSrc);
	}
	return n;
}


// FFEBitmap export function

static int ExportBitmap (FFEBitmap *pBitmap, char *pFilename)
{
	int i, j, w, h, sw;
	ULONG zero, col;
	USHORT pHead[27];						// Complete header size
	FILE *pFile;

	for (i=0; i<27; i++) pHead[i] = 0;		// Zero whole header
	w = pBitmap->height;
	h = pBitmap->width;
	sw = w + 3 & -4;
	zero = 0;

	pFile = fopen (pFilename, "wb");
	if (pFile == NULL) return 0;

	pHead[0] = 0x4D42;			// RgbMap type 'BM'
	*(ULONG *)(pHead+1) = 54 + 256*4 + sw*h;	// Total filesize
	pHead[5] = 54 + 256*4;		// Offset to data
	pHead[7] = 40;				// Size of info structure
	pHead[9] = USHORT(w);		// Width
	pHead[11] = USHORT(h);		// Height
	pHead[13] = 1;				// Planes
	pHead[14] = 8;				// Bpp

	fwrite (pHead, 2, 27, pFile);		// Write header
	for (i=0; i<128; i++) {
		ULONG col = (ULONG(pFFEPal[i*3]) << 2)
			+ (ULONG(pFFEPal[i*3+1]) << 10)
			+ (ULONG(pFFEPal[i*3+2]) << 18);
		fwrite (&col, 4, 1, pFile);			// Write Palette
	}
	for (i=0; i<128; i++) fwrite (&zero, 4, 1, pFile);

	for (i=(h-1)*w; i>=0; i-=w) {
		fwrite (pBitmap->pData+i+j, 1, w, pFile);	// Write data
		fwrite (&zero, 1, sw-w, pFile);		// Write padding
	}

	fclose (pFile);
	return 1;
}


