#include "cps.h"
// CPS (general)

int Cps = 0;							// 1 = CPS1, 2 = CPS2, 3 = CPS Changer
int Cps1Qs = 0;

int SlowSoundFixHaxx0rThingie;

int nCpsCycles = 0;						// Cycles per frame
int	nCpsCyclesLine;						// Cycles per rasterline

unsigned char *CpsGfx =NULL; unsigned int nCpsGfxLen =0; // All the graphics
unsigned char *CpsRom =NULL; unsigned int nCpsRomLen =0; // Program Rom (as in rom)
unsigned char *CpsCode=NULL; unsigned int nCpsCodeLen=0; // Program Rom (decrypted)
unsigned char *CpsZRom=NULL; unsigned int nCpsZRomLen=0; // Z80 Roms
         char *CpsQSam=NULL; unsigned int nCpsQSamLen=0; // QSound Sample Roms
unsigned char *CpsAd  =NULL; unsigned int nCpsAdLen  =0; // ADPCM Data
unsigned char *CpsStar=NULL;
unsigned int nCpsGfxScroll[4]={0,0,0,0}; // Offset to Scroll tiles
unsigned int nCpsGfxMask=0;   // Address mask

// Separate out the bits of a byte
inline static unsigned int Separate(unsigned int b)
{
  unsigned int a;
  a=b;                                   // 00000000 00000000 00000000 11111111
  a=((a&0x000000f0)<<12)|(a&0x0000000f); // 00000000 00001111 00000000 00001111
  a=((a&0x000c000c)<< 6)|(a&0x00030003); // 00000011 00000011 00000011 00000011
  a=((a&0x02020202)<< 3)|(a&0x01010101); // 00010001 00010001 00010001 00010001
  return a;
}
// Precalculated table of the Separate function
static unsigned int SepTable[256];

static int SepTableCalc()
{
  int i=0;
  static int bDone=0;
  if (bDone) return 0; // Already done it
  for (i=0;i<256;i++) SepTable[i]=Separate(255 - i);
  bDone=1; // done it
  return 0;
}

// Allocate space and load up a rom
static int LoadUp(unsigned char **pRom,int *pnRomLen,int nNum)
{
  unsigned char *Rom=NULL; int nRet=0;
  struct BurnRomInfo ri;

  ri.nLen=0; BurnDrvGetRomInfo(&ri,nNum); // Find out how big the rom is
  if (ri.nLen<=0) return 1;
  // Load the rom
  Rom=(unsigned char *)malloc(ri.nLen);

  if (Rom==NULL) return 1;
  nRet=BurnLoadRom(Rom,nNum,1);  if (nRet!=0) { free(Rom); return 1; }
  // Success
  *pRom=Rom; *pnRomLen=ri.nLen;
  return 0;
}

// ----------------------------CPS1--------------------------------
// Load 1 rom and interleve in the CPS style:
// rom  : aa bb
// --ba --ba --ba --ba --ba --ba --ba --ba 8 pixels (four bytes)
//                                                  (skip four bytes)

static int CpsLoadOne(unsigned char *Tile,int nNum,int nWord,int nShift)
{
  int i=0;
  unsigned char *Rom=NULL; int nRomLen=0;
  unsigned char *pt=NULL,*pr=NULL;

  LoadUp(&Rom,&nRomLen,nNum); if (Rom==NULL) return 1;

  nRomLen&=~1; // make sure even

  for (i=0,pt=Tile,pr=Rom; i<nRomLen; pt+=8)
  {
    unsigned int Pix; // Eight pixels
    unsigned char b;
    b=*pr++; i++; Pix=SepTable[b];
    if (nWord) { b=*pr++; i++; Pix|=SepTable[b]<<1; }

    Pix<<=nShift;
    *((unsigned int *)pt)|=Pix;
  }

  free(Rom);
  return 0;
}

static int CpsLoadOneDinoh2(unsigned char *Tile, int nNum, int nWord, int nOffset)
{
	int i = 0;
	unsigned char *Rom1 = NULL, *Rom2 = NULL;
	int nRomLen1 = 0, nRomLen2 = 0;
	unsigned char *pt = NULL, *pr = NULL;
	
	LoadUp(&Rom1, &nRomLen1, nNum);
	if (Rom1 == NULL) return 1;
	LoadUp(&Rom2, &nRomLen2, nNum + 1);
	if (Rom2 == NULL) return 1;
	
	for (i = 0, pt = Tile, pr = Rom1 + (0x80000 * nOffset); i < 0x80000; pt += 8) {
		unsigned int Pix; // Eight pixels
		unsigned char b;
		b = *pr++; i++; Pix = SepTable[b];
		if (nWord) { b = *pr++; i++; Pix |= SepTable[b] << 1; }

		Pix <<= 0;
		*((unsigned int *)pt) |= Pix;
	}
	
	for (i = 0, pt = Tile, pr = Rom2 + (0x80000 * nOffset); i < 0x80000; pt += 8) {
		unsigned int Pix; // Eight pixels
		unsigned char b;
		b = *pr++; i++; Pix = SepTable[b];
		if (nWord) { b = *pr++; i++; Pix |= SepTable[b] << 1; }

		Pix <<= 2;
		*((unsigned int *)pt) |= Pix;
	}
	
	free(Rom2);
	free(Rom1);
	return 0;
}

static int CpsLoadOnePang(unsigned char *Tile,int nNum,int nWord,int nShift)
{
  int i=0;
  unsigned char *Rom=NULL; int nRomLen=0;
  unsigned char *pt=NULL,*pr=NULL;

  LoadUp(&Rom,&nRomLen,nNum); if (Rom==NULL) return 1;

  nRomLen&=~1; // make sure even

  for (i=0x100000,pt=Tile,pr=Rom+0x100000; i<nRomLen; pt+=8)
  {
    unsigned int Pix; // Eight pixels
    unsigned char b;
    b=*pr++; i++; Pix=SepTable[b];
    if (nWord) { b=*pr++; i++; Pix|=SepTable[b]<<1; }

    Pix<<=nShift;
    *((unsigned int *)pt)|=Pix;
  }

  free(Rom);
  return 0;
}

int CpsLoadTiles(unsigned char *Tile,int nStart)
{
  // left  side of 16x16 tiles
  CpsLoadOne(Tile  , nStart  ,1,0);
  CpsLoadOne(Tile  , nStart+1,1,2);
  // right side of 16x16 tiles
  CpsLoadOne(Tile+4, nStart+2,1,0);
  CpsLoadOne(Tile+4, nStart+3,1,2);
  return 0;
}

int CpsLoadTilesPang(unsigned char *Tile,int nStart)
{
  CpsLoadOne(Tile  , nStart  ,1,0);
  CpsLoadOne(Tile  , nStart+1,1,2);
  CpsLoadOnePang(Tile+4, nStart  ,1,0);
  CpsLoadOnePang(Tile+4, nStart+1,1,2);

  return 0;
}

int CpsLoadTilesDinoh2(unsigned char* Tile, int nStart)
{
	CpsLoadOneDinoh2(Tile + 0 + 0x000000, nStart, 1, 0);
	CpsLoadOneDinoh2(Tile + 4 + 0x000000, nStart, 1, 1);
	CpsLoadOneDinoh2(Tile + 0 + 0x200000, nStart, 1, 2);
	CpsLoadOneDinoh2(Tile + 4 + 0x200000, nStart, 1, 3);
	return 0;
}

int CpsLoadTilesByte(unsigned char *Tile,int nStart)
{
  CpsLoadOne(Tile  ,nStart+ 0,0,0);
  CpsLoadOne(Tile  ,nStart+ 1,0,1);
  CpsLoadOne(Tile  ,nStart+ 2,0,2);
  CpsLoadOne(Tile  ,nStart+ 3,0,3);
  CpsLoadOne(Tile+4,nStart+ 4,0,0);
  CpsLoadOne(Tile+4,nStart+ 5,0,1);
  CpsLoadOne(Tile+4,nStart+ 6,0,2);
  CpsLoadOne(Tile+4,nStart+ 7,0,3);
  return 0;
}

// ----------------------------CPS2--------------------------------
// Load 1 rom and interleve in the CPS2 style:
// rom  : aa bb -- -- (4 bytes)
// --ba --ba --ba --ba --ba --ba --ba --ba 8 pixels (four bytes)
//                                                  (skip four bytes)

//  memory 000000-100000 are in even word fields of first 080000 section
//  memory 100000-200000 are in  odd word fields of first 080000 section
// i=ABCD nnnn nnnn nnnn nnnn n000
// s=00AB Cnnn nnnn nnnn nnnn nnD0

inline static void Cps2Load100000(unsigned char *Tile,unsigned char *Sect,int nShift)
{
  unsigned char *pt,*pEnd,*ps;
  pt=Tile; pEnd=Tile+0x100000; ps=Sect;

  do
  {
    unsigned int Pix; // Eight pixels
    Pix =SepTable[ps[0]];
    Pix|=SepTable[ps[1]]<<1;
    Pix<<=nShift;
    *((unsigned int *)pt)|=Pix;

    pt+=8; ps+=4;
  }
  while (pt<pEnd);
}

static int Cps2LoadOne(unsigned char *Tile,int nNum,int nShift)
{
  unsigned char *Rom=NULL; int nRomLen=0;
  int b; unsigned char *pt,*pr;

  LoadUp(&Rom,&nRomLen,nNum); if (Rom==NULL) return 1;

  // Go through each section
  pt=Tile; pr=Rom;
  for (b=0;b<nRomLen>>19;b++)
  {
    Cps2Load100000(pt,pr  ,nShift); pt+=0x100000;
    Cps2Load100000(pt,pr+2,nShift); pt+=0x100000;
    pr+=0x80000;
  }

  free(Rom);
  return 0;
}

int Cps2LoadTiles(unsigned char *Tile,int nStart)
{
  // left  side of 16x16 tiles
  Cps2LoadOne(Tile  ,nStart  ,0);
  Cps2LoadOne(Tile  ,nStart+1,2);
  // right side of 16x16 tiles
  Cps2LoadOne(Tile+4,nStart+2,0);
  Cps2LoadOne(Tile+4,nStart+3,2);
  return 0;
}

int CpsLoadStars(unsigned char* pStar, int nStart)
{
	unsigned char* pTemp[2] = { NULL, NULL };
	int nLen;

	for (int i = 0; i < 2; i++) {
		if (LoadUp(&pTemp[i], &nLen, nStart + (i << 1))) {
			free(pTemp[0]);
			free(pTemp[1]);
		}
	}

	for (int i = 0; i < 0x1000; i++) {
		pStar[i] = pTemp[0][i << 1];
		pStar[0x01000 + i] = pTemp[1][i << 1];
	}

	free(pTemp[0]);
	free(pTemp[1]);
	
	return 0;
}

// ----------------------------------------------------------------

int CpsInit()
{
  int nMemLen=0; int i=0;
  if (Cps==2) nCpsCycles=11800000/60;
  else        nCpsCycles=10000000/60;

  nMemLen  =  nCpsGfxLen+nCpsRomLen+nCpsCodeLen+nCpsZRomLen+nCpsQSamLen+nCpsAdLen;

  if(slamPro==1) nMemLen+=nCpsZRomLen*2;

  // Allocate Gfx, Rom and Z80 Roms
  CpsGfx=(unsigned char *)malloc(nMemLen);
  if (CpsGfx==NULL) return 1;
  memset(CpsGfx,0,nMemLen);

  CpsRom  =CpsGfx +nCpsGfxLen;
  CpsCode =CpsRom +nCpsRomLen;
  if (slamPro==1) { CpsEncZRom =CpsCode+nCpsCodeLen; CpsZRom=CpsEncZRom+nCpsZRomLen*2;}
  else CpsZRom =CpsCode+nCpsCodeLen;
  CpsQSam =(char *)(CpsZRom+nCpsZRomLen);
  CpsAd   =(unsigned char *)(CpsQSam+nCpsQSamLen);

  // Create Gfx addr mask
  for (i=0;i<31;i++) { if ( (1<<i) >= (int)nCpsGfxLen) break; }
  nCpsGfxMask=(1<<i)-1;

  if (Cps==2)
  {
    nCpsGfxScroll[1]=nCpsGfxScroll[2]=nCpsGfxScroll[3]=0x800000; // Offset to Scroll tiles
  }
  else
  {
    nCpsGfxScroll[1]=nCpsGfxScroll[2]=nCpsGfxScroll[3]=0;
  }

  if (nCpsZRomLen>=5)
  {
    // 77->cfff and rst 00 in case driver doesn't load
    CpsZRom[0]=0x3e; CpsZRom[1]=0x77;
    CpsZRom[2]=0x32; CpsZRom[3]=0xff; CpsZRom[4]=0xcf;
    CpsZRom[5]=0xc7;
  }

  // mprot
  CpsMProt[0]=0x40;
  CpsMProt[1]=0x42;
  CpsMProt[2]=0x44;
  CpsMProt[3]=0x46;

  // Board ID (ffight is the default)
  CpsBID[0]=0x60;
  CpsBID[1]=0x00;
  CpsBID[2]=0x04;

  SepTableCalc();								// Precalc the separate table

  CpsReset=0; Cpi01A=0; Cpi01C=0; Cpi01E=0;		// blank other inputs

  nCpsLcReg=0x66;								// Layer controller usually at 0x66
  // Usual bits for layer enable
  CpsLayEn[1]=2; CpsLayEn[2]=4; CpsLayEn[3]=8; CpsLayEn[4]=0; CpsLayEn[5]=0;

  SlowSoundFixHaxx0rThingie = 0;

  return 0;
}

int CpsExit()
{
  SlowSoundFixHaxx0rThingie = 0;
	
  CpsLayEn[1]=0; CpsLayEn[2]=0; CpsLayEn[3]=0; CpsLayEn[4]=0; CpsLayEn[5]=0;
  nCpsLcReg=0;
  nCpsGfxScroll[1]=nCpsGfxScroll[2]=nCpsGfxScroll[3]=0;
  nCpsGfxMask=0;
  CpsAd=NULL;
  CpsQSam=NULL;
  CpsZRom=NULL;
  CpsCode=NULL;
  CpsRom =NULL;
  free(CpsGfx);
  CpsGfx=NULL;
  CpsStar=NULL;
  nCpsCycles=0;

  return 0;
}
