// =============================================================================
// -----------------------------------------------------------------------------
// Program
// -----------------------------------------------------------------------------

#include <stdio.h>
#include <windows.h>
#include <dirent.h>
#include <direct.h>
#include "bitmap.h"
#include "Kosinski Special.h"

	// This defines if the rendering method is double pixeled, 
	// i.e. 01 12 23 34 instead of 01 23 45 67, this allows precise pixels
	// to be selected when scaling, but comes at the cost of twice the amount
	// of memory.

	// WARNING, Qualtity set to TRUE will render as a full X by Y pixel bitmap
	//          there as FALSE will render as tiles for sprite format...

#define QUALITY FALSE
#define COMPRESS TRUE

#define FILM_W 40	// width of resulting film
#define FILM_H 32	// height of resulting film

char Direct [0x1000]; int Folder = 0;
char Text [0x1000];
int IncludeLoc = 0;

int TotalSize = 0;

struct COLLIST

{
	PIX_BGRA Col;
	int Num;
};

// =============================================================================
// -----------------------------------------------------------------------------
// Finding all bitmaps in a folder and generating the graphics
// -----------------------------------------------------------------------------

void ProcessFilm ( )

{
	IMG Palette; Palette.Data = NULL;
	IMG *Frames = NULL;
	int FramesSize = 0;
	int FramesPos = 0;

	PIX_BGRA PadColour = { 0, 0x80, 0, 0 };

	DIR *Dir;
	struct dirent *Ent;
	Dir = opendir (Direct);
	if (Dir == NULL)
	{
		printf ("   Error, could not open the current directory\n");
		return;
	}
	Direct [Folder++] = '\\';
	snprintf (&Direct [Folder], 0x1000-Folder, "_Position.txt");
	FILE *File = fopen (Direct, "rb");
	int AdjustX = 0;
	int AdjustY = 0;
	int Rept = 0;
	if (File != NULL)
	{
		fscanf (File, "%d %d %d", &AdjustX, &AdjustY, &Rept);
		fclose (File);
	}
	printf ("    Adjustments    = %d x %d\n", AdjustX, AdjustY);
	printf ("    \"rept\" count = %d\n\n", Rept);
	while ((Ent = readdir (Dir)) != NULL)
	{
		int FileName, ExtName = -1;
		int Loc = 0;
		for (FileName = Folder; Ent->d_name [Loc] != 0; FileName++, Loc++)
		{
			char Byte = Ent->d_name [Loc];
			Direct [FileName] = Byte;
			if (Byte == '.')
			{
				ExtName = FileName;
			}
		}
		if (ExtName == -1)
		{
			ExtName = FileName;
		}
		Direct [FileName] = 0;
		FileName = Folder;
		if (strcmp (&Direct [FileName], "_Palette.bmp") == 0)
		{
			// Load palette...
			printf ("    \"%s\" (Palette file!)\n", &Direct [FileName]);
			int Return = ImageLoad (&Palette, Direct);
			if (Return != 0)
			{
				switch (Return)
				{
					case 1: printf ("    Error; bitmap file cannot be opened\n"); break;
					case 2: printf ("    Error; bitmap allocation error\n"); break;
					case 3: printf ("    Error; bitmap fread didn't copy full size\n"); break;
					case 4: printf ("    Error; bitmap pixel format not supported\n"); break;
					case 5: printf ("    Error; bitmap image is not a valid \"BM\" format\n"); break;
					case 6: printf ("    Error; bitmap compression not supported\n"); break;
				}
				while (FramesPos-- > 0)
				{
					free (Frames [FramesPos].Data); Frames [FramesPos].Data = NULL;
				}
				free (Palette.Data); Palette.Data = NULL;
				free (Frames); Frames = NULL;
				fflush (stdin); getchar ( );
				return;
			}
			for (int Loc = 0; Loc < Palette.Size; Loc++)
			{
				Palette.Data [Loc].Red &= 0xE0;
				Palette.Data [Loc].Green &= 0xE0;
				Palette.Data [Loc].Blue &= 0xE0;
			}
		}
		else if (strcmp (&Direct [ExtName], ".bmp") == 0)
		{
			// Image file...
			printf ("    \"%s\"\n", &Direct [FileName]);

			if (FramesPos >= FramesSize)
			{
				FramesSize <<= 1;
				if (FramesSize == 0) { FramesSize = 0x10; }
				IMG *New = (IMG*) realloc (Frames, FramesSize * sizeof (IMG));
				if (New == NULL)
				{
					while (FramesPos-- > 0)
					{
						free (Frames [FramesPos].Data); Frames [FramesPos].Data = NULL;
					}
					free (Palette.Data); Palette.Data = NULL;
					free (Frames); Frames = NULL;
					printf ("    Error, could not reallocate IMG memory\n");
					fflush (stdin); getchar ( );
					return;
				}
				Frames = New;
			}
			int Return = ImageLoad (&Frames [FramesPos], Direct);
			if (Return != 0)
			{
				switch (Return)
				{
					case 1: printf ("    Error; bitmap file cannot be opened\n"); break;
					case 2: printf ("    Error; bitmap allocation error\n"); break;
					case 3: printf ("    Error; bitmap fread didn't copy full size\n"); break;
					case 4: printf ("    Error; bitmap pixel format not supported\n"); break;
					case 5: printf ("    Error; bitmap image is not a valid \"BM\" format\n"); break;
					case 6: printf ("    Error; bitmap compression not supported\n"); break;
				}
				while (FramesPos-- > 0)
				{
					free (Frames [FramesPos].Data); Frames [FramesPos].Data = NULL;
				}
				free (Palette.Data); Palette.Data = NULL;
				free (Frames); Frames = NULL;
				fflush (stdin); getchar ( );
				return;
			}
			FlipImage (&Frames [FramesPos]);
			TruncateImage (&Frames [FramesPos], ((Frames [FramesPos].SizeX/2) - (FILM_W / 2)) + AdjustX, ((Frames [FramesPos].SizeY/2) - (FILM_H / 2)) + AdjustY, ((Frames [FramesPos].SizeX/2) + (FILM_W / 2)) + AdjustX, ((Frames [FramesPos].SizeY/2) + (FILM_H / 2)) + AdjustY, PadColour);
			for (int Loc = 0; Loc < Frames [FramesPos].Size; Loc++)
			{
				Frames [FramesPos].Data [Loc].Red &= 0xE0;
				Frames [FramesPos].Data [Loc].Green &= 0xE0;
				Frames [FramesPos].Data [Loc].Blue &= 0xE0;
			}
			FramesPos++;
		}
	}
	FramesSize = FramesPos;
	if (FramesSize == 0)
	{
		printf ("    No data in this folder; skipping...\n");
		return;
	}

// -----------------------------------------------------------------------------
// Getting palette
// -----------------------------------------------------------------------------

	int ColSize = 0;
	int ColPos = 0;
	COLLIST *ColList = NULL;

	int PalCount = 0;
	if (Palette.Data == NULL)
	{
		Palette.Size = 0x0F;
		Palette.SizeX = Palette.Size;
		Palette.SizeY = 1;
		Palette.Data = (PIX_BGRA*) malloc (Palette.Size * sizeof (PIX_BGRA));
	}
	else
	{
		PIX_BGRA IgnoreCol = Palette.Data [0];
		for (int Loc = 1; Loc < Palette.Size; Loc++)
		{
			if (	Palette.Data [Loc].Red   != IgnoreCol.Red	||
				Palette.Data [Loc].Green != IgnoreCol.Green	||
				Palette.Data [Loc].Blue  != IgnoreCol.Blue	)
			{
				int PalLoc;
				for (PalLoc = 0; PalLoc < PalCount; PalLoc++)
				{
					if (	Palette.Data [PalLoc].Red   == Palette.Data [Loc].Red	&&
						Palette.Data [PalLoc].Green == Palette.Data [Loc].Green	&&
						Palette.Data [PalLoc].Blue  == Palette.Data [Loc].Blue	)
					{
						break;
					}
				}
				if (PalLoc >= PalCount)
				{
					Palette.Data [PalCount] = Palette.Data [Loc];
					PalCount++;
				}
			}
		}
		ColSize = PalCount;
		ColPos = PalCount;
		ColList = (COLLIST*) calloc (ColSize, sizeof (COLLIST));
		if (ColList == NULL)
		{
			for (int FramesPos = 0; FramesPos < FramesSize; FramesPos++)
			{
				free (Frames [FramesPos].Data); Frames [FramesPos].Data = NULL;
			}
			free (Palette.Data); Palette.Data = NULL;
			free (Frames); Frames = NULL;
			printf ("    Error, could not allocate memory for colour list\n");
			fflush (stdin); getchar ( );
			return;
		}
		for (int PalLoc = 0; PalLoc < PalCount; PalLoc++)
		{
			ColList [PalLoc].Col = Palette.Data [PalLoc];
			ColList [PalLoc].Num = 0x40000000+((PalCount-PalLoc)*0x10000); // set number to something totally impossible...
								// The +((PalCount-PalLoc)*0x10000) is just to ensure the order of colours
								// remains the same, the higher the priority, the more likely the colour is
								// gonna stick
		}
		Palette.Size = 0x0F;
		Palette.SizeX = 0x0F;
		Palette.SizeY = 1;
	}

// -----------------------------------------------------------------------------
// Collecting most occuring colours
// -----------------------------------------------------------------------------

	for (int FramesPos = 0; FramesPos < FramesSize; FramesPos++)
	{
		for (int Loc = 0; Loc < Frames [FramesPos].Size; Loc++)
		{
			int ColLoc;
			for (ColLoc = 0; ColLoc < ColPos; ColLoc++)
			{
				if (	ColList [ColLoc].Col.Red   == Frames [FramesPos].Data [Loc].Red		&&
					ColList [ColLoc].Col.Green == Frames [FramesPos].Data [Loc].Green	&&
					ColList [ColLoc].Col.Blue  == Frames [FramesPos].Data [Loc].Blue	)
				{
					ColList [ColLoc].Num++;
					break;
				}
			}
			if (ColLoc >= ColPos)
			{
				if (ColPos >= ColSize)
				{
					ColSize <<= 1;
					if (ColSize == 0) { ColSize = 0x10; }
					COLLIST *NewList = (COLLIST*) realloc (ColList, ColSize * sizeof (COLLIST));
					if (NewList == NULL)
					{
						free (ColList); ColList = NULL;
						for (int FramesPos = 0; FramesPos < FramesSize; FramesPos++)
						{
							free (Frames [FramesPos].Data); Frames [FramesPos].Data = NULL;
						}
						free (Palette.Data); Palette.Data = NULL;
						free (Frames); Frames = NULL;
						printf ("    Error, could not reallocate memory for colour list\n");
						fflush (stdin); getchar ( );
						return;
					}
					ColList = NewList;
				}
				ColList [ColPos].Col = Frames [FramesPos].Data [Loc];
				ColList [ColPos].Num = 1;
				ColPos++;
			}
		}
	}
	ColSize = ColPos;

	PalCount = 0;
	for ( ; ; )
	{
		bool Exit = TRUE;
		if (PalCount == 0x0F) { break; }
		int BestNum = 0;
		int BestLoc = 0;
		for (int ColLoc = 0; ColLoc < ColSize; ColLoc++)
		{
			if (ColList [ColLoc].Num != 0)
			{
				Exit = FALSE;
				if (ColList [ColLoc].Num > BestNum)
				{
					BestNum = ColList [ColLoc].Num;
					BestLoc = ColLoc;
				}
			}
		}
		if (Exit == TRUE) { break; }
		Palette.Data [PalCount++] = ColList [BestLoc].Col;
		ColList [BestLoc].Num = 0;
	}
	free (ColList); ColList = NULL;

//	SaveBMP (&Palette, "NewPal.bmp", 24);

// -----------------------------------------------------------------------------
// Converting images to pixels and saving
// -----------------------------------------------------------------------------

	int Pos = snprintf (&Direct [Folder], 0x1000 - Folder, "Data\\", FramesPos);
	mkdir (Direct);
	snprintf (&Direct [Folder+Pos], 0x1000 - (Folder+Pos), "_Pal.bin", FramesPos);
	File = fopen (Direct, "wb");
	for (int Loc = 0; Loc < Palette.Size; Loc++)
	{
		fputc ((Palette.Data [Loc].Blue >> 4) & 0x0E, File);
		fputc ((Palette.Data [Loc].Green & 0xE0) | ((Palette.Data [Loc].Red >> 4) & 0x0E), File);
	}
	fclose (File);

	snprintf (&Direct [Folder+Pos], 0x1000 - (Folder+Pos), "_List.asm", FramesPos);
	File = fopen (Direct, "wb");
	fputs ( "; ===========================================================================\r\n"
		"; ---------------------------------------------------------------------------\r\n"
		"; Film rendering subroutines (Generated by \"Film Ripper.exe\")\r\n"
		"; ---------------------------------------------------------------------------\r\n"
		"\r\n"
		"	.Frames:\r\n", File);
	if (Rept != 0)
	{
		snprintf (Text, 0x1000, "		rept	%d\r\n", Rept);
		fputs (Text, File);
	}
	for (int FramesPos = 0; FramesPos < FramesSize; FramesPos++)
	{
		snprintf (Text, 0x1000, "		dc.l	.Frame%0.3d-.Frames\r\n", FramesPos);
		fputs (Text, File);
	}
	if (Rept != 0)
	{
		fputs ("		endr\r\n", File);
	}
	fputs ( "		dc.w	$FFFF\r\n\r\n", File);
	int CurSize = 0;
	for (int FramesPos = 0; FramesPos < FramesSize; FramesPos++)
	{
		for (int Loc = 0; Loc < Frames [FramesPos].Size; Loc++)
		{
			int BestDist = 0x7FFFFFFF;
			int BestLoc = 0;
			for (int PalLoc = 0; PalLoc < Palette.Size; PalLoc++)
			{
				int Red   = Palette.Data [PalLoc].Red   - Frames [FramesPos].Data [Loc].Red;
				int Green = Palette.Data [PalLoc].Green - Frames [FramesPos].Data [Loc].Green;
				int Blue  = Palette.Data [PalLoc].Blue  - Frames [FramesPos].Data [Loc].Blue;
				if (Red   < 0) { Red   = -Red;   }
				if (Green < 0) { Green = -Green; }
				if (Blue  < 0) { Blue  = -Blue;  }
				Red   = 1<<((Red  >>5)&7);	// making the difference being exponential to increase the chances of "like" colours being matched
				Green = 1<<((Green>>5)&7);	// ...seems to work quite well actually =3
				Blue  = 1<<((Blue >>5)&7);
				int Dist = Red + Green + Blue;
				if (Dist < BestDist)
				{
					BestDist = Dist;
					BestLoc = PalLoc;
				}
			}
			Frames [FramesPos].Data [Loc] = Palette.Data [BestLoc];
			Frames [FramesPos].Data [Loc].Alpha = BestLoc + 1;
		}
		snprintf (&Direct [Folder+Pos], 0x1000 - (Folder+Pos), "%0.3d.bmp", FramesPos);
			FlipImage (&Frames [FramesPos]);
		SaveBMP (&Frames [FramesPos], Direct, 24);
			FlipImage (&Frames [FramesPos]);
#if QUALITY==FALSE
		char *Memory = (char*) malloc ((FILM_W * FILM_H) / 2);
#else
		char *Memory = (char*) malloc (FILM_W * FILM_H);
#endif
		if (Memory == NULL)
		{
			printf ("    Error, fatal!  Could not allocate memory for art file...\n");
			break;
		}
		int Size = 0;
#if QUALITY==FALSE
		for (int X = 0; X < Frames [FramesPos].SizeX; X += 8)
		{
			for (int Y = 0; Y < Frames [FramesPos].SizeY; Y += 8)
			{
				for (int YP = 0; YP < 8; YP++)
				{
					for (int XP = 0; XP < 8; XP += 2)
					{
						int Loc = (X + XP) + ((Y + YP) * Frames [FramesPos].SizeX);
						char Byte = Frames [FramesPos].Data [Loc].Alpha << 4;
						if ((X + 1) < Frames [FramesPos].SizeX)
						{
							Byte |= Frames [FramesPos].Data [Loc+1].Alpha & 0x0F;
						}
						else
						{
							Byte |= (Byte >> 4) & 0x0F;
						}
						Memory [Size++] = Byte;
					}
				}
			}
		}
#else
		for (int Y = 0; Y < Frames [FramesPos].SizeY; Y++)
		{
			for (int X = 0; X < Frames [FramesPos].SizeX; X++)
			{
				int Loc = X + (Y * Frames [FramesPos].SizeX);
				char Byte = Frames [FramesPos].Data [Loc].Alpha << 4;
				if ((X + 1) < Frames [FramesPos].SizeX)
				{
					Byte |= Frames [FramesPos].Data [Loc+1].Alpha & 0x0F;
				}
				else
				{
					Byte |= (Byte >> 4) & 0x0F;
				}
				Memory [Size++] = Byte;
			}
		}
#endif

#if COMPRESS==TRUE
		KosComp (Memory, Size);
		snprintf (&Direct [Folder+Pos], 0x1000 - (Folder+Pos), "%0.3d.ksp", FramesPos);
#else
		snprintf (&Direct [Folder+Pos], 0x1000 - (Folder+Pos), "%0.3d.bin", FramesPos);
#endif
		CurSize += Size;
		FILE *Out = fopen (Direct, "wb");
		for (int Loc = 0; Loc < Size; Loc++)
		{
			fputc (Memory [Loc], Out);
		}
		fclose (Out);
		free (Memory); Memory = NULL;

		snprintf (Text, 0x1000, ".Frame%0.3d:	incbin	\"%s\"\r\n", FramesPos, &Direct [IncludeLoc]);
		fputs (Text, File);
	}
	fputs ( "		even\r\n"
		"\r\n; ===========================================================================\r\n", File);
	fclose (File);

	TruncateImage (&Palette, -1, 0, Palette.SizeX, Palette.SizeY, PadColour);
	snprintf (&Direct [Folder+Pos], 0x1000 - (Folder+Pos), "_Pal.bmp", FramesPos);
	PerformResize (&Palette, Palette.SizeX * 8, Palette.SizeY * 8, FALSE);
	SaveBMP (&Palette, Direct, 24);

// -----------------------------------------------------------------------------
// Finish
// -----------------------------------------------------------------------------

	TotalSize += CurSize;
	printf ("\n    Memory cost: %8X\n", CurSize);
	for (int FramesPos = 0; FramesPos < FramesSize; FramesPos++)
	{
		free (Frames [FramesPos].Data); Frames [FramesPos].Data = NULL;
	}
	free (Palette.Data); Palette.Data = NULL;
	free (Frames); Frames = NULL;
}

// =============================================================================
// -----------------------------------------------------------------------------
// Main routine
// -----------------------------------------------------------------------------

int main (int ArgNumber, char **ArgList, char **EnvList)

{
	int DirLoc = 0, Loc;
	for (Loc = 0; ArgList [0] [Loc] != 0; Loc++)
	{
		Direct [Loc] = ArgList [0] [Loc];
		if (Direct [Loc] == '\\' || Direct [Loc] == '/')
		{
			IncludeLoc = DirLoc;
			DirLoc = Loc + 1;
		}
	}
	Direct [Loc] = 0;

	printf ("Film Ripper - by MarkeyJester\n");
	DIR *Dir;
	struct dirent *Ent;
	Dir = opendir (".");
	if (Dir == NULL)
	{
		printf ("   Error, could not open the current directory\n");
		return (0x00);
	}
	while ((Ent = readdir (Dir)) != NULL)
	{
		int Loc = 0;
		for (Folder = DirLoc; Ent->d_name [Loc] != 0; Loc++)
		{
			char Byte = Ent->d_name [Loc];
			Direct [Folder] = Byte;
			if (Byte == '.') { break; }
			Direct [++Folder] = 0;
		}
		if (Direct [Folder] != '.')
		{
			Direct [Folder] = 0;
			printf ("\n -> %s\n\n", &Direct [IncludeLoc]);
			ProcessFilm ( );
		}
	}
	printf ("\n -> Total memory cost: %8X\n", TotalSize);
	printf ("\nPress enter key to exit...\n");
	fflush (stdin); getchar ( ); return (0x00);
}

// =============================================================================
