/*******************************************************************************
  Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
 
  (c) Copyright 1996 - 2002 Gary Henderson (gary.henderson@ntlworld.com) and
                            Jerremy Koot (jkoot@snes9x.com)

  (c) Copyright 2001 - 2004 John Weidman (jweidman@slip.net)

  (c) Copyright 2002 - 2004 Brad Jorsch (anomie@users.sourceforge.net),
                            funkyass (funkyass@spam.shaw.ca),
                            Joel Yliluoma (http://iki.fi/bisqwit/)
                            Kris Bleakley (codeviolation@hotmail.com),
                            Matthew Kendora,
                            Nach (n-a-c-h@users.sourceforge.net),
                            Peter Bortas (peter@bortas.org) and
                            zones (kasumitokoduck@yahoo.com)

  C4 x86 assembler and some C emulation code
  (c) Copyright 2000 - 2003 zsKnight (zsknight@zsnes.com),
                            _Demo_ (_demo_@zsnes.com), and Nach

  C4 C++ code
  (c) Copyright 2003 Brad Jorsch

  DSP-1 emulator code
  (c) Copyright 1998 - 2004 Ivar (ivar@snes9x.com), _Demo_, Gary Henderson,
                            John Weidman, neviksti (neviksti@hotmail.com),
                            Kris Bleakley, Andreas Naive

  DSP-2 emulator code
  (c) Copyright 2003 Kris Bleakley, John Weidman, neviksti, Matthew Kendora, and
                     Lord Nightmare (lord_nightmare@users.sourceforge.net

  OBC1 emulator code
  (c) Copyright 2001 - 2004 zsKnight, pagefault (pagefault@zsnes.com) and
                            Kris Bleakley
  Ported from x86 assembler to C by sanmaiwashi

  SPC7110 and RTC C++ emulator code
  (c) Copyright 2002 Matthew Kendora with research by
                     zsKnight, John Weidman, and Dark Force

  S-DD1 C emulator code
  (c) Copyright 2003 Brad Jorsch with research by
                     Andreas Naive and John Weidman
 
  S-RTC C emulator code
  (c) Copyright 2001 John Weidman
  
  ST010 C++ emulator code
  (c) Copyright 2003 Feather, Kris Bleakley, John Weidman and Matthew Kendora

  Super FX x86 assembler emulator code 
  (c) Copyright 1998 - 2003 zsKnight, _Demo_, and pagefault 

  Super FX C emulator code 
  (c) Copyright 1997 - 1999 Ivar, Gary Henderson and John Weidman


  SH assembler code partly based on x86 assembler code
  (c) Copyright 2002 - 2004 Marcus Comstedt (marcus@mc.pp.se) 

 
  Specific ports contains the works of other authors. See headers in
  individual files.
 
  Snes9x homepage: http://www.snes9x.com
 
  Permission to use, copy, modify and distribute Snes9x in both binary and
  source form, for non-commercial purposes, is hereby granted without fee,
  providing that this license information and copyright notice appear with
  all copies and any derived work.
 
  This software is provided 'as-is', without any express or implied
  warranty. In no event shall the authors be held liable for any damages
  arising from the use of this software.
 
  Snes9x is freeware for PERSONAL USE only. Commercial users should
  seek permission of the copyright holders first. Commercial use includes
  charging money for Snes9x or software derived from Snes9x.
 
  The copyright holders request that bug fixes and improvements to the code
  should be forwarded to them so everyone can benefit from the modifications
  in future versions.
 
  Super NES and Super Nintendo Entertainment System are trademarks of
  Nintendo Co., Limited and its subsidiary companies.
*******************************************************************************/

//#define SPC7110_DEBUG

#include "spc7110.h"
#include "memmap.h"
#include <time.h>
#include <sys/stat.h>

//Windows includes
#ifdef __WIN32__
#ifndef _XBOX				// chdir and getcwd not supported on Xbox hardware
#include <direct.h>
#define chdir _chdir
#define getcwd _getcwd
#endif
#define FREEZEFOLDER	GUI.FreezeFileDir
//zinx suggested this, for *nix compatibility
#define PATH_MAX	MAX_PATH
#else // Unix
#include "display.h"
#include <limits.h>
#include <unistd.h>
#define FREEZEFOLDER S9xGetSnapshotDirectory ()
#endif

extern "C" const char *S9xGetFilename (const char *);
extern "C" char *osd_GetPackDir();
//really not needed, but usually MS adds the _ to POSIX functions,
//while *nix doesn't, so this was to "un-M$" the function.
#define splitpath _splitpath

//not much headroom, but FEOEZ has 41 tables, I think, and SPL4 has 38.
#define MAX_TABLES 48

//default to using 5 megs of RAM for method 3 caching.
uint16 cacheMegs=5;

//using function pointers to initialize cache management
void (*CleanUp7110)(void)=NULL;
void (*LoadUp7110)(char*)=&SPC7110Load;
void (*Copy7110)(void)=NULL;

//size and offset of the pack data
//offset and size of reads from pack
typedef struct SPC7110DecompressionLocationStruct
{
	uint32 offset;
	uint32 size;
	uint16 used_offset;
	uint16 used_len;
} Data7110;

//this maps an index.bin table to the decompression pack
typedef struct SPC7110DecompressionIndexStruct
{
	int table;
	bool is_file;
	Data7110 location[256];
} Index7110;

//this contains all the data for the decompression pack.
typedef struct SPC7110DecompressionPackStructure
{
	uint8* binfiles[MAX_TABLES];
	Index7110 tableEnts[MAX_TABLES];
	int last_table;
	int idx;
	uint8 last_idx;
	uint16 last_offset;
} Pack7110;


char pfold[9];				//hack variable for log naming (each game makes a different log)
Pack7110* decompack=NULL;   //decompression pack uses a fair chunk of RAM, so dynalloc it.
SPC7110Regs s7r;			//SPC7110 registers, about 33KB
S7RTC rtc_f9;				//FEOEZ (and Shounen Jump no SHou) RTC
void	S9xUpdateRTC ();	//S-RTC function hacked to work with the RTC

//Emulate power on state
void S9xSpc7110Init()
{
	s7r.DataRomOffset=0x00100000;//handy constant!
	s7r.DataRomSize=Memory.CalculatedSize-s7r.DataRomOffset;
	s7r.reg4800=0;
	s7r.reg4801=0;
	s7r.reg4802=0;
	s7r.reg4803=0;
	s7r.reg4804=0;
	s7r.reg4805=0;
	s7r.reg4806=0;
	s7r.reg4807=0;
	s7r.reg4808=0;
	s7r.reg4809=0;
	s7r.reg480A=0;
	s7r.reg480B=0;
	s7r.reg480C=0;
	s7r.reg4811=0;
	s7r.reg4812=0;
	s7r.reg4813=0;
	s7r.reg4814=0;
	s7r.reg4815=0;
	s7r.reg4816=0;
	s7r.reg4817=0;
	s7r.reg4818=0;
	s7r.reg4820=0;
	s7r.reg4821=0;
	s7r.reg4822=0;
	s7r.reg4823=0;
	s7r.reg4824=0;
	s7r.reg4825=0;
	s7r.reg4826=0;
	s7r.reg4827=0;
	s7r.reg4828=0;
	s7r.reg4829=0;
	s7r.reg482A=0;
	s7r.reg482B=0;
	s7r.reg482C=0;
	s7r.reg482D=0;
	s7r.reg482E=0;
	s7r.reg482F=0;
	s7r.reg4830=0;
	s7r.reg4831=0;
	s7r.reg4832=1;
	s7r.reg4833=2;
	s7r.reg4834=0;
	s7r.reg4840=0;
	s7r.reg4841=0;
	s7r.reg4842=0;
	s7r.written=0;
	s7r.offset_add=0;
	s7r.AlignBy=1;

	(*LoadUp7110)(osd_GetPackDir());

	if(Settings.SPC7110RTC)
		Settings.TurboMode=false;

	s7r.bank50Internal=0;
	memset(s7r.bank50,0x00,DECOMP_BUFFER_SIZE);
}


//full cache decompression routine (memcpy) Method 1
void MovePackData()
{
	//log the last entry
	Data7110* log=&(decompack->tableEnts[decompack->idx].location[decompack->last_idx]);
	if((log->used_len+log->used_offset)<(decompack->last_offset+(unsigned short)s7r.bank50Internal))
	{
		log->used_len=s7r.bank50Internal;
		log->used_offset=decompack->last_offset;
	}
	
	//set up for next logging
	decompack->last_offset=(s7r.reg4805)|(s7r.reg4806<<8);
	
	decompack->last_idx=s7r.reg4804;

	//start decompression
	int table=(s7r.reg4803<<16)|(s7r.reg4802<<8)|s7r.reg4801;
	
	//the table is a offset multiplier byte and a big-endian pointer
	int j= 4*s7r.reg4804;
	j+=s7r.DataRomOffset;
	j+=table;
	
	//set proper offsetting.
	if(s7r.reg480B==0)
		s7r.AlignBy=0;
	else
	{
		switch(ROM[j])
		{
		case 0x03:
			s7r.AlignBy=8;
			break;
		case 0x01:
			s7r.AlignBy=2;
			break;
		case 0x02:
			s7r.AlignBy=4;
			break;
		case 0x00:
		default:
			s7r.AlignBy=1;
			break;
		}
	}
	//note that we are still setting up for the next log.
	decompack->last_offset*=s7r.AlignBy;
	decompack->last_offset%=DECOMP_BUFFER_SIZE;

	//find the table
	if(table!=decompack->last_table)
	{
		int i=0;
		while(i<MAX_TABLES&&decompack->tableEnts[i].table!=table)
			i++;
		if(i==MAX_TABLES)
		{
#ifdef _XBOX
			FILE* fp=fopen("T:\\sp7err.out","a");
#else
			FILE* fp=fopen("sp7err.out","a");
#endif

//			fprintf(fp, "Table Entry %06X:%02X not found\n", table, s7r.reg4804);
			fclose(fp);
			return;
		}
		decompack->idx=i;
		decompack->last_table=table;
	}

	//copy data
	if(decompack->binfiles[decompack->idx])
	{
		memcpy(s7r.bank50,
			&(decompack->binfiles[decompack->idx][decompack->tableEnts[decompack->idx].location[s7r.reg4804].offset]),
			decompack->tableEnts[decompack->idx].location[s7r.reg4804].size);
	}
}


//this is similar to the last function, but it keeps the last 5 accessed files open,
// and reads the data directly. Method 2
void ReadPackData()
{
	static int table_age_2;
	static int table_age_3;
	static int table_age_4;
	static int table_age_5;
	
	int table=(s7r.reg4803<<16)|(s7r.reg4802<<8)|s7r.reg4801;

	if(table==0)
	{
		table_age_2=table_age_3=table_age_4=table_age_5=MAX_TABLES;
		return;
	}

	if(table_age_2==0&&table_age_3==0&&table_age_4==0&&table_age_5==0)
		table_age_2=table_age_3=table_age_4=table_age_5=MAX_TABLES;
	Data7110* log=&(decompack->tableEnts[decompack->idx].location[decompack->last_idx]);
	if((log->used_len+log->used_offset)<(decompack->last_offset+(unsigned short)s7r.bank50Internal))
	{
		log->used_len=s7r.bank50Internal;
		log->used_offset=decompack->last_offset;
	}
	
	decompack->last_offset=(s7r.reg4805)|(s7r.reg4806<<8);
	
	decompack->last_idx=s7r.reg4804;
	
	int j= 4*s7r.reg4804;
	j+=s7r.DataRomOffset;
	j+=table;
	
	if(s7r.reg480B==0)
		s7r.AlignBy=0;
	else
	{
		switch(ROM[j])
		{
		case 0x03:
			s7r.AlignBy=8;
			break;
		case 0x01:
			s7r.AlignBy=2;
			break;
		case 0x02:
			s7r.AlignBy=4;
			break;
		case 0x00:
		default:
			s7r.AlignBy=1;
			break;
		}
	}
	decompack->last_offset*=s7r.AlignBy;
	decompack->last_offset%=DECOMP_BUFFER_SIZE;
	if(table!=decompack->last_table)
	{
		int i=0;
		while(i<MAX_TABLES&&decompack->tableEnts[i].table!=table)
			i++;
		if(i==MAX_TABLES)
		{
			FILE* fp=fopen("sp7err.out","a");
//			fprintf(fp, "Table Entry %06X:%02X not found\n", table, s7r.reg4804);
			fclose(fp);
			return;
		}
		if(i!= table_age_2 && i!= table_age_3 && i!= table_age_4 && i!= table_age_5)
		{
			if(table_age_5!=MAX_TABLES&&decompack->binfiles[table_age_5])
			{
				fclose((FILE*)(decompack->binfiles[table_age_5]));
				(decompack->binfiles[table_age_5])=NULL;
			}
			table_age_5=table_age_4;
			table_age_4=table_age_3;
			table_age_3=table_age_2;
			table_age_2=decompack->idx;
			char name[PATH_MAX];
			//open file
			char drive [_MAX_DRIVE + 1];
			char dir [_MAX_DIR + 1];
			char fname [_MAX_FNAME + 1];
			char ext [_MAX_EXT + 1];
			if (strlen (FREEZEFOLDER))
			{
				//splitpath (Memory.ROMFilename, drive, dir, fname, ext);
				strcpy (name, FREEZEFOLDER);
				strcat (name, "/");
			}
			else
			{
				splitpath (Memory.ROMFilename, drive, dir, fname, ext);
				strcpy(name, drive);
				//strcat(filename, "\\");
				strcat(name, dir);
			}
			strcat(name, pfold);
			char bfname[11];
			sprintf(bfname, "%06X.bin", table);
			strcat(name, "/");
			strcat(name, bfname);
			decompack->binfiles[i]=(uint8*)fopen(name, "rb");
		}
		else
		{
			//fix tables in this case
			if(table_age_5==i)
			{
				table_age_5=table_age_4;
				table_age_4=table_age_3;
				table_age_3=table_age_2;
				table_age_2=decompack->idx;
			}
			if(table_age_4==i)
			{
				table_age_4=table_age_3;
				table_age_3=table_age_2;
				table_age_2=decompack->idx;
			}
			if(table_age_3==i)
			{
				table_age_3=table_age_2;
				table_age_2=decompack->idx;
			}
			if(table_age_2==i)
			{
				table_age_2=decompack->idx;
			}
		}
		decompack->idx=i;
		decompack->last_table=table;
	}
	//do read here.
	if(decompack->binfiles[decompack->idx])
	{
		fseek((FILE*)(decompack->binfiles[decompack->idx]), decompack->tableEnts[decompack->idx].location[s7r.reg4804].offset, 0);
		fread(s7r.bank50,1, (decompack->tableEnts[decompack->idx].location[s7r.reg4804].size), (FILE*)(decompack->binfiles[decompack->idx]));
	}
}

//Cache Method 3: some entries are cached, others are file handles.
//use is_file to distinguish.
void GetPackData()
{
	Data7110* log=&(decompack->tableEnts[decompack->idx].location[decompack->last_idx]);
	if((log->used_len+log->used_offset)<(decompack->last_offset+(unsigned short)s7r.bank50Internal))
	{
		log->used_len=s7r.bank50Internal;
		log->used_offset=decompack->last_offset;
	}
	
	decompack->last_offset=(s7r.reg4805)|(s7r.reg4806<<8);
	
	decompack->last_idx=s7r.reg4804;
	int table=(s7r.reg4803<<16)|(s7r.reg4802<<8)|s7r.reg4801;
	
	int j= 4*s7r.reg4804;
	j+=s7r.DataRomOffset;
	j+=table;
	
	if(s7r.reg480B==0)
		s7r.AlignBy=0;
	else
	{
		switch(ROM[j])
		{
		case 0x03:
			s7r.AlignBy=8;
			break;
		case 0x01:
			s7r.AlignBy=2;
			break;
		case 0x02:
			s7r.AlignBy=4;
			break;
		case 0x00:
		default:
			s7r.AlignBy=1;
			break;
		}
	}
	decompack->last_offset*=s7r.AlignBy;
	decompack->last_offset%=DECOMP_BUFFER_SIZE;
	if(table!=decompack->last_table)
	{
		int i=0;
		while(i<MAX_TABLES&&decompack->tableEnts[i].table!=table)
			i++;
		if(i==MAX_TABLES)
		{
			FILE* fp=fopen("sp7err.out","a");
//			fprintf(fp, "Table Entry %06X:%02X not found\n", table, s7r.reg4804);
			fclose(fp);
			return;
		}
		decompack->idx=i;
		decompack->last_table=table;
	}
	if(decompack->binfiles[decompack->idx])
	{
		if(decompack->tableEnts[decompack->idx].is_file)
		{
			fseek((FILE*)decompack->binfiles[decompack->idx], decompack->tableEnts[decompack->idx].location[s7r.reg4804].offset, 0);
			fread(s7r.bank50,1, (decompack->tableEnts[decompack->idx].location[s7r.reg4804].size), (FILE*)(decompack->binfiles[decompack->idx]));
		}
		else
		{
			memcpy(s7r.bank50,
				&(decompack->binfiles[decompack->idx][decompack->tableEnts[decompack->idx].location[s7r.reg4804].offset]),
				decompack->tableEnts[decompack->idx].location[s7r.reg4804].size);
		}
	}
}

extern "C"{
//reads SPC7110 and RTC registers.
uint8 S9xGetSPC7110(uint16 Address)
{
#ifdef SPC7110_DEBUG
	printf("%04X read\n", Address);
#endif
	switch (Address)
	{
		//decompressed data read port. decrements 4809-A (with wrap)
		//4805-6 is the offset into the bank
		//AlignBy is set (afaik) at decompression time, and is the offset multiplier
		//bank50internal is an internal pointer to the actual byte to read.
		//so you read from offset*multiplier + bank50internal
		//the offset registers cannot be incremented due to the offset multiplier.
	case 0x4800:
		{
			unsigned short count=s7r.reg4809|(s7r.reg480A<<8);
			uint32 i, j;
			j=(s7r.reg4805|(s7r.reg4806<<8));
			j*=s7r.AlignBy;
			i=j;
			if(count >0)
				count--;
			else count = 0xFFFF;
			s7r.reg4809=0x00ff&count;
			s7r.reg480A=(0xff00&count)>>8;
			i+=s7r.bank50Internal;
			i%=DECOMP_BUFFER_SIZE;
			s7r.reg4800=s7r.bank50[i];
			
			s7r.bank50Internal++;
			s7r.bank50Internal%=DECOMP_BUFFER_SIZE;
#ifdef SPC7110_DEBUG
			printf("Returned %02X\n", s7r.reg4800);
#endif
		}
		return s7r.reg4800;
	//table register low
	case 0x4801: return s7r.reg4801;
	//table register middle
	case 0x4802: return s7r.reg4802;
	//table register high
	case 0x4803: return s7r.reg4803;
	//index of pointer in table (each entry is 4 bytes)
	case 0x4804: return s7r.reg4804;
	//offset register low
	case 0x4805: return s7r.reg4805;
	//offset register high
	case 0x4806: return s7r.reg4806;
	//DMA channel (not that I see this usually set,
	//regardless of what channel DMA is on)
	case 0x4807: return s7r.reg4807;
	//C r/w option, unknown, defval:00 is what Dark Force says
	//afaict, Snes9x doesn't use this at all.
	case 0x4808: return s7r.reg4808;
	//C-Length low
	//counts down the number of bytes left to read from the decompression buffer.
	//this is set by the ROM, and wraps on bounds.
	case 0x4809: return s7r.reg4809;
	//C Length high
	case 0x480A: return s7r.reg480A;
	//Offset enable.
	//if this is zero, 4805-6 are useless. Emulated by setting AlignBy to 0
	case 0x480B:
		return s7r.reg480B;
	//decompression finished: just emulated by switching each read.
	case 0x480C: 
		s7r.reg480C^=0x80;
		return s7r.reg480C^0x80;
	
	//Data access port
	//reads from the data ROM (anywhere over the first 8 mbits
	//behavior is complex, will document later,
	//possibly missing cases, because of the number of switches in play
	case 0x4810:
		if(s7r.written==0)
			return 0;
		if((s7r.written&0x07)==0x07)
		{
			uint32 i=(s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811;
			i%=s7r.DataRomSize;
			if(s7r.reg4818&0x02)
			{
				if(s7r.reg4818&0x08)
				{
					signed short r4814;
					r4814=(s7r.reg4815<<8)|s7r.reg4814;
					i+=r4814;
					r4814++;
					s7r.reg4815=(uint8)(r4814>>8);
					s7r.reg4814=(uint8)(r4814&0x00FF);
				}
				else
				{
					unsigned short r4814;
					r4814=(s7r.reg4815<<8)|s7r.reg4814;
					i+=r4814;
					if(r4814!=0xFFFF)
						r4814++;
					else r4814=0;
					s7r.reg4815=(uint8)(r4814>>8);
					s7r.reg4814=(uint8)(r4814&0x00FF);
					
				}
			}
			i+=s7r.DataRomOffset;
			uint8 tmp=ROM[i];
			i=(s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811;
			
			if(s7r.reg4818&0x02)
			{
			}
			else if(s7r.reg4818&0x01)
			{
				if(s7r.reg4818&0x04)
				{
					signed short inc;
					inc=(s7r.reg4817<<8)|s7r.reg4816;
					
					if(!(s7r.reg4818&0x10))
						i+=inc;
					else
					{
						if(s7r.reg4818&0x08)
						{
							signed short r4814;
							r4814=(s7r.reg4815<<8)|s7r.reg4814;
							r4814+=inc;
							s7r.reg4815=(r4814&0xFF00)>>8;
							s7r.reg4814=r4814&0xFF;
						}
						else
						{
							unsigned short r4814;
							r4814=(s7r.reg4815<<8)|s7r.reg4814;
							r4814+=inc;
							s7r.reg4815=(r4814&0xFF00)>>8;
							s7r.reg4814=r4814&0xFF;
							
						}
					}
					//is signed
				}
				else
				{
					uint16 inc;
					inc=(s7r.reg4817<<8)|s7r.reg4816;
					if(!(s7r.reg4818&0x10))
						i+=inc;
					else
					{
						if(s7r.reg4818&0x08)
						{
							signed short r4814;
							r4814=(s7r.reg4815<<8)|s7r.reg4814;
							r4814+=inc;
							s7r.reg4815=(r4814&0xFF00)>>8;
							s7r.reg4814=r4814&0xFF;
						}
						else
						{
							unsigned short r4814;
							r4814=(s7r.reg4815<<8)|s7r.reg4814;
							r4814+=inc;
							s7r.reg4815=(r4814&0xFF00)>>8;
							s7r.reg4814=r4814&0xFF;
							
						}
					}
				}
			}
			else
			{
				if(!(s7r.reg4818&0x10))
					i+=1;
				else
				{
					if(s7r.reg4818&0x08)
					{
						signed short r4814;
						r4814=(s7r.reg4815<<8)|s7r.reg4814;
						r4814+=1;
						s7r.reg4815=(r4814&0xFF00)>>8;
						s7r.reg4814=r4814&0xFF;
					}
					else
					{
						unsigned short r4814;
						r4814=(s7r.reg4815<<8)|s7r.reg4814;
						r4814+=1;
						s7r.reg4815=(r4814&0xFF00)>>8;
						s7r.reg4814=r4814&0xFF;
						
					}
				}
			}
			
#ifdef SPC7110_DEBUG
			printf("Returned %02X\n", tmp);
#endif
			
			i%=s7r.DataRomSize;
			s7r.reg4811=i&0x00FF;
			s7r.reg4812=(i&0x00FF00)>>8;
			s7r.reg4813=((i&0xFF0000)>>16);
			return tmp;
		}
		else return 0;
	//direct read address low
	case 0x4811: return s7r.reg4811;
	//direct read address middle
	case 0x4812: return s7r.reg4812;
	//direct read access high
	case 0x4813: return s7r.reg4813;
	//read adjust low
	case 0x4814: return s7r.reg4814;
	//read adjust high
	case 0x4815: return s7r.reg4815;
	//read increment low
	case 0x4816: return s7r.reg4816;
	//read increment high
	case 0x4817: return s7r.reg4817;
	//Data ROM command mode
	//essentially, this controls the insane code of $4810 and $481A
	case 0x4818: return s7r.reg4818;
	//read after adjust port
	//what this does, besides more nasty stuff like 4810,
	//I don't know. Just assume it is a different implementation of $4810,
	//if it helps your sanity
	case 0x481A:
		if(s7r.written==0x1F)
		{
			uint32 i=((s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811);
			if(s7r.reg4818&0x08)
			{
				short adj;
				adj=((short)(s7r.reg4815<<8))|s7r.reg4814;
				i+=adj;
			}
			else
			{
				uint16 adj;
				adj=(s7r.reg4815<<8)|s7r.reg4814;
				i+=adj;
			}
			
			i%=s7r.DataRomSize;
			i+=s7r.DataRomOffset;
			uint8 tmp=ROM[i];
			i=((s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811);
			if(0x60==(s7r.reg4818&0x60))
			{
				i=((s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811);
				
				if(!(s7r.reg4818&0x10))
				{
					if(s7r.reg4818&0x08)
					{
						short adj;
						adj=((short)(s7r.reg4815<<8))|s7r.reg4814;
						i+=adj;
					}
					else
					{
						uint16 adj;
						adj=(s7r.reg4815<<8)|s7r.reg4814;
						i+=adj;
					}
					i%=s7r.DataRomSize;
					s7r.reg4811=i&0x00FF;
					s7r.reg4812=(i&0x00FF00)>>8;
					s7r.reg4813=((i&0xFF0000)>>16);
				}
				else
				{
					if(s7r.reg4818&0x08)
					{
						short adj;
						adj=((short)(s7r.reg4815<<8))|s7r.reg4814;
						adj+=adj;
						s7r.reg4815=(adj&0xFF00)>>8;
						s7r.reg4814=adj&0xFF;
					}
					else
					{
						uint16 adj;
						adj=(s7r.reg4815<<8)|s7r.reg4814;
						adj+=adj;
						s7r.reg4815=(adj&0xFF00)>>8;
						s7r.reg4814=adj&0xFF;
					}
				}
			}
#ifdef SPC7110_DEBUG
			printf("Returned %02X\n", tmp);
#endif
			return tmp;
		}
		else return 0;
	
	//multiplicand low or dividend lowest
	case 0x4820: return s7r.reg4820;
	//multiplicand high or divdend lower
	case 0x4821: return s7r.reg4821;
	//dividend higher
	case 0x4822: return s7r.reg4822;
	//dividend highest
	case 0x4823: return s7r.reg4823;
	//multiplier low
	case 0x4824: return s7r.reg4824;
	//multiplier high
	case 0x4825: return s7r.reg4825;
	//divisor low
	case 0x4826: return s7r.reg4826;
	//divisor high
	case 0x4827: return s7r.reg4827;
	
	//result lowest
	case 0x4828:
		return s7r.reg4828;
	//result lower
	case 0x4829:
		return s7r.reg4829;
	//result higher
	case 0x482A:
		return s7r.reg482A;
	//result highest
	case 0x482B:
		return s7r.reg482B;
	//remainder (division) low
	case 0x482C: return s7r.reg482C;
	//remainder (division) high
	case 0x482D: return s7r.reg482D;
	//signed/unsigned
	case 0x482E: return s7r.reg482E;
	//finished flag, emulated as an on-read toggle.
	case 0x482F:
		if(s7r.reg482F)
		{
			s7r.reg482F=0;
			return 0x80;
		}
		return 0;
		break;

	//SRAM toggle
	case 0x4830:
		return s7r.reg4830;
	//DX bank mapping
	case 0x4831:
		return s7r.reg4831;
	//EX bank mapping
	case 0x4832:
		return s7r.reg4832;
	//FX bank mapping
	case 0x4833:
		return s7r.reg4833;
	//SRAM mapping? We have no clue!
	case 0x4834:
		return s7r.reg4834;
//RTC enable
	case 0x4840:
		if(!Settings.SPC7110RTC)
			return Address>>8;
		return s7r.reg4840;
//command/index/value of RTC (essentially, zero unless we're in read mode
	case 0x4841:
		if(!Settings.SPC7110RTC)
			return Address>>8;
		if(rtc_f9.init)
		{
			S9xUpdateRTC();
			uint8 tmp=rtc_f9.reg[rtc_f9.index];
			rtc_f9.index++;
			rtc_f9.index%=0x10;
#ifdef SPC7110_DEBUG
			printf("$4841 returned %02X\n", tmp);
#endif
			return tmp;
		}
		else return 0;
//RTC done flag
	case 0x4842:
		if(!Settings.SPC7110RTC)
			return Address>>8;
		s7r.reg4842^=0x80;
		return s7r.reg4842^0x80;
	default:
#ifdef SPC7110_DEBUG
		printf("Access to Reg %04X\n", Address);
#endif
		return 0x00;
	}
}
}
void S9xSetSPC7110 (uint8 data, uint16 Address)
{
#ifdef SPC7110_DEBUG
	printf("%04X written to, value %02X\n", Address, data);
#endif
	switch(Address)
	{
//Writes to $4800 are undefined.

	//table low, middle, and high.
	case 0x4801:
		s7r.reg4801=data;
		break;
	case 0x4802:
		s7r.reg4802=data;
		break;
	case 0x4803:
		s7r.reg4803=data;
		break;

	//table index (4 byte entries, bigendian with a multiplier byte)
	case 0x4804:
		s7r.reg4804=data;
		break;

	//offset low
	case 0x4805:
		s7r.reg4805=data;
		break;

	//offset high, starts decompression
	case 0x4806:
		s7r.reg4806=data;
		(*Copy7110)();
		s7r.bank50Internal=0;
		s7r.reg480C&=0x7F;
		break;

	//DMA channel register (Is it used??)
	case 0x4807:
		s7r.reg4807=data;
		break;

	//C r/w? I have no idea. If you get weird values written here before a bug,
	//The Dumper should probably be contacted about running a test.
	case 0x4808:
		s7r.reg4808=data;
		break;

	//C-Length low
	case 0x4809:
		s7r.reg4809=data;
		break;
	//C-Length high
	case 0x480A:
		s7r.reg480A=data;
		break;

	//Offset enable
	case 0x480B:
		{
			s7r.reg480B=data;
			int table=(s7r.reg4803<<16)|(s7r.reg4802<<8)|s7r.reg4801;
			
			int j= 4*s7r.reg4804;
			j+=s7r.DataRomOffset;
			j+=table;
			
			if(s7r.reg480B==0)
				s7r.AlignBy=0;
			else
			{
				switch(ROM[j])
				{
				case 0x03:
					s7r.AlignBy=8;
					break;
				case 0x01:
					s7r.AlignBy=2;
					break;
				case 0x02:
					s7r.AlignBy=4;
					break;
				case 0x00:
				default:
					s7r.AlignBy=1;
					break;
				}
			}
//			s7r.decomp_set=true;
		}
		break;
//$4810 is probably read only.

	//Data port address low
	case 0x4811:
		s7r.reg4811=data;
		s7r.written|=0x01;
		break;

	//data port address middle
	case 0x4812:
		s7r.reg4812=data;
		s7r.written|=0x02;
		break;

	//data port address high
	case 0x4813:
		s7r.reg4813=data;
		s7r.written|=0x04;
		break;

	//data port adjust low (has a funky immediate increment mode)
	case 0x4814:
		s7r.reg4814=data;
		if(s7r.reg4818&0x02)
		{
			if((s7r.reg4818&0x20)&&!(s7r.reg4818&0x40))
			{
				s7r.offset_add|=0x01;
				if(s7r.offset_add==3)
				{
					if(s7r.reg4818&0x10)
					{
					}
					else
					{
						uint32 i=(s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811;
						if(s7r.reg4818&0x08)
						{
							i+=(signed char)s7r.reg4814;
						}
						else
						{
							i+=s7r.reg4814;
						}
						i%=s7r.DataRomSize;
						s7r.reg4811=i&0x00FF;
						s7r.reg4812=(i&0x00FF00)>>8;
						s7r.reg4813=((i&0xFF0000)>>16);
					}
				}
			}
			else if((s7r.reg4818&0x40)&&!(s7r.reg4818&0x20))
			{
				s7r.offset_add|=0x01;
				if(s7r.offset_add==3)
				{
					if(s7r.reg4818&0x10)
					{
					}
					else
					{
						uint32 i=(s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811;
						if(s7r.reg4818&0x08)
						{
							short adj;
							adj=((short)(s7r.reg4815<<8))|s7r.reg4814;
							i+=adj;
						}
						else
						{
							uint16 adj;
							adj=(s7r.reg4815<<8)|s7r.reg4814;
							i+=adj;
						}
						i%=s7r.DataRomSize;
						s7r.reg4811=i&0x00FF;
						s7r.reg4812=(i&0x00FF00)>>8;
						s7r.reg4813=((i&0xFF0000)>>16);
					}
				}
				
			}
		}
		
		s7r.written|=0x08;
		break;
		
	//data port adjust high (has a funky immediate increment mode)
	case 0x4815:
		s7r.reg4815=data;
		if(s7r.reg4818&0x02)
		{
			if(s7r.reg4818&0x20&&!(s7r.reg4818&0x40))
			{
				s7r.offset_add|=0x02;
				if(s7r.offset_add==3)
				{
					if(s7r.reg4818&0x10)
					{
					}
					else
					{
						uint32 i=(s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811;
						
						if(s7r.reg4818&0x08)
						{
							i+=(signed char)s7r.reg4814;
						}
						else
						{
							i+=s7r.reg4814;
						}
						i%=s7r.DataRomSize;
						s7r.reg4811=i&0x00FF;
						s7r.reg4812=(i&0x00FF00)>>8;
						s7r.reg4813=((i&0xFF0000)>>16);
					}
				}
			}
			else if(s7r.reg4818&0x40&&!(s7r.reg4818&0x20))
			{
				s7r.offset_add|=0x02;
				if(s7r.offset_add==3)
				{
					if(s7r.reg4818&0x10)
					{
					}
					else
					{
						uint32 i=(s7r.reg4813<<16)|(s7r.reg4812<<8)|s7r.reg4811;
						if(s7r.reg4818&0x08)
						{
							short adj;
							adj=((short)(s7r.reg4815<<8))|s7r.reg4814;
							i+=adj;
						}
						else
						{
							uint16 adj;
							adj=(s7r.reg4815<<8)|s7r.reg4814;
							i+=adj;
						}
						i%=s7r.DataRomSize;
						s7r.reg4811=i&0x00FF;
						s7r.reg4812=(i&0x00FF00)>>8;
						s7r.reg4813=((i&0xFF0000)>>16);
					}
				}
			}
		}
		s7r.written|=0x10;
		break;
	//data port increment low
	case 0x4816:
		s7r.reg4816=data;
		break;
	//data port increment high
	case 0x4817:
		s7r.reg4817=data;
		break;

	//data port mode switches
	//note that it starts inactive.
	case 0x4818:
		if((s7r.written&0x18)!=0x18)
			break;
		s7r.offset_add=0;
		s7r.reg4818=data;
		break;

	//multiplicand low or dividend lowest
	case 0x4820:
		s7r.reg4820=data;
		break;
	//multiplicand high or dividend lower
	case 0x4821:
		s7r.reg4821=data;
		break;
	//dividend higher
	case 0x4822:
		s7r.reg4822=data;
		break;
	//dividend highest
	case 0x4823:
		s7r.reg4823=data;
		break;
	//multiplier low
	case 0x4824:
		s7r.reg4824=data;
		break;
	//multiplier high (triggers operation)
	case 0x4825:
		s7r.reg4825=data;
		if(s7r.reg482E&0x01)
		{
			int mul;
			short m1=(short)((s7r.reg4824)|(s7r.reg4825<<8));
			short m2=(short)((s7r.reg4820)|(s7r.reg4821<<8));
			
			mul=m1*m2;
			s7r.reg4828=(uint8)(mul&0x000000FF);
			s7r.reg4829=(uint8)((mul&0x0000FF00)>>8);
			s7r.reg482A=(uint8)((mul&0x00FF0000)>>16);
			s7r.reg482B=(uint8)((mul&0xFF000000)>>24);
		}
		else
		{
			uint32 mul;
			uint16 m1=(uint16)((s7r.reg4824)|(s7r.reg4825<<8));
			uint16 m2=(uint16)((s7r.reg4820)|(s7r.reg4821<<8));
			
			mul=m1*m2;
			s7r.reg4828=(uint8)(mul&0x000000FF);
			s7r.reg4829=(uint8)((mul&0x0000FF00)>>8);
			s7r.reg482A=(uint8)((mul&0x00FF0000)>>16);
			s7r.reg482B=(uint8)((mul&0xFF000000)>>24);
		}
		s7r.reg482F=0x80;
		break;
	//divisor low
	case 0x4826:
		s7r.reg4826=data;
		break;
	//divisor high (triggers operation)
	case 0x4827:
		s7r.reg4827=data;
		if(s7r.reg482E&0x01)
		{
			int quotient;
			short remainder;
			int dividend=(int)(s7r.reg4820|(s7r.reg4821<<8)|(s7r.reg4822<<16)|(s7r.reg4823<<24));
			short divisor=(short)(s7r.reg4826|(s7r.reg4827<<8));
			if(divisor != 0)
			{
				quotient=(int)(dividend/divisor);
				remainder=(short)(dividend%divisor);
			}
			else
			{
				quotient=0;
				remainder=dividend&0x0000FFFF;
			}
			s7r.reg4828=(uint8)(quotient&0x000000FF);
			s7r.reg4829=(uint8)((quotient&0x0000FF00)>>8);
			s7r.reg482A=(uint8)((quotient&0x00FF0000)>>16);
			s7r.reg482B=(uint8)((quotient&0xFF000000)>>24);
			s7r.reg482C=(uint8)remainder&0x00FF;
			s7r.reg482D=(uint8)((remainder&0xFF00)>>8);
		}
		else
		{
			uint32 quotient;
			uint16 remainder;
			uint32 dividend=(uint32)(s7r.reg4820|(s7r.reg4821<<8)|(s7r.reg4822<<16)|(s7r.reg4823<<24));
			uint16 divisor=(uint16)(s7r.reg4826|(s7r.reg4827<<8));
			if(divisor != 0)
			{
				quotient=(uint32)(dividend/divisor);
				remainder=(uint16)(dividend%divisor);
			}
			else
			{
				quotient=0;
				remainder=dividend&0x0000FFFF;
			}
			s7r.reg4828=(uint8)(quotient&0x000000FF);
			s7r.reg4829=(uint8)((quotient&0x0000FF00)>>8);
			s7r.reg482A=(uint8)((quotient&0x00FF0000)>>16);
			s7r.reg482B=(uint8)((quotient&0xFF000000)>>24);
			s7r.reg482C=(uint8)remainder&0x00FF;
			s7r.reg482D=(uint8)((remainder&0xFF00)>>8);
		}
		s7r.reg482F=0x80;
		break;
	//result registers are possibly read-only

	//reset: writes here nuke the whole math unit
	//Zero indicates unsigned math, resets with non-zero values turn on signed math
	case 0x482E:
		s7r.reg4820=s7r.reg4821=s7r.reg4822=s7r.reg4823=s7r.reg4824=s7r.reg4825=s7r.reg4826=s7r.reg4827=s7r.reg4828=s7r.reg4829=s7r.reg482A=s7r.reg482B=s7r.reg482C=s7r.reg482D=0;
		s7r.reg482E=data;
		break;

	//math status register possibly read only

	//SRAM toggle
	case 0x4830:
		Memory.SPC7110Sram(data);
		s7r.reg4830=data;
		break;
	//Bank DX mapping
	case 0x4831:
		s7r.reg4831=data;
		break;
	//Bank EX mapping
	case 0x4832:
		s7r.reg4832=data;
		break;
	//Bank FX mapping
	case 0x4833:
		s7r.reg4833=data;
		break;
	//S-RAM mapping? who knows?
	case 0x4834:
		s7r.reg4834=data;
		break;
	//RTC Toggle
	case 0x4840:
		if(0==data)
		{
			S9xUpdateRTC();
			//	rtc_f9.init=false;
			//	rtc_f9.index=-1;
		}
		if(data&0x01)
		{
			s7r.reg4842=0x80;
			//rtc_f9.last_used=time(NULL);//????
			rtc_f9.init=false;
			rtc_f9.index=-1;
		}
		s7r.reg4840=data;
		break;
	//RTC init/command/index register
	case 0x4841:
		if(rtc_f9.init)
		{
			if(-1==rtc_f9.index)
			{
				rtc_f9.index=data&0x0F;
				break;
			}
			if(rtc_f9.control==0x0C)
			{
				rtc_f9.index=data&0x0F;
				s7r.reg4842=0x80;
				rtc_f9.last_used=time(NULL);
			}
			else
			{
				
				if(0x0D==rtc_f9.index)
				{
					if(data&0x08)
					{
						if(rtc_f9.reg[1]<3)
						{
							S9xUpdateRTC();
							rtc_f9.reg[0]=0;
							rtc_f9.reg[1]=0;
							rtc_f9.last_used=time(NULL);
						}
						else
						{
							S9xUpdateRTC();
							rtc_f9.reg[0]=0;
							rtc_f9.reg[1]=0;
							rtc_f9.last_used=time(NULL)-60;
							S9xUpdateRTC();
							rtc_f9.last_used=time(NULL);
						}
						data&=0x07;
					}
					if(rtc_f9.reg[0x0D]&0x01)
					{
						if(!(data%2))
						{
							rtc_f9.reg[rtc_f9.index&0x0F]=data;
							rtc_f9.last_used=time(NULL)-1;
							S9xUpdateRTC();
							rtc_f9.last_used=time(NULL);
						}
					}
				}
				if(0x0F==rtc_f9.index)
				{
					if(data&0x01&&!(rtc_f9.reg[0x0F]&0x01))
					{
						S9xUpdateRTC();
						rtc_f9.reg[0]=0;
						rtc_f9.reg[1]=0;
						rtc_f9.last_used=time(NULL);
					}
					if(data&0x02&&!(rtc_f9.reg[0x0F]&0x02))
					{
						S9xUpdateRTC();
						rtc_f9.last_used=time(NULL);
					}
				}
				rtc_f9.reg[rtc_f9.index&0x0F]=data;
				s7r.reg4842=0x80;
				rtc_f9.index=(rtc_f9.index+1)%0x10;
			}
		}
		else
		{
			if(data==0x03||data==0x0C)
			{
				rtc_f9.init=true;
				rtc_f9.control=data;
				rtc_f9.index=-1;
			}
		}
		break;
	//writes to RTC status register aren't expected to be meaningful
	default:
		Address-=0x4800;
		break;
		//16 BIT MULTIPLIER: ($FF00) high byte, defval:00
	}
}
extern "C"{
//emulate the SPC7110's ability to remap banks Dx, Ex, and Fx.
uint8 S9xGetSPC7110Byte(uint32 Address)
{
	uint32 i;
	switch((Address&0x00F00000)>>16)
	{
	case 0xD0:
		i=s7r.reg4831*0x00100000;
		break;
	case 0xE0:
		i=s7r.reg4832*0x00100000;
		break;
	case 0xF0:
		i=s7r.reg4833*0x00100000;
		break;
	default:i=0;
	}
	i+=Address&0x000FFFFF;
	i+=s7r.DataRomOffset;
	return ROM[i];
}
}
/**********************************************************************************************/
/* S9xSRTCDaysInMonth()                                                                       */
/* Return the number of days in a specific month for a certain year                           */
/* copied directly for RTC functionality, separated in case of incompatibilities			  */
/**********************************************************************************************/
int	S9xRTCDaysInMonth( int month, int year )
{
    int		mdays;
	
    switch ( month )
    {
	case 2:
		if ( ( year % 4 == 0 ) )    // DKJM2 only uses 199x - 22xx
			mdays = 29;
		else
			mdays = 28;
		break;
		
	case 4:
	case 6:
	case 9:
	case 11:
		mdays = 30;
		break;
		
	default:	// months 1,3,5,7,8,10,12
		mdays = 31;
		break;
    }
	
    return mdays;
}


#define DAYTICKS (60*60*24)
#define HOURTICKS (60*60)
#define MINUTETICKS 60


/**********************************************************************************************/
/* S9xUpdateRTC()									                                          */
/* Advance the RTC time							                                              */
/**********************************************************************************************/

void	S9xUpdateRTC ()
{
	time_t	cur_systime;
	long    time_diff;
	
    // Keep track of game time by computing the number of seconds that pass on the system
    // clock and adding the same number of seconds to the RTC clock structure.
	
    if (rtc_f9.init && 0==(rtc_f9.reg[0x0D]&0x01) && 0==(rtc_f9.reg[0x0F]&0x03))
    {
        cur_systime = time (NULL);
		
        // This method assumes one time_t clock tick is one second
        //        which should work on PCs and GNU systems.
        //        If your tick interval is different adjust the
		//        DAYTICK, HOURTICK, and MINUTETICK defines
		
        time_diff = (long) (cur_systime - rtc_f9.last_used);
		rtc_f9.last_used = cur_systime;
        
        if ( time_diff > 0 )
        {
			int		seconds;
			int		minutes;
			int		hours;
			int		days;
			int		month;
			int		year;
			int		temp_days;
			
			int		year_hundreds;
			int		year_tens;
			int		year_ones;
			
			
			if ( time_diff > DAYTICKS )
			{
				days = time_diff / DAYTICKS;
				time_diff = time_diff - days * DAYTICKS;
			}
			else
			{
				days = 0;
			}
			
			if ( time_diff > HOURTICKS )
			{
				hours = time_diff / HOURTICKS;
				time_diff = time_diff - hours * HOURTICKS;
			}
			else
			{
				hours = 0;
			}
			
			if ( time_diff > MINUTETICKS )
			{
				minutes = time_diff / MINUTETICKS;
				time_diff = time_diff - minutes * MINUTETICKS;
			}
			else
			{
				minutes = 0;
			}
			
			if ( time_diff > 0 )
			{
				seconds = time_diff;
			}
			else
			{
				seconds = 0;
			}
			
			
			seconds += (rtc_f9.reg[1]*10 + rtc_f9.reg[0]);
			if ( seconds >= 60 )
			{
				seconds -= 60;
				minutes += 1;
			}
			
			minutes += (rtc_f9.reg[3]*10 + rtc_f9.reg[2]);
			if ( minutes >= 60 )
			{
				minutes -= 60;
				hours += 1;
			}
			
			hours += (rtc_f9.reg[5]*10 + rtc_f9.reg[4]);
			if ( hours >= 24 )
			{
				hours -= 24;
				days += 1;
			}
			
			year =  rtc_f9.reg[11]*10 + rtc_f9.reg[10];
			year += ( 1900 );
			month = rtc_f9.reg[8]+10*rtc_f9.reg[9];
			rtc_f9.reg[12]+=days;
			days += (rtc_f9.reg[7]*10 + rtc_f9.reg[6]);
			if ( days > 0 )
			{
				while ( days > (temp_days = S9xRTCDaysInMonth( month, year )) )
				{
					days -= temp_days;
					month += 1;
					if ( month > 12 )
					{
						year += 1;
						month = 1;
					}
				}
			}

			year_tens = year % 100;
			year_ones = year_tens % 10;
			year_tens /= 10;
			year_hundreds = (year - 1000) / 100;
			
			rtc_f9.reg[0] = seconds % 10;
			rtc_f9.reg[1] = seconds / 10;
			rtc_f9.reg[2] = minutes % 10;
			rtc_f9.reg[3] = minutes / 10;
			rtc_f9.reg[4] = hours % 10;
			rtc_f9.reg[5] = hours / 10;
			rtc_f9.reg[6] = days % 10;
			rtc_f9.reg[7] = days / 10;
			rtc_f9.reg[8] = month%10;
			rtc_f9.reg[9] = month /10;
			rtc_f9.reg[10] = year_ones;
			rtc_f9.reg[11] = year_tens;
			rtc_f9.reg[12] %= 7;
			return;
        }
    }
}
extern "C"{

//allows DMA from the ROM (is this even possible on the SPC7110?
uint8* Get7110BasePtr(uint32 Address)
{
	uint32 i;
	switch((Address&0x00F00000)>>16)
	{
	case 0xD0:
		i=s7r.reg4831*0x100000;
		break;
	case 0xE0:
		i=s7r.reg4832*0x100000;
		break;
	case 0xF0:
		i=s7r.reg4833*0x100000;
		break;
	default:i=0;
	}
	i+=Address&0x000F0000;
	return &ROM[i];
}
//end extern
}

//loads the index into memory.
//index.bin is little-endian
//format index (1)-table(3)-file offset(4)-length(4)
bool Load7110Index(char* filename)
{
	FILE* fp;
	uint8 buffer[12];
	int table=0;
	uint8 index=0;
	uint32 offset=0;
	uint32 size=0;
	int i=0;
	fp=fopen(filename, "rb");
	if(NULL==fp)
		return false;

    int f_len;
	//do
    while(1)
	{
		i=0;
		f_len= fread(buffer, 1, 12,fp);
        if(f_len < 12) break;

		table=(buffer[3]<<16)|(buffer[2]<<8)|buffer[1];
		index=buffer[0];
		offset=(buffer[7]<<24)|(buffer[6]<<16)|(buffer[5]<<8)|buffer[4];
		size=(buffer[11]<<24)|(buffer[10]<<16)|(buffer[9]<<8)|buffer[8];
		while(i<MAX_TABLES&&decompack->tableEnts[i].table!=table&&decompack->tableEnts[i].table!=0)
			i++;
		if(i==MAX_TABLES)
			return false;
		//added
		decompack->tableEnts[i].table=table;
		//-----
		decompack->tableEnts[i].location[index].offset=offset;
		decompack->tableEnts[i].location[index].size=size;
		decompack->tableEnts[i].location[index].used_len=0;
		decompack->tableEnts[i].location[index].used_offset=0;
		
	}
	//while(!feof(fp));
	fclose(fp);
	return true;
}


//Cache 1 load function
void SPC7110Load(char* dirname)
{
	char temp_path[PATH_MAX];
	int i=0;

	decompack=new Pack7110;

#ifndef _XBOX
	getcwd(temp_path,PATH_MAX);
#endif

	ZeroMemory(decompack, sizeof(Pack7110));
	
#ifndef _XBOX
		if(-1==chdir(dirname))
		{
			S9xMessage(0,0,"Graphics Pack not found!");
		}
#endif

#ifndef _XBOX
	Load7110Index("index.bin");
#else
	// D:\\ is always app.path in Xbox
	Load7110Index("d:\\index.bin");
#endif

	for(i=0;i<MAX_TABLES;i++)
	{
		if(decompack->tableEnts[i].table!=0)
		{
			char binname[PATH_MAX];
#ifndef _XBOX
			sprintf(binname,"%06X.bin",decompack->tableEnts[i].table);
#else
			sprintf(binname,"%s%06X.bin",filename,decompack->tableEnts[i].table);
#endif
			struct stat buf;
			if(-1!=stat(binname, &buf))
				decompack->binfiles[i]=new uint8[buf.st_size];
			FILE* fp=fopen(binname, "rb");
			if(fp)
			{
				fread(decompack->binfiles[i],buf.st_size,1,fp);
				fclose(fp);
			}
		}
	}

#ifndef _XBOX	
	chdir(temp_path);
#endif

	Copy7110=&MovePackData;
	CleanUp7110=&Del7110Gfx;
#ifdef __WIN32__
	#ifndef _XBOX
		EnableMenuItem(GUI.hMenu, IDM_LOG_7110, MF_ENABLED);
	#endif
#endif
}

//Cache 2 load function
void SPC7110Open(char* dirname)
{
	char temp_path[PATH_MAX];
	int i=0;

	decompack=new Pack7110;

#ifndef _XBOX
	getcwd(temp_path,PATH_MAX);
#endif

	ZeroMemory(decompack, sizeof(Pack7110));
	
#ifndef _XBOX
		if(-1==chdir(dirname))
		{
						S9xMessage(0,0,"Graphics Pack not found!");
		}
#endif

#ifndef _XBOX
	Load7110Index("index.bin");
#else
		// D:\\ is always app.path in Xbox
	Load7110Index("d:\\index.bin");
#endif

	for (i=0; i<MAX_TABLES; i++)
		decompack->binfiles[i]=NULL;

	ReadPackData();

#ifndef _XBOX
	chdir(temp_path);
#endif

	Copy7110=&ReadPackData;
	CleanUp7110=&Close7110Gfx;

#ifdef __WIN32__
	#ifndef _XBOX
		EnableMenuItem(GUI.hMenu, IDM_LOG_7110, MF_ENABLED);
	#endif
#endif
}

//Cache 3's load function
void SPC7110Grab(char* dirname)
{
	char temp_path[PATH_MAX];
	int i=0;

	decompack=new Pack7110;

#ifndef _XBOX
	getcwd(temp_path,PATH_MAX);
#endif

	int32 buffer_size=1024*1024*cacheMegs;//*some setting

	ZeroMemory(decompack, sizeof(Pack7110));
#ifndef _XBOX
  
		if(-1==chdir(dirname))
		{
			S9xMessage(0,0,"Graphics Pack not found!");
		}
#endif

#ifndef _XBOX
	Load7110Index("index.bin");
#else
	// D:\\ is always app.path in Xbox
	Load7110Index("d:\\index.bin");
#endif

	for(i=0;i<MAX_TABLES;i++)
	{
		if(decompack->tableEnts[i].table!=0)
		{
			char binname[PATH_MAX];
#ifndef _XBOX
			sprintf(binname,"%06X.bin",decompack->tableEnts[i].table);
#else
			sprintf(binname,"%s%06X.bin",filename,decompack->tableEnts[i].table);
#endif
			struct stat buf;
//add load/no load calculations here
			if(-1!=stat(binname, &buf))
			{
				if(buf.st_size<buffer_size)
					decompack->binfiles[i]=new uint8[buf.st_size];
				FILE* fp=fopen(binname, "rb");
				//use them here
				if(fp)
				{
					if(buf.st_size<buffer_size)
					{
						fread(decompack->binfiles[i],buf.st_size,1,fp);
						fclose(fp);
						buffer_size-=buf.st_size;
						decompack->tableEnts[i].is_file=false;
					}
					else
					{
						decompack->binfiles[i]=(uint8*)fp;
						decompack->tableEnts[i].is_file=true;
					}
				}
			}
		}
	}

#ifndef _XBOX
	chdir(temp_path);
#endif

	Copy7110=&GetPackData;
	CleanUp7110=&Drop7110Gfx;


#ifdef __WIN32__
	#ifndef _XBOX
		EnableMenuItem(GUI.hMenu, IDM_LOG_7110, MF_ENABLED);
	#endif
#endif
}

//Cache 1 clean up function
void Del7110Gfx()
{
	int i;
	if(Settings.SPC7110)
	{
#ifdef __WIN32__
	#ifndef _XBOX
		EnableMenuItem(GUI.hMenu, IDM_LOG_7110, MF_GRAYED);
	#endif
#endif
		Do7110Logging();
	}
	for(i=0;i<MAX_TABLES;i++)
	{
		if(decompack->binfiles[i]!=NULL)
		{
			delete []decompack->binfiles[i];
			decompack->binfiles[i]=NULL;
		}
	}
	Settings.SPC7110=false;
	Settings.SPC7110RTC=false;
	if(NULL!=decompack)
		delete decompack;
	decompack=NULL;
	CleanUp7110=NULL;
	Copy7110=NULL;
}

//Cache2 cleanup function
void Close7110Gfx()
{
	int i;
	if(Settings.SPC7110)
	{
#ifdef __WIN32__
	#ifndef _XBOX
		EnableMenuItem(GUI.hMenu, IDM_LOG_7110, MF_GRAYED);
	#endif
#endif
		Do7110Logging();
	}
	for(i=0;i<MAX_TABLES;i++)
	{
		if(decompack->binfiles[i]!=NULL)
		{
			fclose((FILE*)decompack->binfiles[i]);
			decompack->binfiles[i]=NULL;
		}
	}
	Settings.SPC7110=false;
	Settings.SPC7110RTC=false;
	if(NULL!=decompack)
		delete decompack;
	decompack=NULL;
	CleanUp7110=NULL;
	Copy7110=NULL;
}

//cache 3's clean-up code
void Drop7110Gfx()
{
	int i;
	if(Settings.SPC7110)
	{
#ifdef __WIN32__
	#ifndef _XBOX
		EnableMenuItem(GUI.hMenu, IDM_LOG_7110, MF_GRAYED);
	#endif
#endif
		Do7110Logging();
	}
	for(i=0;i<MAX_TABLES;i++)
	{
		if(decompack->binfiles[i]!=NULL)
		{
			if(decompack->tableEnts[i].is_file)
			{
				fclose((FILE*)decompack->binfiles[i]);
				decompack->binfiles[i]=NULL;
			}
			else
			{
				delete []decompack->binfiles[i];
				decompack->binfiles[i]=NULL;
			}
		}
	}
	Settings.SPC7110=false;
	Settings.SPC7110RTC=false;
	if(NULL!=decompack)
		delete decompack;
	decompack=NULL;
	CleanUp7110=NULL;
	Copy7110=NULL;
}

//emulate a reset.
void S9xSpc7110Reset()
{
	s7r.reg4800=0;
	s7r.reg4801=0;
	s7r.reg4802=0;
	s7r.reg4803=0;
	s7r.reg4804=0;
	s7r.reg4805=0;
	s7r.reg4806=0;
	s7r.reg4807=0;
	s7r.reg4808=0;
	s7r.reg4809=0;
	s7r.reg480A=0;
	s7r.reg480B=0;
	s7r.reg480C=0;
	s7r.reg4811=0;
	s7r.reg4812=0;
	s7r.reg4813=0;
	s7r.reg4814=0;
	s7r.reg4815=0;
	s7r.reg4816=0;
	s7r.reg4817=0;
	s7r.reg4818=0;
	s7r.reg4820=0;
	s7r.reg4821=0;
	s7r.reg4822=0;
	s7r.reg4823=0;
	s7r.reg4824=0;
	s7r.reg4825=0;
	s7r.reg4826=0;
	s7r.reg4827=0;
	s7r.reg4828=0;
	s7r.reg4829=0;
	s7r.reg482A=0;
	s7r.reg482B=0;
	s7r.reg482C=0;
	s7r.reg482D=0;
	s7r.reg482E=0;
	s7r.reg482F=0;
	s7r.reg4830=0;
	s7r.reg4831=0;
	s7r.reg4832=1;
	s7r.reg4833=2;
	s7r.reg4834=0;
	s7r.reg4840=0;
	s7r.reg4841=0;
	s7r.reg4842=0;
	s7r.written=0;
	s7r.offset_add=0;
	s7r.AlignBy=1;
	s7r.bank50Internal=0;
	memset(s7r.bank50,0x00,DECOMP_BUFFER_SIZE);
}


//outputs a cumulative log for the game.
//there's nothing really weird here, just
//reading the old log, and writing a new one.
//note the logs are explicitly little-endian, not host byte order.
void Do7110Logging()
{
	uint8 ent_temp;
	FILE* flog;
	int entries=0;
	
	if(Settings.SPC7110)
	{
		//flush last read into logging
		(*Copy7110)();
		
		if(!strncmp((char*)&Memory.ROM [0xffc0], "SUPER POWER LEAG 4   ", 21))
		{
#ifdef _XBOX
			flog=fopen("T:\\spl4-sp7.dat","rb");
#else
			flog=fopen("spl4-sp7.dat","rb");
#endif
		}
		else if(!strncmp((char*)&Memory.ROM [0xffc0], "MOMOTETSU HAPPY      ",21))
		{
#ifdef _XBOX
			flog=fopen("T:\\smht-sp7.dat","rb");
#else
			flog=fopen("smht-sp7.dat","rb");
#endif
		}
		else if(!strncmp((char*)&Memory.ROM [0xffc0], "HU TENGAI MAKYO ZERO ", 21))
		{
#ifdef _XBOX
			flog=fopen("T:\\feoezsp7.dat","rb");
#else
			flog=fopen("feoezsp7.dat","rb");
#endif
		}
		else if(!strncmp((char*)&Memory.ROM [0xffc0], "JUMP TENGAIMAKYO ZERO",21))
		{
#ifdef _XBOX
			flog=fopen("T:\\sjumpsp7.dat","rb");
#else
			flog=fopen("sjumpsp7.dat","rb");
#endif
		}
		else
		{
#ifdef _XBOX
			flog=fopen("T:\\misc-sp7.dat","rb");
#else
			flog=fopen("misc-sp7.dat","rb");
#endif
		}
		
		if(flog)
		{
			uint8 buffer[8];
			int table=0;
			uint16 offset=0;
			uint16 length=0;
			fseek(flog, 35,0);

            int f_len;
			//do
            while(1)
			{
				int i=0;
				Data7110 *log=NULL;
				f_len= fread(buffer, 1, 8, flog);
                if(f_len < 8) break;

				table=buffer[0]|(buffer[1]<<8)|(buffer[2]<<16);
				offset=buffer[6]|(buffer[7]<<8);
				length=buffer[4]|(buffer[5]<<8);
				while(i<MAX_TABLES&&log==NULL)
				{
					if(decompack->tableEnts[i].table==table)
					{
						log=&(decompack->tableEnts[i].location[(buffer[3])]);
						if((log->used_offset+log->used_len)<(offset+length))
						{
							log->used_offset=offset;
							log->used_len=length;
						}
					}
					i++;
				}
			}
			//while(!feof(flog));
			fclose(flog);
		}
		
		
		if(!strncmp((char*)&Memory.ROM [0xffc0], "SUPER POWER LEAG 4   ", 21))
		{
#ifdef _XBOX	// cwd could be the dvd-rom, so write to T:\\ which is storage region for each title
			flog=fopen("T:\\spl4-sp7.dat","wb");
#else
			flog=fopen("spl4-sp7.dat","wb");
#endif
		}
		else if(!strncmp((char*)&Memory.ROM [0xffc0], "MOMOTETSU HAPPY      ",21))
		{
#ifdef _XBOX
			flog=fopen("T:\\smht-sp7.dat","wb");
#else
			flog=fopen("smht-sp7.dat","wb");
#endif
		}
		else if(!strncmp((char*)&Memory.ROM [0xffc0], "HU TENGAI MAKYO ZERO ", 21))
		{
#ifdef _XBOX
			flog=fopen("T:\\feoezsp7.dat","wb");
#else
			flog=fopen("feoezsp7.dat","wb");
#endif
		}
		else if(!strncmp((char*)&Memory.ROM [0xffc0], "JUMP TENGAIMAKYO ZERO",21))
		{
#ifdef _XBOX
			flog=fopen("T:\\sjumpsp7.dat","wb");
#else
			flog=fopen("sjumpsp7.dat","wb");
#endif
		}
		else
		{
#ifdef _XBOX
			flog=fopen("T:\\misc-sp7.dat","wb");
#else
			flog=fopen("misc-sp7.dat","wb");
#endif
		}
		//count entries
		if(flog)
		{
			int j=0;
			int temp=0;
			for(j=0;j<MAX_TABLES;j++)
			{
				for(int k=0;k<256;k++)
				{
					if(decompack->tableEnts[j].location[k].used_len!=0)
						entries++;
				}
			}
			ent_temp=entries&0xFF;
			fwrite(&ent_temp,1,1,flog);
			ent_temp=(entries>>8)&0xFF;
			fwrite(&ent_temp,1,1,flog);
			ent_temp=(entries>>16)&0xFF;
			fwrite(&ent_temp,1,1,flog);
			ent_temp=(entries>>24)&0xFF;
			fwrite(&ent_temp,1,1,flog);
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			
			ent_temp=0;
			fwrite(&ent_temp,1,1,flog);
			ent_temp=0;
			fwrite(&ent_temp,1,1,flog);
			ent_temp=0;
			fwrite(&ent_temp,1,1,flog);
			
			for(j=0;j<MAX_TABLES;j++)
			{
				for(int k=0;k<256;k++)
				{
					if(decompack->tableEnts[j].location[k].used_len!=0)
					{
						ent_temp=decompack->tableEnts[j].table&0xFF;
						fwrite(&ent_temp,1,1,flog);//801
						ent_temp=(decompack->tableEnts[j].table>>8)&0xFF;;
						fwrite(&ent_temp,1,1,flog);//802
						ent_temp=(decompack->tableEnts[j].table>>16)&0xFF;;
						fwrite(&ent_temp,1,1,flog);//803
						ent_temp=k&0xFF;
						fwrite(&ent_temp,1,1,flog);//804
						ent_temp=decompack->tableEnts[j].location[k].used_len&0xFF;
						fwrite(&ent_temp,1,1,flog);//lsb of 
						ent_temp=(decompack->tableEnts[j].location[k].used_len>>8)&0xFF;
						fwrite(&ent_temp,1,1,flog);//msb of
						ent_temp=(decompack->tableEnts[j].location[k].used_offset)&0xFF;
						fwrite(&ent_temp,1,1,flog);//lsb of
						ent_temp=(decompack->tableEnts[j].location[k].used_offset>>8)&0xFF;
						fwrite(&ent_temp,1,1,flog);//msb of
					}
				}
			}
			fwrite(&temp,1,4,flog);
			fwrite(&temp,1,4,flog);
			fclose(flog);
		}
	}
}
bool8 S9xSaveSPC7110RTC (S7RTC *rtc_f9)
{
    FILE* fp;

    if((fp=fopen(S9xGetFilename(".rtc"), "wb"))==NULL)
        return (FALSE);
	int i=0;
	uint8 temp=0;
	for (i=0;i<16;i++)
		fwrite(&rtc_f9->reg[i],1,1,fp);
	temp=rtc_f9->index&0x00FF;
	fwrite(&temp,1,1,fp);
	temp=(rtc_f9->index)>>8;
	fwrite(&temp,1,1,fp);
	temp=(uint8)rtc_f9->control;
	fwrite(&temp,1,1,fp);
	temp=(uint8)rtc_f9->init;
	fwrite(&temp,1,1,fp);
	temp=rtc_f9->last_used&0x00FF;
	fwrite(&temp,1,1,fp);
	temp=(rtc_f9->last_used>>8)&0x00FF;
	fwrite(&temp,1,1,fp);
	temp=(rtc_f9->last_used>>16)&0x00FF;
	fwrite(&temp,1,1,fp);
	temp=(rtc_f9->last_used>>24)&0x00FF;;
	fwrite(&temp,1,1,fp);
	fclose(fp);
    return (TRUE);
}

bool8 S9xLoadSPC7110RTC (S7RTC *rtc_f9)
{
    FILE* fp;

    if((fp=fopen(S9xGetFilename(".rtc"), "rb"))==NULL)
        return (FALSE);
	for (int i=0; i<16;i++)
	{
		fread(&(rtc_f9->reg[i]),1,1,fp);
	}
	uint8 temp=0;
	fread(&temp,1,1,fp);
	rtc_f9->index=temp;
	fread(&temp,1,1,fp);
	rtc_f9->index|=(temp<<8);
	fread(&rtc_f9->control,1,1,fp);
	fread(&rtc_f9->init,1,1,fp);
	
	fread(&temp,1,1,fp);
	rtc_f9->last_used=temp;
	fread(&temp,1,1,fp);
	rtc_f9->last_used|=(temp<<8);
	fread(&temp,1,1,fp);
	rtc_f9->last_used|=(temp<<16);
	fread(&temp,1,1,fp);
	rtc_f9->last_used|=(temp<<24);
    fclose(fp);
    return (TRUE);
}