/* Cheat Database Converter : MAME 37B16 -> RAINE 0.32a */
/* Coded by JCK from The Ultimate Patchers              */
/* Version 4.0B - Release date : 19th of July 2001      */

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

#ifdef __linux__
#define stricmp strcasecmp
#endif

#define MAXLINELENGTH	2048
#define MAXCHEATS		256

struct cheat_struct
{
	char Game[9];
	int  Cpu;
	int  Addr;
	int  Data;
	int  Mask;
	int  Type;
	char Name[MAXLINELENGTH];
	char More[MAXLINELENGTH];
} Cheat;

const int OldTypeTable[] =
{
	0,		1,
	20,		21,
	40,		41,
	998,
	-1
};

const int NewTypeTable[] =
{
	0x00,		0x08,
	0x04,		0x0c,
	0x04,		0x0c,
	0x10,
	-1
};


void DisplayHelp(char *ProgName)
{
  printf("Syntax : %s <CheatFileIn> <CheatFileOut> <GameName>\n\n", ProgName);
}

int ExistFile(char *FileName)
{
  FILE *F;

  F = fopen(FileName,"r");
  if (!F)    /* If the file doesn't exist .... */
	return(0);
  else
  {
	fclose(F);
	printf("\nFile '%s' already exists ! ", FileName);
	printf("Do you want to overwrite it (y = YES) ? ");
	return(toupper(getchar()) == 'Y' ? 0 : 1);
  }
}

int FindTypeInTable(int Type)
{
	const int *cType;
	
	for(cType = OldTypeTable; *cType != -1; cType++)
	{	
		if(*cType == Type)
			return (cType - OldTypeTable);
	}
	return -1;
}

int main(int argc, char **argv)
{
  FILE *FI;
  FILE *FO;
  char *ptr;
  char SI[MAXLINELENGTH];
  char SO[MAXLINELENGTH];
  char fmt[40];
  char CFI[80] = "";
  char CFO[80] = "";
  char GN[9] = "";
  int  Count = 0;

  int  AddMore;		/* For extra description */
  int  LinkedCheat;	/* 1 if 500 <= Type < 998 */
  int  TypeIdx;		/* Index for FindTypeInTable function */

  if (2 <= argc)    /* Copy the 1st parameter if it exists */
	strcpy(CFI, argv[1]);

  if (3 <= argc)    /* Copy the 2nd parameter if it exists */
	strcpy(CFO, argv[2]);

  if (4 <= argc)    /* Copy the 3rd parameter if it exists and convert it */
  {
	int i;

	strcpy(GN, argv[3]);
	for (i = 0; i < strlen(GN); i ++)
		GN[i] = (unsigned char)tolower(GN[i]);
  }

  if ((!CFI[0]) || (!CFO[0]) || (!GN[0]))    /* Wrong number of arguments */
  {
	DisplayHelp(argv[0]);
	return(99);
  }

  if (!stricmp(CFI, CFO))
  {
	printf("Input and Output files must have a different name !\n\n");
	return(1);
  }

  FI = fopen(CFI,"r");    /* Open 1st file for input */

  if (!FI)
  {
	printf("\nUnable to read '%s' !\n\n", CFI);
	return(2);
  }

  FO = fopen(CFO,"a+");    /* Open 2nd file for output (append) */

  if (!FO)
  {
	printf("\nUnable to write '%s' !\n\n", CFO);
	return(3);
  }

  printf("\nAdding cheats for game '%s' in '%s' into '%s' .... ", GN, CFI, CFO);
  for(;;)
  {
	if (Count == MAXCHEATS)    /* RAINE doesn't support more than 256 cheats */
		break;

	if (fgets(SI, MAXLINELENGTH, FI) == NULL)
		break;

	if ((!SI[0]) || (SI[0] == '\n'))		/* Ignore empty lines */
		continue;

	if (SI[0] == ';')					/* Ignore comment lines */
		continue;

	ptr = strtok(SI, ":");				/* Extract the name of the game */
	strcpy (Cheat.Game, ptr);

	if (!strcmp(GN, Cheat.Game))			/* Compare the names of the game */
	{
		ptr = strtok(NULL, ":");		/* Extract the CPU */
		sscanf(ptr,"%d", &Cheat.Cpu);
		ptr = strtok(NULL, ":");		/* Extract the address */
		sscanf(ptr,"%x", &Cheat.Addr);
		ptr = strtok(NULL, ":");		/* Extract the byte to poke */
		sscanf(ptr,"%x", &Cheat.Data);
		ptr = strtok(NULL, ":");		/* Extract the type of cheat */
		sscanf(ptr,"%d", &Cheat.Type);
		ptr = strtok(NULL, ":");		/* Extract the description */
		strcpy(Cheat.Name,ptr);
		strcpy(Cheat.More,"\n");
		ptr = strtok(NULL, ":");
		if (ptr)
			strcpy(Cheat.More,ptr);		/* Extract the extra description */
		if (strstr(Cheat.Name,"\n") != NULL)
			Cheat.Name[strlen(Cheat.Name)-1] = 0;
		if (strstr(Cheat.More,"\n") != NULL)
			Cheat.More[strlen(Cheat.More)-1] = 0;

		Cheat.Name[255] = 0;			/* To avoid having too long lines in RAINE */
		Cheat.More[255] = 0;

      	Cheat.Mask = 0;

		AddMore = (Cheat.More[0]);
		LinkedCheat = ((Cheat.Type >= 500) & (Cheat.Type < 998));

		if (LinkedCheat)
			Cheat.Type -= 500;

		TypeIdx = FindTypeInTable(Cheat.Type);

		if ((TypeIdx >=0) && (Cheat.Cpu == 0))
		{
			if ((Cheat.Type >= 20) & (Cheat.Type <= 44))
				Cheat.Mask = Cheat.Data;

			if ((Cheat.Type >= 40) & (Cheat.Type <= 44))
				Cheat.Data = 0;

			/* Formatting output */
			if (Cheat.Type != 998)
				strcpy (fmt, "Cheat%02d = %X:%02X:%02X:%02X:%s");
			else
				strcpy (fmt, "Cheat%02d = %X:%08X:%02X:%02X:%s");

			Cheat.Type = NewTypeTable[TypeIdx];

			if (!Count)    /* First occurence, so we copy the header */
				fprintf (FO, "[%s:cheats]\n", GN);

			if (AddMore)
				strcat (fmt,":%s");

			strcat (fmt, "\n");

			fprintf (FO, fmt, Count, Cheat.Addr, Cheat.Data, Cheat.Mask, Cheat.Type,
						Cheat.Name, (AddMore ? Cheat.More : ""));

		      Count++;
		}
	}
  }

  if (Count)
	fprintf(FO, "\n");

  fclose(FI);
  fclose(FO);

  printf ("\n%d cheat codes have been added.\n\n", Count); 

  return(0);
}
