#include "neogeo.h"
#include "memdumper.h"
// Neo Geo -- misc. support functions

unsigned char nNeoProtectionXor;
unsigned int nNeoNumSpriteRom;

FILE *dataxor_file = NULL;

// This function loads the 68K ROMs
int NeoLoadCode(int nOffset, int nNum, unsigned char* pDest)
{
	struct BurnRomInfo ri;

	for (int i = 0; i < nNum; i++) {
		ri.nLen = 0;
		BurnDrvGetRomInfo(&ri, nOffset + i);

		if ((BurnDrvGetHardwareCode() & HARDWARE_SNK_P32) && (i == 0))
		{
			if (BurnLoadRom(pDest + 0, nOffset + i + 0, 2)) return 1;
			if (BurnLoadRom(pDest + 1, nOffset + i + 1, 2)) return 1;
 
			for (unsigned int j = 0; j < ri.nLen << 1; j+=4)
				BurnByteswap(pDest + j + 1, 2);

			i++;
			pDest += ri.nLen << 1;
			continue;
		}

		if (BurnLoadRom(pDest, nOffset + i, 1)) {
			return 1;
		}

		if ((BurnDrvGetHardwareCode() & HARDWARE_SNK_SWAPP) && (i == 0)) {
			for (unsigned int j = 0; j < (ri.nLen / 2); j++) {
				int k = pDest[j];
				pDest[j] = pDest[j + (ri.nLen / 2)];
				pDest[j + (ri.nLen / 2)] = k;
			}
		}

		pDest += ri.nLen;
	}

	return 0;
}

// This function loads and pre-processes the sprite data
// Modified by Kenshiro to get it run on Xbox(25/03/2009)
int NeoLoadSprites(int nOffset, int nNum, unsigned char* pDest, unsigned int nSpriteSize)
{
	struct BurnRomInfo ri;
	ri.nType = 0;
	ri.nLen = 0;

	int ROM32MB = 0, nRomBlockLen = 0, nDataDone = 0;

	char *NeoDrvName = BurnDrvGetTextA(DRV_NAME);
	FILE *NeoSpriteFile = NULL;

	unsigned int nRomSize = 0;	

	nNeoNumSpriteRom = nNum;
	nNeoSpriteLen = nSpriteSize;
    

	if (BurnDrvGetHardwareCode() & (HARDWARE_SNK_ENCRYPTED_A | HARDWARE_SNK_ENCRYPTED_B)) {
		if (BurnDrvGetHardwareCode() & HARDWARE_SNK_ENCRYPTED_A) {
			NeoGfxDecryptCMC42Init();
		} else {
			if (BurnDrvGetHardwareCode() & HARDWARE_SNK_ENCRYPTED_B) {
				NeoGfxDecryptCMC50Init();
			}
		}

		nRomSize = ((nSpriteSize / nNum) & 0xf00000) ? 0x1000000 : 0x2000000;
		unsigned char* pBuf = (unsigned char *)osd_malloc( nRomSize );
		if (pBuf == NULL) {
			return 1;
		}        
		
		dataxor_file = fopen("Z:\\dataxor.bin", "wb");

		for (int i = 0; i < (nNum + ROM32MB >> 1); i++) {
			if ((nSpriteSize / nNum) == 0x2000000) {		// svcpcb
				ROM32MB = 2;
				BurnLoadRom(pBuf, nOffset + i, 1);

				pcb_gfx_crypt(pBuf, 0);
			} else {						// standard
				BurnLoadRom(pBuf + 0, nOffset + 0 + (i << 1), 2);
				BurnLoadRom(pBuf + 1, nOffset + 1 + (i << 1), 2);

				if (nRomSize == 0x2000000) {			// ms5pcb, svcpcba, kf2k3pcb
					for (int j = 0; j < 0x2000000; j+=4)
						BurnByteswap(pBuf + j + 1, 2);

					pcb_gfx_crypt(pBuf, nNum & 2);
				}
                
			}			
            NeoGfxDecrypt(nNeoProtectionXor, nRomSize, nSpriteSize, pBuf, true);			
		}

		free(pBuf);
		fclose(dataxor_file);
		// Final call
		NeoGfxDecrypt(nNeoProtectionXor, nRomSize, nSpriteSize, pBuf, false);		

	} else {
    
        if (strcmp(BurnDrvGetTextA(DRV_NAME), "svcpcbnd") == 0) {
            return 0;
        }
	
		nSpriteSize = 0;		

		// Compute correct size to gaps into account (Kizuna)
		for (int i = 0; i < nNum - 2; i++) {
			BurnDrvGetRomInfo(&ri, nOffset + i);
			if (ri.nLen > nRomSize) {
				nRomSize = ri.nLen;
			}
		}
		
		NeoSpriteFile = fopen(SPRITE_FILE_PATH, "wb");
		unsigned char *pBuf = NULL;

		for (int i = 0; i < (nNum >> 1); i++) {
		
		    // SNK_SWAPC will be process after
            if (BurnDrvGetHardwareCode() & HARDWARE_SNK_SWAPC) {
			    break;
	 	    }	

		    BurnDrvGetRomInfo(&ri, nOffset + (i << 1));	            		

		    if (i < (nNum >> 1) - 1) {  
			    nDataDone += nRomSize << 1;
		    } else {
			    nDataDone += ri.nLen << 1;
		    }

            nRomBlockLen = nDataDone - nSpriteSize;
			
			if ((pBuf = (unsigned char*)osd_malloc(nRomBlockLen)) == NULL) {
			    return 1;
			} 
            
			memset(pBuf, 0, nRomBlockLen);
            
			
		    BurnLoadRom(pBuf + 0, nOffset + (i << 1), 2);
		    BurnLoadRom(pBuf + 1, nOffset + 1 + (i << 1), 2);

			if ((strcmp(NeoDrvName, "garoubl") == 0) || (strcmp(NeoDrvName, "kog") == 0) || (strcmp(NeoDrvName, "lans2004") == 0) || (strcmp(NeoDrvName, "samsho5b") == 0)) {
	            lans2004_cx_decode(pBuf, nRomBlockLen);
            }

			if (strcmp(NeoDrvName, "kof2002b") == 0) {
			    kof2002b_gfx_decrypt(pBuf, nRomBlockLen);
			}

			if ((strcmp(NeoDrvName, "svcboot") == 0) || (strcmp(NeoDrvName, "svcplus") == 0) || (strcmp(NeoDrvName, "svcplusa") == 0) || (strcmp(NeoDrvName, "svcsplus") == 0)) {
                DoPerm(pBuf, 1);
            }

			if ((strcmp(NeoDrvName, "cthd2003") == 0) || (strcmp(NeoDrvName, "ct2k3sa") == 0) || (strcmp(NeoDrvName, "ct2k3sp") == 0) || (strcmp(NeoDrvName, "matrimbl") == 0)) {
                DoPerm(pBuf, 0);
            }

            fwrite(pBuf, nRomBlockLen, 1, NeoSpriteFile);
            free(pBuf);			

			if (i < (nNum >> 1) - 1) {
				nSpriteSize += nRomSize << 1;
			} else {
				nSpriteSize += ri.nLen << 1;
			}
		}		

	 	// Swap data for viewpoin, aof, ssideki, kotm2, more
	 	if (BurnDrvGetHardwareCode() & HARDWARE_SNK_SWAPC) {
		
		    if ((pBuf = (unsigned char*)osd_malloc(nNeoSpriteLen)) == NULL) {
			    return 1;
			}
            
			memset(pBuf, 0, nNeoSpriteLen);            			
			
			for (int i = 0; i < (nNum >> 1); i++) {	
			
                BurnDrvGetRomInfo(&ri, nOffset + (i << 1));		
				
			    BurnLoadRom(pBuf + 0 + nSpriteSize, nOffset + (i << 1), 2);
		        BurnLoadRom(pBuf + 1 + nSpriteSize, nOffset + 1 + (i << 1), 2);
				
				if (i < (nNum >> 1) - 1) {
				    nSpriteSize += nRomSize << 1;
			    } else {
				    nSpriteSize += ri.nLen << 1;
			    }
			}
			
			for (int i = 0; i < 0x200000; i++) {
				unsigned char n = pBuf[i + 0x400000];
				pBuf[i + 0x400000] = pBuf[i + 0x200000];
				pBuf[i + 0x200000] = n;
	 		}

            fwrite(pBuf, nNeoSpriteLen, 1, NeoSpriteFile);
            free(pBuf);				
	 	}		
		fclose(NeoSpriteFile);
	}
	return 0;
}

// Modified by Kenshiro to get it run on Xbox(25/03/2009)
void NeoDecodeSprites(unsigned char* pDest, int nSize)
{

	FILE *NeoSpriteFile = NULL;
    int nRomBlockLen = nSize >> 3;
    
	NeoSpriteFile = fopen(SPRITE_FILE_PATH, "rb+");
    pDest = (unsigned char*)osd_malloc(nRomBlockLen);	

	for (int i = 0; i < 8; i++) {

        unsigned char* pStart = NULL;
	    unsigned char* pEnd = NULL;        

		fseek(NeoSpriteFile, i * nRomBlockLen, SEEK_SET);
		memset(pDest, 0, nRomBlockLen);
		fread(pDest, nRomBlockLen, 1, NeoSpriteFile);

        pStart = pDest;
		pEnd = pStart + nRomBlockLen;       

		{
			int nStep = 8;
			if (BurnDrvGetHardwareCode() & (HARDWARE_SNK_ENCRYPTED_A | HARDWARE_SNK_ENCRYPTED_B)) {
				nStep *= 4;
			}
			//BurnUpdateProgress(1.0 / nStep, i ? NULL : _T("Preprocessing graphics..."), 0);

		}

		// Pre-process the sprite graphics
		for (unsigned char* pTile = pStart; pTile < pEnd; pTile += 128) {
			unsigned int data[32];

			for (int y = 0; y < 16; y++) {
				unsigned int n = 0;
				for (int x = 0; x < 8; x++) {
					unsigned int m = ((pTile[67 + (y << 2)] >> x) & 1) << 3;
					m |= ((pTile[65 + (y << 2)] >> x) & 1) << 2;
					m |= ((pTile[66 + (y << 2)] >> x) & 1) << 1;
					m |= ((pTile[64 + (y << 2)] >> x) & 1) << 0;

					n |= m << (x << 2);
				}
				data[(y << 1) + 0] = n;

				n = 0;
				for (int x = 0; x < 8; x++) {
					unsigned int m = ((pTile[3 + (y << 2)] >> x) & 1) << 3;
					m |= ((pTile[1 + (y << 2)] >> x) & 1) << 2;
					m |= ((pTile[2 + (y << 2)] >> x) & 1) << 1;
					m |= ((pTile[0 + (y << 2)] >> x) & 1) << 0;

					n |= m << (x << 2);
				}
				data[(y << 1) + 1] = n;
			}
			for (int n = 0; n < 32; n++) {
				((unsigned int*)pTile)[n] = data[n];
			}
		}
		
		fseek(NeoSpriteFile, i * nRomBlockLen, SEEK_SET);
		fwrite(pDest, nRomBlockLen, 1, NeoSpriteFile);		
	}
   
	fclose(NeoSpriteFile);
    free(pDest);	
}

void NeoDecodeText(unsigned char* pDest, int nSize)
{
	// Pre-process the text layer graphics
	for (unsigned char* pTile = pDest; pTile < (pDest + 0x20000 + nSize); pTile += 32) {
		unsigned char data[32];
		for (int n = 0; n < 8; n++) {
			data[0 + n * 4] = pTile[16 + n];
			data[1 + n * 4] = pTile[24 + n];
			data[2 + n * 4] = pTile[ 0 + n];
			data[3 + n * 4] = pTile[ 8 + n];
		}

		for (int n = 0; n < 32; n++) {
			pTile[n] = data[n] << 4;
			pTile[n] |= data[n] >> 4;
		}
	}
}

int NeoLoadADPCM(int nOffset, int nNum, unsigned char* pDest)
{
	struct BurnRomInfo ri;
	ri.nLen = 0;
	BurnDrvGetRomInfo(&ri, nOffset);

	for (int i = 0; i < nNum; i++) {
		BurnLoadRom(pDest, nOffset + i, 1);
		pDest += ri.nLen;
	}

	return 0;
}

// This function fills the screen with the first palette entry
void NeoClearScreen()
{
	unsigned int nColour = NeoPalette[0x0FFF];

	if (nColour) {
		switch (nBurnBpp) {
			case 4: {
				unsigned int* pClear = (unsigned int*)pBurnDraw;
				for (int i = 0; i < nNeoScreenWidth * 224 / 8; i++) {
					*pClear++ = nColour;
					*pClear++ = nColour;
					*pClear++ = nColour;
					*pClear++ = nColour;
					*pClear++ = nColour;
					*pClear++ = nColour;
					*pClear++ = nColour;
					*pClear++ = nColour;
				}
				break;
			}

			case 3: {
				unsigned char* pClear = pBurnDraw;
				unsigned char r = nColour;
				unsigned char g = (r >> 8) & 0xFF;
				unsigned char b = (r >> 16) & 0xFF;
				r &= 0xFF;
				for (int i = 0; i < nNeoScreenWidth * 224; i++) {
					*pClear++ = r;
					*pClear++ = g;
					*pClear++ = b;
				}
				break;
			}

			case 2: {
				unsigned int* pClear = (unsigned int*)pBurnDraw;
				nColour |= nColour << 16;
				for (int i = 0; i < nNeoScreenWidth * 224 / 16; i++) {
					*pClear++ = nColour;
					*pClear++ = nColour;
					*pClear++ = nColour;
					*pClear++ = nColour;
					*pClear++ = nColour;
					*pClear++ = nColour;
					*pClear++ = nColour;
					*pClear++ = nColour;
				}
				break;
			}
		}
	} else {
		memset(pBurnDraw, 0, nNeoScreenWidth * 224 * nBurnBpp);
	}
}

