#include <stdlib.h>
#include <stdio.h>
#include <eikenv.h>
#include <e32keys.h>
#include <eikapp.h>
#include <zlib.h>
#ifdef S60V3
#include <picodrive.rsg>
#else
#include <picodriveS60.rsg>
#endif
#include <eikdoc.h>
#include <aknlistquerydialog.h>
#include <s32file.h>
#include <string.h>
#include <ctype.h>
#include <eikenv.h>
#include <sys\time.h>

#include "PicoDriveexe.h"
#include "pico.h"
#include "unzip.h"
#include "PicoInt.h"
#include "GGenie.h"

TInt KLineGap = 2;
static const char* KAboutText = 
"This emulator uses code from\n"
"these people/projects:\n"
"\n"
"Dave\n"
"Cyclone 68000 core,\n"
"Pico emulation library\n"
"Homepage:http://www.finalburn.com\n"
"E-mail: david(at)finalburn.com\n"
"\n"
"notaz\n"
"UIQ port,Cyclone 68000 hacks,some\n"
"additional coding (see changelog).\n"
"Homepage:http://notaz.atspace.com/\n"
"E-mail: notasas(at)gmail.com\n"
"\n"
"Reesy\n"
"DrZ80, the Z80 emulator\n"
"written in ARM assembly.\n"
"Homepage: http://reesy.gp32x.de/\n"
"E-mail:drsms_reesy(at)yahoo.co.uk\n"
"\n"
"Tatsuyuki Satoh, Jarek Burczynski,\n"
"MultiArcadeMachineEmulator\n"
"development\n"
"software implementation of\n"
"Yamaha FM sound generator\n"
"\n"
"MultiArcadeMachineEmulator(MAME)\n"
"development\n"
"Texas Instruments SN76489/SN76496\n"
"programmable tone/noise generator\n"
"Homepage: http://www.mame.net/\n"
"\n"
"Additional thanks\n"
"-----------------\n"
"* Peter van Sebille for ECompXL\n"
"  and his various open-source\n"
"  Symbian project to learn from.\n"
"* Mark and Jean-loup for zlib\n"
"  library.\n"
"* Reesy for also finding some\n"
"  Cyclone bugs.\n"
"* Charles MacDonald\n"
"  (http://cgfm2.emuviews.com/)\n"
"  for old but still very useful\n"
"  info about genesis hardware.\n"
"* Stúphane Dallongeville\n"
"  for creating Gens\n"
"  (http://www.gens.ws)\n"
"*The development team behind the\n"
" Symbian GCC Improvement Project\n"
" http://www.inf.u-szeged.hu\n"
" /symbian-gcc/) for their updated\n"
" compiler tools.\n"
"* Inder for the icons.\n";

// Picodrive prefrence uid
const TUid KPicoDrivePrefs={0x1234432E};

// Bittable corresponding to the bitvalues for the different control actions
TUint16 KBitValTable[EPicoNoKeys]={1,2,4,8 ,64,16,32,1024,512,256,2048,128,5,9,10,6,0,0,0};

extern"C" unsigned short *framebuff = 0;  // temporary buffer in sega native BGR format
const  int framebuffsize  = ((8+320)*(224+16))*2; // actual framebuffer size (in bytes+to support new rendering mode)

// Colour lookuptable from BGR to RGB
unsigned short gColorMapTab[4096];

// Scaling line table
TUint8 gColumnStepTable[320];
TUint8 gNarrowColumnStepTable[256];
unsigned short gLineTable[240];
TUint32 gLineOffsets[416];
TUint32 gFullOffset;
extern int PsndLen;

#ifndef S60V3
GLDEF_C  TInt E32Dll(TDllReason)
{
	return KErrNone;
}
#ifdef __WINS__
_LIT(KLitResourceFileName,	"z:\\system\\apps\\picodrives60\\PicoDriveS60.rsc");
#else
_LIT(KLitResourceFileName,	"PicoDriveS60.rsc");
#endif

#endif

#ifdef __WINS__
RHeap* gChunk;
#endif
struct Target Targ;

#ifdef S60V3
#include "S60V3Video.inl"
#else
#include "NormalVideo.inl"
#include "InterpolateVideo.inl"
#endif

TInt CPicoDriveUi::AsyncUpdateL(TAny* aAppUi)
{
	static_cast<CPicoDriveUi*>(aAppUi)->UpdateScreen();
	return 0;
}

void CPicoDriveUi::StartAsynchUpdate() 
{
	TCallBack callback(AsyncUpdateL,iEikonEnv->EikAppUi());
	iAsyncUpdate.Cancel();
	iAsyncUpdate.Set(callback);
	iAsyncUpdate.CallBack();
}


CPicoDriveUi::CPicoDriveUi():iIdleCallBack(CActive::EPriorityIdle),iStartUp(CActive::EPriorityIdle),
iAsyncUpdate(CActive::EPriorityStandard)
{
	iCurrentScan=-1;
	FramesPerSecond=60;
	PicoOpt = 7;
	iLastAboutPos = -1;
	iFrameSkip = -1;
	PsndRate = 8000;
	iInterpolate = ETrue;
	iSoundVolume = 6;
	gFullOffset = 0;
}

CPicoDriveUi::~CPicoDriveUi()
{
	delete iKeyNames;
	delete iRegNames;
	if(iView)
	{
		RemoveFromStack(iView);
		delete iView;
	}
	delete iBackBuffer;
	delete iSndStream;
#ifndef S60V3
	iCoeEnv->DeleteResourceFile(iResourceFileId);
#endif

#ifdef __WINS__
	if(gChunk != NULL)
	{
		gChunk->Close();
	}
#endif
	free(framebuff);
	framebuff = 0;
	CloseSTDLIB();
}

TKeyResponse CPicoDriveUi::HandleKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
{
	if(iCheatEnter)
	{
		return iCheatDlg->OfferKeyEventL(aKeyEvent,aType);
	}

	if(aType==EEventKey && aKeyEvent.iScanCode==EStdKeyBackspace)
	{
		if(iPicoMenu ==ESelectSoundMenu)
		{
			if(iSndRateChanged)
			{
				if(!UpdatePSndRate()) // Not compatible.. reset to 8000
				{
					PsndRate = 8000;
					UpdatePSndRate();											
				}
				
				if(iRomLoaded)
				{
					sound_rerate();
				}
			}

			iView->Clear();
			iPicoMenu = EPicoMainMenu;
			PutMainMenu();
			return EKeyWasConsumed;
		}
		else if (iPicoMenu == ESelectControlsMenu)
		{
			iView->Clear();
			iPicoMenu = EPicoMainMenu;
			PutMainMenu();
			return EKeyWasConsumed;
		}
		else if(iPicoMenu == ESelectScrMenu)
		{
			iScrMode = iLastScrMode;
			iView->Clear();
			iPicoMenu = EPicoMainMenu;
			PutMainMenu();
			return EKeyWasConsumed;
		}
		else if(iPicoMenu == ESelectCheatMenu)
		{
			if(iCheatSelection<2)
			{
				iView->Clear();
				iPicoMenu = EPicoMainMenu;
				PutMainMenu();
			}
			else
			{
				TInt index = (iListOffset+iCheatSelection)-2;
				TInt noFound = 0;
				for(TInt i = 0; i < 256;i++)
				{
					if (Liste_GG[i].code[0] != 0)
					{
						noFound++;
						if(noFound-1 == index)
						{
							Liste_GG[i].code[0] = 0;
							Liste_GG[i].active = 0;
							iNoCheats--;
							iListOffset = 0;
							iCheatSelection = 0;
							break;
						}
						
					}
				}

				iView->Clear();
				PutCheatSelect();
			}

			return EKeyWasConsumed;
		}

		if(!iEmuRunning && (iPicoMenu!=EPicoMainMenu ||iCurrentScan!=-1) )
			return EKeyWasNotConsumed;
		
		if(iRomLoaded )
		{
			iEmuRunning=!iEmuRunning;
			
			if(iEmuRunning && iRomLoaded)
			{
				iView->Clear();
				TCallBack callback(StartEmulatorL,this);
				iStartUp.Set(callback);
				iStartUp.CallBack();
			}
		}
		return EKeyWasConsumed;
	}

	if(!iEmuRunning)
	{
		if(iCurrentScan==-1)
		{
			if(aType==EEventKey)
			{
				
				switch(aKeyEvent.iScanCode) // first determine bit value to change
				{
				case EStdKeyUpArrow:
					switch(iPicoMenu)
					{
					case ESelectControlsMenu:
						{
							iCtrlSelection=!iCtrlSelection;
							PutControllerSelect();
						}break;
					case EPicoMainMenu:
						if(iSelection>0)
							iSelection--;
						else iSelection=EExitPico;
						PutMainMenu();
						break;
					case ESelectScrMenu:
						if(iScrMode>0)
							iScrMode--;
						else iScrMode=10;
						PutScreenSelect();
						break;
					case ESelectCheatMenu:
							if(iCheatSelection>0)
								iCheatSelection--;
							else
								iCheatSelection = ELastCheatItem-1;						
							PutCheatSelect();
						break;
					case EAboutPicoMenu:
						{
							iView->Clear();
							PutAbout();
						}break;
					case ESelectSoundMenu:
						{
							if(iSndSelection>0)
								iSndSelection--;
							else
								iSndSelection = ELastSoundItem-1;						
							PutSoundSelect();
						}break;
					}					
				break;
					case EStdKeyDownArrow:
						switch(iPicoMenu)
						{
						case ESelectControlsMenu:
								{
									iCtrlSelection=!iCtrlSelection;
									PutControllerSelect();
								}break;
						case EPicoMainMenu:
							iSelection++;
							if(iSelection==ELastMenuItem)
								iSelection=0;
							PutMainMenu();
							break;
						case ESelectScrMenu:
							iScrMode++;
							if(iScrMode==11)
								iScrMode=0;
							PutScreenSelect();
							break;
						case ESelectCheatMenu:
							if(iCheatSelection<(ELastCheatItem+iNoCheats-1) && iCheatSelection<6)
								iCheatSelection++;
							else if(iCheatSelection == ELastCheatItem+4 && iListOffset<iNoCheats-5)
							{
								iListOffset++;
							}
							else
							{
								iListOffset = 0;
								iCheatSelection = 0;						
							}
							PutCheatSelect();
						break;
							case EAboutPicoMenu:
							{
								iView->Clear();
								PutAbout();
							}break;
						case ESelectSoundMenu:
							{
								iSndSelection++;
								if(iSndSelection==ELastSoundItem)
									iSndSelection=0;
								PutSoundSelect();
							}break;
						}
						break;
						case EStdKeyDevice0:
						case EStdKeyDevice3:
							{
								switch(iPicoMenu)
								{
								case ESelectControlsMenu:
								{
									if(iCtrlSelection == EConfigControls)
									{
										iPicoMenu = EPicoMainMenu;
										iView->Clear();
										iCurrentScan=0;
										PutConfigKeys();
									}
									else
									{
										iEnableSixButtons = !iEnableSixButtons;
										PicoOpt=PicoOpt^ 32;									
										
										PutControllerSelect();
									}
								}break;
								case ESelectCheatMenu:
									{
										switch(iCheatSelection)
										{
										case EAddCheat:
											{
												TBuf8<16> cheatCode;
												iCheatEnter = ETrue;
												iCheatDlg = new (ELeave) CPicoAddCheatDlg(cheatCode);
												iCheatDlg->SetMopParent(iEikonEnv->EikAppUi());		   
												TInt result = iCheatDlg->ExecuteLD(R_PICO_ADD_CHEAT);

												if(result == EEikBidOk)
												{
													for(TInt i = 0; i < 256;i++)
													{
														if (Liste_GG[i].code[0] == 0)
														{														
															if(check_code((const char*) cheatCode.PtrZ(),i))
															{
																decode( Liste_GG[i].code, (patch *) (&(Liste_GG[i].addr)));
																
																if ((Liste_GG[i].restore == 0xFFFFFFFF) && (Liste_GG[i].addr < Pico.romsize) && (iRomLoaded))
																{
																	Liste_GG[i].restore = (unsigned int) (Pico.rom[Liste_GG[i].addr] & 0xFF);
																	Liste_GG[i].restore += (unsigned int) ((Pico.rom[Liste_GG[i].addr + 1] & 0xFF) << 8);
																}
																iNoCheats++;
																Liste_GG[i].active = 1;
															}
															break; // Found position free
														}
													}
												}
												iCheatDlg = NULL;
												iCheatEnter = EFalse;
											}break;
										case EClearCheats:
											{
												iNoCheats = 0;
												for(TInt i = 0; i < 256;i++)
												{
													Liste_GG[i].code[0] = 0;
													Liste_GG[i].active = 0;
												}
											}
											break;
										default:
											{
												TInt index = (iListOffset+iCheatSelection)-2;
												TInt noFound = 0;
												for(TInt i = 0; i < 256;i++)
													{
														if (Liste_GG[i].code[0] != 0)
														{
															noFound++;
															if(noFound-1 == index)
															{
																Liste_GG[i].active=!Liste_GG[i].active;
																break;
															}

														}
												}
											}
											break;
									
										}

										PutCheatSelect();
									}
									break;
								case EAboutPicoMenu:
									{
										iView->Clear();
										PutAbout();
									}break;
								case ESelectScrMenu:
									{

										switch(iScrMode)
										{
										case 5:
											iInterpolate = !iInterpolate;
											iView->Clear();
											PutScreenSelect();
											break;
										case 6:
											iFrameSkip++;
											if(iFrameSkip == 11)
											{
												iFrameSkip = -1;
											}
											iView->Clear();
											PutScreenSelect();
											break;
										case 7:
											PicoOpt = PicoOpt^0x40;
											iView->Clear();
											PutScreenSelect();
										break;
										case 8:
											PicoOpt = PicoOpt^0x80;
											iView->Clear();
											PutScreenSelect();
										break;
										case 9:
											PicoOpt = PicoOpt^0x10;
											iView->Clear();
											PutScreenSelect();
											break;
										case 10:
											switch(PicoRegionOverride)
											{
											case 0:
											default:
												PicoRegionOverride = 1;
												break;
											case 1:
												PicoRegionOverride = 2;
												break;
											case 2:
												PicoRegionOverride = 4;
												break;
											case 4:
												PicoRegionOverride = 8;
												break;
											case 8:
												PicoRegionOverride = 0;
												break;
											}

											iView->Clear();
											PutScreenSelect();
											break;
										default:
											{
											TBitmapUtil util(iBackBuffer);
											util.Begin(TPoint(0,0));
											TSize sz=iBackBuffer->SizeInPixels();
											TInt dataSize=sz.iWidth*sz.iHeight*2;
											TPtr8 ptr(reinterpret_cast<unsigned char*>(iBackBuffer->DataAddress()),dataSize,dataSize);
											ptr.Fill(0);
											util.End();
											iPicoMenu=EPicoMainMenu;
											iView->Clear();
											PutMainMenu();
											TargetInit(); 
											SaveSettingsL();
											}break;
										}									
									}break;
								case ESelectSoundMenu:
									{
										switch(iSndSelection)
										{
										case EEnableZ80:
											PicoOpt=PicoOpt^4;
											break;
										case EEnableYM2612:
											PicoOpt=PicoOpt^1;
											break;
										case EEnableSN76496:
											PicoOpt=PicoOpt^2;
											break;
										case ESoundVolume:
											{										
												iSoundVolume++;
												if(iSoundVolume==11)
												{
													iSoundVolume=0;
													iEnableSound=EFalse;
												}
												else
												{
													iEnableSound=ETrue;
													iSndStream->SetVolume((iSndStream->MaxVolume()*iSoundVolume)/10);
												}

												if(!iEnableSound)
												{
													iSndStream->Stop();
												}
											}break;
										case ESoundRate:
											{
												iSndRateChanged = ETrue;

												switch(PsndRate)
												{
												case 8000:
													PsndRate = 11025;
													break;
												case 11025:
													PsndRate = 16000;
													break;
												case 16000:
													PsndRate = 22050;
													break;
												case 22050:
													PsndRate = 8000;
													break;
												}											
											}break;
										}	
										PutSoundSelect();
									}break;
								case EPicoMainMenu:
									{
										switch(iSelection)
										{
										case EResetHw:
											{
												if(!PicoReset(0))
												{
													iEmuRunning=ETrue;
													TCallBack callback(StartEmulatorL,this);
													iStartUp.Set(callback);
													iStartUp.CallBack();
												}
											}
											break;
										case ELoadState:
											{
												if(iRomLoaded)
												{													
													saveLoadGame(1,0);													
													iEmuRunning=ETrue;
													iView->Clear();
													TCallBack callback(StartEmulatorL,this);
													iStartUp.Set(callback);
													iStartUp.CallBack();										
												}
											}
											break;
										case ESaveState:
											{												
												if(iRomLoaded)
												{													
													saveLoadGame(0,0);
													iEmuRunning=ETrue;
													iView->Clear();
													TCallBack callback(StartEmulatorL,this);
													iStartUp.Set(callback);
													iStartUp.CallBack();
													
												}
											}
											break;
										case ELoadRom:
											{
												TParsePtr parse(iRomName);
												iRomName=parse.DriveAndPath();
												if(SelectFile(iRomName))
												{
													SaveSettingsL();
													EmulateExit();
													TPtr8 ptr((unsigned char*)RomName,256);
													ptr.Fill(0,256);
													ptr.Copy(iRomName);
													User::CompressAllHeaps();
													if(EmulateInit()==0)
													{
														if(Pico.m.pal)
														{
															FramesPerSecond=50;
														}
														else 
														{
															FramesPerSecond=60;
														}
														
														SetKeyBlockMode(ENoKeyBlock);
														iEmuRunning=ETrue;
														iRomLoaded=ETrue;
														iView->Clear();
														TCallBack callback(StartEmulatorL,this);
														iStartUp.Set(callback);
														iStartUp.CallBack();
													}
													else
													{
														iRomName=KNullDesC();
														iView->Clear();
														PutMainMenu();
														iRomLoaded=EFalse;
													}
												}
												else
												{
													iView->Clear();
													PutMainMenu();
												}
											}
											break;//load rom
										case ESetControls:
											iPicoMenu = ESelectControlsMenu;
											PutControllerSelect();										
											break;
										case ESetScreen:
											iView->Clear();
											iLastScrMode = iScrMode; // In case of cancel
											PutScreenSelect();
											iPicoMenu=ESelectScrMenu;
											break;
										case ESelectSound:
											{			
												iSndRateChanged = EFalse;
												iPicoMenu=ESelectSoundMenu;
												PutSoundSelect();
											}break;
										case ESelectCheat:
											{
											iPicoMenu=ESelectCheatMenu;
											iListOffset = 0;
											iNoCheats = 0;
											for(TInt i = 0; i < 256;i++)
											{
												if (Liste_GG[i].code[0] != 0)
												{
													iNoCheats++;
													if ((Liste_GG[i].restore != 0xFFFFFFFF) && (Liste_GG[i].addr < Pico.romsize) && (iRomLoaded))
													{
														Pico.rom[Liste_GG[i].addr] = (unsigned char)(Liste_GG[i].restore & 0xFF);
														Pico.rom[Liste_GG[i].addr + 1] = (unsigned char)((Liste_GG[i].restore & 0xFF00) >> 8);
													}
													
												}
											}

											PutCheatSelect();
											}
										break;
										case EAboutPico:
											{
												iView->Clear();
												PutAbout();
												iPicoMenu=EAboutPicoMenu;
											}break;
										case EExitPico:
											{
												SaveSettingsL();
												EmulateExit();
												Exit();
											}
											break;
							}
								}
						}break;
					}
					break;
				}
			}
		}
		else
		{
			if(aType == EEventKeyDown)
			{
				if(aKeyEvent.iScanCode != EStdKeyBackspace)
				{
					iScanCodes[iCurrentScan]=aKeyEvent.iScanCode;
				}
				else
				{
					iScanCodes[iCurrentScan] = KErrNotFound;
				}

				iCurrentScan++;
				if(iCurrentScan==iKeyNames->Count())
				{
					SaveSettingsL();
					iCurrentScan=-1;
					iView->DrawText(_L("Done!"),TPoint(0,iFontHeight*11));
					
					User::After(1000000);
					iView->Clear();
					PutMainMenu();
				}
				else
				{
					iView->Clear();
					PutConfigKeys();
				}
			}
		}
	}
	else
	{
		if((aType == EEventKeyUp || aType == EEventKeyDown))
		{						
			TUint16 bitVal=0;
			for(TInt loop=0;loop<16;loop++)
			{
				if(aKeyEvent.iScanCode==iScanCodes[loop])
				{
					bitVal=KBitValTable[loop];
					break;
				}
			}
			
			if(aType == EEventKeyUp)
			{
				iPad1=iPad1&(65535-bitVal); // remove bit
			}
			else 
			{
				iPad1=(iPad1|bitVal); // set bit
			}			
		}

		if(aType == EEventKey)
		{
			if(aKeyEvent.iScanCode == iScanCodes[EPicoResetKey])
			{
				PicoReset(0);
			}

			if(aKeyEvent.iScanCode == iScanCodes[EPicoPanLKey] && gFullOffset>0)
			{
				gFullOffset-=24;
			}
			else if(aKeyEvent.iScanCode == iScanCodes[EPicoPanRKey]&& gFullOffset<144)
			{
				gFullOffset+=24;
			}
		}
	}
	
	return EKeyWasConsumed;
}

TInt CPicoDriveUi::SelectFile(TFileName& aFileName)
{
	TFileName filename=aFileName;
	TInt selectedIndex=-1;
	RArray<TEntry> romList;
	TDriveList driveList;
	TBool refresh=EFalse;
	TInt lastLength=-1;
	do
	{
		CAknListQueryDialog* dlg = new (ELeave) CAknListQueryDialog(&selectedIndex);
		CDesCArrayFlat* list=new (ELeave) CDesCArrayFlat(5);
		refresh=EFalse;
		if(filename.Length()==0)
		{
			
			iEikonEnv->FsSession().DriveList(driveList);
			for(TInt drive=0;drive<driveList.Length();drive++)
			{
				if(driveList[drive]!=0)
				{
					TBuf<16> form;
					form.Format(_L("%c:\\"),drive+65);
					list->AppendL(form);
				}
			}	
		}
		else
		{
			CDir* romDir = NULL;;
			romList.Reset();
			iEikonEnv->FsSession().GetDir(filename,KEntryAttMatchMask,0,romDir);
			
			if(romDir!=NULL)
				list->AppendL(_L(".. <DIR>"));
			
			if(romDir!=NULL && romDir->Count()>0)
			{
				for(TInt loop=0;loop<romDir->Count();loop++)
				{
					const TEntry& entry=(*romDir)[loop];
					TFileName name=entry.iName;
					if(entry.IsDir())
					{
						name.Append(_L(" <DIR>"));
						list->AppendL(name);
						romList.Append(entry);
					}
					else
					{
						TParsePtr parse(name);
						
						if(parse.Ext().CompareF(_L(".bin"))==KErrNone ||
							parse.Ext().CompareF(_L(".smd"))==KErrNone ||
							parse.Ext().CompareF(_L(".zip"))==KErrNone)
						{
							romList.Append(entry);
							list->AppendL(name);
						}
					}
					
				}
				delete romDir;
				romDir=NULL;
			}
			else // no files found.. or path not found.. return to
			{
				if(filename.Length()>3) // more than a c:\ specified
				{
					
					refresh=ETrue;
					filename=KNullDesC();
				}
				else return EFalse;
			}
			
		}
		if(list->Count()>0)
		{
			dlg->PrepareLC(R_PICO_FILE_SELECT_DIALOG);
			dlg->SetItemTextArray(list);
			if(dlg->RunLD())
			{
				if(filename.Length()==0)
				{
					TFileName driveLetter;
					TUint8 driveL;
					TInt countedDrives=0;
					for(TInt drive=0;drive<driveList.Length();drive++)
					{
						if(driveList[drive]!=0 && countedDrives==selectedIndex)
						{
							driveLetter.Format(_L("%c:\\"),drive+65);
							driveL=drive+65;
							break;
						}
						else if(driveList[drive]!=0) countedDrives++;
						
					}
					filename=driveLetter;
					refresh=ETrue;					
				}
				else
				{
					if(selectedIndex>0)
					{
						selectedIndex--;
						const TEntry& entry=romList[selectedIndex];
						TFileName name =entry.iName;
						if(entry.IsDir())
						{
							lastLength=aFileName.Length();
							filename.Append(name);
							filename.Append(_L("\\"));
							
							refresh=ETrue;
							
						}
						else
						{
							filename.Append(name);
							aFileName=filename;
							romList.Close();
							return ETrue;
						}
					}
					else
					{
						refresh=ETrue;
						TInt pos=filename.Left(filename.Length()-1).LocateReverse('\\');
						if(pos!=KErrNotFound)
							filename=filename.Left(pos+1);// keep 
						else filename=KNullDesC();
						// and changefilename..
						
						//return 2;// go up one.
					}
				}
			}
			else
			{
				romList.Close();
				return EFalse;
			}
		}
	}while(refresh);
	romList.Close();
	return EFalse;
}

void CPicoDriveUi::SaveSettingsL()
{
#ifdef S60V3
	CDictionaryStore* prefs = Application()->OpenIniFileLC(iEikonEnv->FsSession());
#else
	CDictionaryFileStore* prefs=CDictionaryFileStore::OpenLC(iEikonEnv->FsSession(),iAppPath,TUid::Uid(0));
#endif
	ExternalizeL(*prefs);
	prefs->CommitL();
	CleanupStack::PopAndDestroy();//close prefs
}


void CPicoDriveUi::ExternalizeL(CDictionaryStore& aStore) 
{	
	RDictionaryWriteStream writeStream;
	writeStream.AssignLC(aStore, KPicoDrivePrefs);
	
	TInt loop=0;
	for(loop=0;loop<EPicoNoKeys;loop++)
	{
		writeStream.WriteInt32L(iScanCodes[loop]);
	}
	
	writeStream.WriteInt32L(iScrMode);
	writeStream.WriteInt32L(PicoOpt);
	writeStream.WriteInt32L(iSoundVolume);
	writeStream.WriteInt32L(iFrameSkip);
	writeStream.WriteInt32L(PsndRate);
	writeStream.WriteInt32L(iInterpolate);
	writeStream.WriteInt32L(iEnableSixButtons);
	writeStream.WriteInt32L(PicoRegionOverride);
	TParsePtr parser(iRomName);
	writeStream.WriteInt32L(parser.DriveAndPath().Length());
	writeStream.WriteL(parser.DriveAndPath(),parser.DriveAndPath().Length());
	writeStream.CommitL();
	
	CleanupStack::PopAndDestroy();//writeStream
}

void CPicoDriveUi::InternalizeL(const CDictionaryStore& aStore)
{
	if (!aStore.IsPresentL(KPicoDrivePrefs))
	{
		iFirstStart = ETrue;
		return;
	}
	RDictionaryReadStream	readStream;
	readStream.OpenLC(aStore, KPicoDrivePrefs);
	TInt loop=0;
	for(loop=0;loop<EPicoNoKeys;loop++)
	{
		iScanCodes[loop] = readStream.ReadInt32L();
	}
	
	iScrMode=readStream.ReadInt32L();
	PicoOpt=readStream.ReadInt32L();
	iEnableSound = PicoOpt&3;
	iSoundVolume=readStream.ReadInt32L();
	iFrameSkip = readStream.ReadInt32L();
	PsndRate = readStream.ReadInt32L();
	iInterpolate = readStream.ReadInt32L();
	iEnableSixButtons = readStream.ReadInt32L();
	PicoRegionOverride = readStream.ReadInt32L();

	TInt len=0;
	TRAPD(err,len=readStream.ReadInt32L())
		if(err==KErrNone)
		{
			readStream.ReadL(iRomName,len);
		}
		
		CleanupStack::PopAndDestroy();//readStream
}

void CPicoDriveUi::ConstructL()
{
#ifdef S60V3
	CAknAppUi::BaseConstructL(CAknAppUi::EAknEnableSkin);
#else
	BaseConstructL(ENoAppResourceFile);	
#endif
	framebuff =  (unsigned short *) malloc(framebuffsize);
	memset(framebuff,0,framebuffsize);

	iFontHeight = iEikonEnv->NormalFont()->HeightInPixels()+KLineGap;
	iScanCodes[0]= EStdKeyUpArrow;			
	iScanCodes[1]= EStdKeyDownArrow;
	iScanCodes[2]=EStdKeyLeftArrow;
	iScanCodes[3]= EStdKeyRightArrow;
	iScanCodes[6]=EStdKeyDevice0;
	iScanCodes[4]= EStdKeyDevice1;
	iScanCodes[5]=EStdKeyDevice3;
	iScanCodes[7]=0;//x
	iScanCodes[8]=0; // y
	iScanCodes[9]=0;//z
	iScanCodes[10]=0;// mode
	iScanCodes[11]= '0'; // start
#ifdef __WINS__
	gChunk = UserHeap::ChunkHeap(&_L("ROMHEAP"),512000,16384000);
#endif

#ifndef S60V3
	TFileName name;
	iEikonEnv->RootWin().SetName(_L("PicoDrive"));
#ifndef __WINS__
	RProcess process;
	process.Rename(_L("PicoDrive"));
	TFileName fname  =process.FileName();
	TParsePtr parser(fname);
	name.Append(parser.DriveAndPath());
#endif
	name.Append(KLitResourceFileName());
	iAppPath=_L("C:");
	iAppPath.Append(TParsePtr(name).Path());
	iAppPath.Append(_L("PicoDriveS60.ini"));
	iResourceFileId = iCoeEnv->AddResourceFileL(name); // eb205: needs to hunt around drives
#endif // S60V3
	iKeyNames =iEikonEnv->ReadDesCArrayResourceL(R_PICODRIVE_KEYS);
	iRegNames = iEikonEnv->ReadDesCArrayResourceL(R_PICODRIVE_REGIONS);

	iEikonEnv->FsSession().MkDirAll(TParsePtr(iAppPath).DriveAndPath());
#ifdef S60V3
	CDictionaryStore* prefs = Application()->OpenIniFileLC(iEikonEnv->FsSession());
	InternalizeL(*prefs);
	CleanupStack::PopAndDestroy();//close prefs
#else
	TRAPD(err,{CDictionaryFileStore* prefs=CDictionaryFileStore::OpenLC(iEikonEnv->FsSession(),iAppPath,TUid::Uid(0));
	InternalizeL(*prefs);
	CleanupStack::PopAndDestroy();//close prefs
	});
#endif
	
	if(iFirstStart)
	{
		iPicoMenu = EAboutPicoMenu;
	}
	
	iView=new (ELeave)CQPicoDriveView;
	iView->ConstructL();
	AddToStackL(iView);
	iDisplayMode =iEikonEnv->ScreenDevice()->DisplayMode();
	if(iDisplayMode != EColor64K && iDisplayMode != EColor4K)
	{
		iDisplayMode=EColor64K;; // Also tried to switch to by the view.
	}
	CalculatePaletteTable();
	iBackBuffer= new (ELeave)CFbsBitmap;
	iBackBuffer->Create(iEikonEnv->ScreenDevice()->SizeInPixels(),iDisplayMode);
	TBitmapUtil util(iBackBuffer);
	util.Begin(TPoint(0,0));
	TSize sz=iBackBuffer->SizeInPixels();
	Targ.view = TRect(TPoint(0,0),sz);
	TInt dataSize=sz.iWidth*sz.iHeight*2;
	Targ.scanline_length = sz.iWidth*2;
	Targ.screen_offset = Targ.scanline_length*(sz.iHeight-1);
	TPtr8 ptr(reinterpret_cast<unsigned char*>(iBackBuffer->DataAddress()),dataSize,dataSize);
	ptr.Fill(0);
	util.End();
	SetKeyBlockMode(ENoKeyBlock);
	iSelection=0;
	
	iSndStream = CMdaAudioOutputStream::NewL(*this);
	iAudioSettings.Query();
	iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate8000Hz;
	iAudioSettings.iChannels = TMdaAudioDataSettings::EChannelsMono;
	iAudioSettings.iFlags = 0;
	iAudioSettings.iVolume = iAudioSettings.iMaxVolume/2;
	iSndStream->Open(&iAudioSettings);
	CActiveScheduler::Start(); // wait for open
}

void CPicoDriveUi::PutAbout(TBool iOnlyRedraw)
{
	TPtrC8 charPtr((unsigned char*)KAboutText,strlen(KAboutText));
	HBufC* credits = HBufC::NewLC(charPtr.Length());
	credits->Des().Copy(charPtr);
	
	if(iLastAboutPos>=credits->Length())
	{
		iLastAboutPos = -1;
		iView->Clear();
		iPicoMenu = EPicoMainMenu;
		PutMainMenu();
	}
	else
	{
		if(iLastAboutPos == -1)
			iLastAboutPos = 0;
		iView->DrawText(_L("PicoDrive S60 Credits"),TPoint(0,0));
		if(iOnlyRedraw && iLastAboutPos == 0)
			iView->DrawTextInRect(*credits,TRect(0,iFontHeight*2,Targ.view.iBr.iX,Targ.view.iBr.iY),iLastAboutPos);
		else
			iLastAboutPos = iView->DrawTextInRect(*credits,TRect(0,iFontHeight*2,Targ.view.iBr.iX,Targ.view.iBr.iY),iLastAboutPos);
	}
	CleanupStack::PopAndDestroy(credits);
	
}
void CPicoDriveUi::PutMainMenu()
{
	iView->DrawText(_L("PicoDrive S60"),TPoint(0,0));
	iView->DrawText(_L("by Dave et Co"),TPoint(0,iFontHeight*1));
	
	iView->DrawText(_L("Load ROM"),TPoint(0,iFontHeight*3),iSelection==0);
	iView->DrawText(_L("Load state"),TPoint(0,iFontHeight*4),iSelection==1);
	iView->DrawText(_L("Save state"),TPoint(0,iFontHeight*5),iSelection==2);
	iView->DrawText(_L("Configure controls"),TPoint(0,iFontHeight*6),iSelection==3);
	iView->DrawText(_L("Configure screen"),TPoint(0,iFontHeight*7),iSelection==4);
	iView->DrawText(_L("Configure sound"),TPoint(0,iFontHeight*8),iSelection==5);
	iView->DrawText(_L("Game Genie/Cheats"),TPoint(0,iFontHeight*9),iSelection==6);

	iView->DrawText(_L("Reset"),TPoint(0,iFontHeight*10),iSelection==7);
	
	iView->DrawText(_L("Credits"),TPoint(0,iFontHeight*11),iSelection==8);
	iView->DrawText(_L("Exit"),TPoint(0,iFontHeight*12),iSelection==9);
	if(iRomName.Length()>0)
	{
		iView->DrawText(TParsePtr(iRomName).Name(),TPoint(0,iFontHeight*13));
	}
	else
	{
		iView->DrawText(_L("No rom loaded"),TPoint(0,iFontHeight*13));
	}
	
}

void CPicoDriveUi::PutScreenSelect()
{
	TInt regionIndex = 0;

	switch(PicoRegionOverride)
	{
	default:
		regionIndex = 0;
		break;
	case 1:
		regionIndex = 1;
		break;
	case 2:
		regionIndex = 2;
		break;
	case 4:
		regionIndex = 3;
		break;
	case 8:
		regionIndex = 4;
		break;
	}

	iView->DrawText(_L("PicoDrive S60"),TPoint(0,0));
	iView->DrawText(_L("Screen options"),TPoint(0,iFontHeight));
	iView->DrawText(_L("Portrait"),TPoint(0,iFontHeight*3),iScrMode==0);
	iView->DrawText(_L("Landscape Left"),TPoint(0,iFontHeight*4),iScrMode==1);
	iView->DrawText(_L("Landscape Right"),TPoint(0,iFontHeight*5),iScrMode==2);
	iView->DrawText(_L("Portrait stretched"),TPoint(0,iFontHeight*6),iScrMode==3);
	iView->DrawText(_L("Portrait full"),TPoint(0,iFontHeight*7),iScrMode==4);
	if(iInterpolate)
	{
	iView->DrawText(_L("Interpolate on"),TPoint(0,iFontHeight*8),iScrMode==5);
	}
	else
	{
	iView->DrawText(_L("Interpolate off"),TPoint(0,iFontHeight*8),iScrMode==5);
	}

	if(iFrameSkip == -1)
	{
		iView->DrawText(_L("Frameskip auto"),TPoint(0,iFontHeight*9),iScrMode==6);
	}
	else
	{	
		TBuf<64> skip;
		skip.Format(_L("Frameskip %d"),iFrameSkip);
		iView->DrawText(skip,TPoint(0,iFontHeight*9),iScrMode==6);
	}

	if(PicoOpt & 0x40)
	{
	iView->DrawText(_L("Accurate timing on"),TPoint(0,iFontHeight*10),iScrMode==7);
	}
	else
	{
	iView->DrawText(_L("Accurate timing off"),TPoint(0,iFontHeight*10),iScrMode==7);
	}

	if(PicoOpt & 0x80)
	{
	iView->DrawText(_L("Accurate sprites on"),TPoint(0,iFontHeight*11),iScrMode==8);
	}
	else
	{
	iView->DrawText(_L("Accurate sprites off"),TPoint(0,iFontHeight*11),iScrMode==8);
	}

	if(PicoOpt & 0x10)
	{
	iView->DrawText(_L("Alt. renderer on"),TPoint(0,iFontHeight*12),iScrMode==9);
	}
	else
	{
	iView->DrawText(_L("Alt. renderer off"),TPoint(0,iFontHeight*12),iScrMode==9);
	}

	iView->DrawText(iRegNames->MdcaPoint(regionIndex),TPoint(0,iFontHeight*13),iScrMode==10);
	
}

void CPicoDriveUi::PutSoundSelect()
{
	iView->Clear();
	iView->DrawText(_L("PicoDrive S60"),TPoint(0,0));
	iView->DrawText(_L("Sound options"),TPoint(0,iFontHeight));
	if (PicoOpt&4)
		iView->DrawText(_L("Z80 enabled"),TPoint(0,iFontHeight*3),iSndSelection==EEnableZ80);
	else
		iView->DrawText(_L("Z80 disabled"),TPoint(0,iFontHeight*3),iSndSelection==EEnableZ80);
	if (PicoOpt&1)
		iView->DrawText(_L("YM2612 enabled"),TPoint(0,iFontHeight*4),iSndSelection==EEnableYM2612);
	else
		iView->DrawText(_L("YM2612 disabled"),TPoint(0,iFontHeight*4),iSndSelection==EEnableYM2612);
	
	if (PicoOpt&2)
		iView->DrawText(_L("SN76496 enabled"),TPoint(0,iFontHeight*5),iSndSelection==EEnableSN76496);
	else
		iView->DrawText(_L("SN76496 disabled"),TPoint(0,iFontHeight*5),iSndSelection==EEnableSN76496);
	
	
	TBuf<32> vol;
	vol.Format(_L("Volume %d"),iSoundVolume*10);
	iView->DrawText(vol,TPoint(0,iFontHeight*6),iSndSelection==ESoundVolume);

	vol.Format(_L("Sample rate %dKhz"),PsndRate/1000);
	iView->DrawText(vol,TPoint(0,iFontHeight*7),iSndSelection==ESoundRate);
}


void CPicoDriveUi::PutCheatSelect()
{
	iView->Clear();
	iView->DrawText(_L("PicoDrive S60"),TPoint(0,0));
	iView->DrawText(_L("Cheat options"),TPoint(0,iFontHeight));

	iView->DrawText(_L("Add cheat"),TPoint(0,iFontHeight*3),iCheatSelection==EAddCheat);
	iView->DrawText(_L("Clear cheats"),TPoint(0,iFontHeight*4),iCheatSelection==EClearCheats);
	TInt noCheats = 0;
	TBuf<17>cheatCode;

	for(TInt i = iListOffset; (i < 256)&&(noCheats<5); i++)
			{
				if (Liste_GG[i].code[0] != 0)
				{

					TRgb textColour = Liste_GG[i].active?KRgbGreen:KRgbDarkGreen;
					if(iCheatSelection == 2+(i-iListOffset))
					{
						textColour = Liste_GG[i].active?KRgbRed:KRgbDarkRed;
					}

					TPtrC8 ptr((const unsigned char*)Liste_GG[i].code,strlen(Liste_GG[i].code));
					cheatCode.Copy(ptr);										

					iView->DrawText(cheatCode,TPoint(0,iFontHeight*(5+noCheats)),EFalse,textColour);
				
					noCheats++;
				}
			}

	iView->DrawText(_L("Supports GG & Patch"),TPoint(0,iFontHeight*11));
	iView->DrawText(_L("GG: XXXX-XXXX"),TPoint(0,iFontHeight*12));
	iView->DrawText(_L("Patch: XXXXXX:YYYY"),TPoint(0,iFontHeight*13));
}

void CPicoDriveUi::PutControllerSelect()
{
	iView->Clear();
	iView->DrawText(_L("PicoDrive S60"),TPoint(0,0));
	iView->DrawText(_L("Controller options"),TPoint(0,iFontHeight));
		if (iEnableSixButtons)
		iView->DrawText(_L("6 button pad"),TPoint(0,iFontHeight*3),iCtrlSelection==EControllerType);
	else
		iView->DrawText(_L("3 button pad"),TPoint(0,iFontHeight*3),iCtrlSelection==EControllerType);
	iView->DrawText(_L("Configure keys"),TPoint(0,iFontHeight*4),iCtrlSelection==EConfigControls);



}


void CPicoDriveUi::PutConfigKeys()
{
	iView->DrawText(_L("PicoDrive S60"),TPoint(0,0));
	iView->DrawText(_L("Configure keys"),TPoint(0,iFontHeight));
	iView->DrawText(_L("Please press:"),TPoint(0,iFontHeight*3));
	iView->DrawText(iKeyNames->MdcaPoint(iCurrentScan),TPoint(0,iFontHeight*4));

	iView->DrawText(_L("Press 'C' to skip this!"),TPoint(0,iFontHeight*6));

}

TInt CPicoDriveUi::IdleCallBackStop(TAny* /*aAppUi*/)
{
	CActiveScheduler::Stop();
	return 0;
}

TInt CPicoDriveUi::StartEmulatorL(TAny* aAppUi)
{
	static_cast<CPicoDriveUi*>(aAppUi)->StartEmulatorL();
	return 0;
}

void CPicoDriveUi::HandleForegroundEventL(TBool aForeground)
{
	if(iView != NULL)
	{
		iView->iForeground=aForeground;
	}

	if(!aForeground)
	{
		if(iView != NULL)
		{
			iView->AbortNow(RDirectScreenAccess::ETerminateCancel);
		}
	}
	else
	{
		if(iView != NULL)
		{
		iView->Restart(RDirectScreenAccess::ETerminateCancel);
		UpdateScreen();
		}

		SetKeyBlockMode(ENoKeyBlock);
	}
	
}

void CPicoDriveUi::UpdateScreen()
{
	if(!iEmuRunning)
	{
		iView->Clear();
		if(iCurrentScan>=0)
		{
			PutConfigKeys();
		}
		else if(iPicoMenu==ESelectScrMenu)
		{
			PutScreenSelect();
		}
		else if (iPicoMenu==EAboutPicoMenu)
		{
			PutAbout(ETrue);
		}
		else if (iPicoMenu==ESelectSoundMenu)
		{
			PutSoundSelect();
		}
		else if (iPicoMenu==ESelectControlsMenu)
		{
			PutControllerSelect();
		}
		else if (iPicoMenu==ESelectCheatMenu)
		{
			PutCheatSelect();
		}
		else 
			PutMainMenu();
		
	}
}

void CPicoDriveUi::StartEmulatorL()
{	
	iView->Clear();
	TTime time;
	time.HomeTime();
#ifdef S60V3
	LastSecond=(TInt)(time.Int64()/1000);//GetTickCount(); 
#else
	LastSecond=(TInt)(time.Int64()/1000).GetTInt();//GetTickCount(); 
#endif
	FramesDone=0;
	
	iSndStream->SetVolume((iSndStream->MaxVolume()*iSoundVolume)/10);
	UpdatePSndRate();

	while(iEmuRunning)
	{	
		EmulateFrame();
	}
	iSndStream->Stop();
	iView->Clear();
	PutMainMenu();
	
}

/**
 * Calculates the palette table 0-4096
 */
void CPicoDriveUi::CalculatePaletteTable()
{
	for(TInt cram =0;cram<4096;cram++)
	{
		if(iDisplayMode == EColor4K)
		{
			unsigned short high=0x111;
			high|=(cram&0x00e)<<8; // Red
			high|=(cram&0x0e0); // Green
			high|=(cram&0xe00)>> 8; // Blue
			gColorMapTab[cram] =  high;
		}
		else // 64K color mode
		{
			unsigned short high=0x0841;
			// Convert 0000bbbb ggggrrrr
			// to      rrrr1ggg g10bbbb1
			high|=(cram&0x00f)<<12; // Red
			high|=(cram&0x0f0)<< 3; // Green
			high|=(cram&0xf00)>> 7; // Blue
			gColorMapTab[cram] = high;
		}
	}
}

int CPicoDriveUi::TargetInit()
{
	PicoCram=NULL;
	TUint16 currentLine = 0;
	TReal xFactor = 1;
	TReal xNarrowFactor = 1;
	TReal yFactor = 1;
	TInt loop;

	memset(framebuff,0,framebuffsize);

	if(iScrMode==0)
	{
#ifdef S60V3
		xFactor = ((TReal)Targ.view.iBr.iX/(TReal)320);
		xNarrowFactor = ((TReal)Targ.view.iBr.iX/(TReal)256);
		
		if(xFactor>1)
			xFactor = 1;
		
		if(xNarrowFactor>1)
			xNarrowFactor = 1;

		yFactor = ((TReal)Targ.view.iBr.iY/(TReal)240);

		if(yFactor>1)
			yFactor = 1;		
		
		for(loop = 0;loop<256;loop++)
		{
			TInt line = (loop*xNarrowFactor);
			TInt nextLine = ((loop+1)*xNarrowFactor);
			if(line != nextLine)
			{
				gNarrowColumnStepTable[loop] = 1;
			}
			else
			{
				gNarrowColumnStepTable[loop] = 0;
			}				
		}
		
		for(loop = 0;loop<320;loop++)
		{
			TInt line = (loop*xFactor);
			TInt nextLine = ((loop+1)*xFactor);
			if( line != nextLine) 
			{
				gColumnStepTable[loop] = 1;
			}
			else
			{
				gColumnStepTable[loop] = 0;
			}		
		}

		for(TInt loop = 0;loop<240;loop++)
		{
			gLineTable[loop] = currentLine;
			if((loop*yFactor) != ((loop+1)*yFactor))
				currentLine++;
		}
		myPicoScan=EmulateScan16;
		
#else
		if(iInterpolate)
			myPicoScan=EmulateScan16_176Interpolate;
		else
			myPicoScan=EmulateScan16_176;
		for(TInt loop = 0;loop<240;loop++)
		{
			gLineTable[loop] = currentLine;
			if(((loop*3)/4) != (((loop+1)*3)/4))
				currentLine++;
		}
#endif
		KBitValTable[0] = 1;
		KBitValTable[1] = 2;
		KBitValTable[2] = 4;
		KBitValTable[3] = 8;
	}
#ifdef S60V3
	else if (iScrMode == 1 || iScrMode == 2)
	{
		xFactor = ((TReal)Targ.view.iBr.iY/(TReal)320);
		xNarrowFactor = ((TReal)Targ.view.iBr.iY/(TReal)256);	

		if(xFactor>2)
			xFactor = 2;
		
		if(xNarrowFactor>2)
			xNarrowFactor = 2;

		yFactor = ((TReal)Targ.view.iBr.iX/(TReal)240);
		if(yFactor>2)
			yFactor = 2;
		
		for(loop = 0;loop<256;loop++)
		{
			TInt col = (loop*xNarrowFactor);
			TInt nextCol= ((loop+1)*xNarrowFactor);
		
			gNarrowColumnStepTable[loop] = nextCol-col;;						
		}
		
		for(loop = 0;loop<320;loop++)
		{
			TInt col = (loop*xFactor);
			TInt nextCol= ((loop+1)*xFactor);
		
			gColumnStepTable[loop] = nextCol-col;;
		}

		for(TInt loop = 0;loop<240;loop++)
		{
			gLineTable[loop] = currentLine;
			TInt line = (loop*yFactor);
			TInt nextLine = ((loop+1)*yFactor);
			currentLine+=(nextLine-line);
		}
		if(iScrMode == 2)
		{
			KBitValTable[0] = 4;
			KBitValTable[1] = 8;
			KBitValTable[2] = 2;
			KBitValTable[3] = 1;
			myPicoScan=EmulateScanFullRight16;
		}
		else
		{
			KBitValTable[0] = 8;
			KBitValTable[1] = 4;
			KBitValTable[2] = 1;
			KBitValTable[3] = 2;
			myPicoScan=EmulateScanFull16;
		}
#else
else if (iScrMode == 1)
{
		if(iInterpolate)
			myPicoScan=EmulateScanFull16_176Interpolate;
		else
			myPicoScan=EmulateScanFull16_176;

		for(TInt loop = 0;loop<240;loop++)
		{
			gLineTable[loop] = currentLine;
			if(((loop*3)/4) != (((loop+1)*3)/4))
				currentLine++;
		}
		KBitValTable[0] = 8;
		KBitValTable[1] = 4;
		KBitValTable[2] = 1;
		KBitValTable[3] = 2;
#endif	
	}
#ifndef S60V3
	else if(iScrMode==2)
	{
		if(iInterpolate)
			myPicoScan=EmulateScanFullRight16_176Interpolate;
		else
			myPicoScan=EmulateScanFullRight16_176;
		for(TInt loop = 0;loop<240;loop++)
		{
			gLineTable[loop] = currentLine;
			if(((loop*3)/4) != (((loop+1)*3)/4))
				currentLine++;
		}
		KBitValTable[0] = 4;
		KBitValTable[1] = 8;
		KBitValTable[2] = 2;
		KBitValTable[3] = 1;
	}
#endif
	else 
	{
#ifdef S60V3
		xFactor = ((TReal)Targ.view.iBr.iX/(TReal)320);
		xNarrowFactor = ((TReal)Targ.view.iBr.iX/(TReal)256);
		
		if(xFactor>2)
			xFactor = 2;
		
		if(xNarrowFactor>2)
			xNarrowFactor = 2;

		yFactor = ((TReal)Targ.view.iBr.iY/(TReal)240);

		if(yFactor>2)
			yFactor = 2;		
		
		for(loop = 0;loop<256;loop++)
		{
			TInt col = (loop*xNarrowFactor);
			TInt nextCol= ((loop+1)*xNarrowFactor);
		
			gNarrowColumnStepTable[loop] = nextCol-col;;						
		}
		
		for(loop = 0;loop<320;loop++)
		{
			TInt col = (loop*xFactor);
			TInt nextCol= ((loop+1)*xFactor);
		
			gColumnStepTable[loop] = nextCol-col;;
		}

		for(TInt loop = 0;loop<240;loop++)
		{
			gLineTable[loop] = currentLine;
			TInt line = (loop*yFactor);
			TInt nextLine = ((loop+1)*yFactor);
			currentLine+=(nextLine-line);
		}
		myPicoScan=EmulateStretchScan16;
#else
		if(iScrMode == 4)
		{
			myPicoScan=EmulateStretchScan16_320;
			iInterpolate = EFalse; // not needed
		}
		else
		{
		if(iInterpolate)
			myPicoScan=EmulateStretchScan16_176Interpolate;
		else
			myPicoScan=EmulateStretchScan16_176;
		}

		for(TInt loop = 0;loop<240;loop++)
		{
			gLineTable[loop] = currentLine;
			if(((loop*15)/16) != (((loop+1)*15)/16))
				currentLine++;
		}
#endif
		KBitValTable[0] = 1;
		KBitValTable[1] = 2;
		KBitValTable[2] = 4;
		KBitValTable[3] = 8;
	}
	
	if(iView)
		iView->SetRect(TRect(TPoint(0,0),iEikonEnv->ScreenDevice()->SizeInPixels()));
#ifdef S60V3
	switch(iScrMode)
	{
	case 0:
	case 3:
	case 4:
		iPutRect=TRect(TPoint(0,0),TSize(xFactor*320,yFactor*240));
		iPutPoint=TPoint(Targ.view.Size().iWidth/2-iPutRect.Size().iWidth/2,Targ.view.Size().iHeight/2-iPutRect.Size().iHeight/2);
		
		break;
	case 1:
	case 2:
		iPutRect=TRect(TPoint(0,0),TSize(yFactor*240,xFactor*320));
		iPutPoint=TPoint(Targ.view.Size().iWidth/2-iPutRect.Size().iWidth/2,Targ.view.Size().iHeight/2-iPutRect.Size().iHeight/2);
		break;

	}
#else
	switch(iScrMode)
	{
	case 0:
		iPutPoint=TPoint(0,20);
		iPutRect=TRect(TPoint(0,0),TSize(176,168));	
		break;
	case 1:
	case 2:
		iPutPoint=TPoint(4,1);
		iPutRect=TRect(TPoint(4,1),TSize(168,205));

		break;
	case 3:
	case 4:
		iPutPoint=TPoint(0,0);
		iPutRect=TRect(TPoint(0,0),TSize(176,208));
		break;
	}
#endif
	CalulateLineStarts();
	return 0;
}
#ifdef S60V3
#define KCenterOffset 8
#else
#define KCenterOffset 0
#endif
void CPicoDriveUi::CalulateLineStarts()
{
	switch(iScrMode)
	{
	case 0:
	case 3:
	case 4:
		{
			for(TInt loop = 0;loop<Targ.view.Size().iHeight;loop++)
			{
				gLineOffsets[loop] = loop*Targ.scanline_length;
			}
		}break;
	case 1:
		{		
			for(TInt loop = 0;loop<Targ.view.Size().iHeight;loop++)
			{
				gLineOffsets[loop] = loop*2+Targ.screen_offset+KCenterOffset;
			}
		}break;
	case 2:
		{
			for(TInt loop = 0;loop<Targ.view.Size().iHeight;loop++)
			{
				gLineOffsets[loop] = Targ.scanline_length-loop*2-KCenterOffset;
			}
		}break;	
	}
}

bool IsZip(char *filename)
{
	unsigned char buf[2];
	FILE *fp;
	
	if((fp=fopen(filename,"rb"))!=NULL)
	{
		fread(buf, 2, 1, fp);
		fclose(fp);
		return(memcmp(buf,"PK",2)==0);
	}
	if(fp)fclose(fp);
	return false;
}

int CPicoDriveUi::EmulateInit()
{
	FILE *f=NULL;
	EmulateExit(); // Make sure exited
	TargetInit(); // Find out where to put the screen
	PicoInit();
	// Load cartridge
	if(IsZip(RomName))
	{
		if(CartLoadZip(RomName,&RomData,&RomSize))
		{		
			return 1;
		}
	}
	else
	{
		// Load cartridge
		RFile file;
		TInt error = file.Open(iEikonEnv->FsSession(),iRomName,0);
		file.Close();
		f=fopen(RomName,"rb"); 
		if (f==NULL) 
			return 1;
		TInt result =PicoCartLoad(f,&RomData,&RomSize);
		fclose(f);
		if(result)
			return 1; // failed to load cart
	}
	
	TParsePtr parser(iRomName);
	TPtr8 ptr(Pico.rom_name,511);
	ptr.Copy(parser.DriveAndPath());
	ptr.Append(parser.Name());
	ptr.ZeroTerminate();

	PicoCartInsert(RomData,RomSize);
	Load_Patch_File();
	saveLoadGame(1, 1); // load sram if any saved

	if(!iEnableSixButtons)
		PicoOpt=PicoOpt& 223;
	else
		PicoOpt=PicoOpt|32;	
	return 0;
}

void CPicoDriveUi::EmulateExit()
{
	// Save sram if any
	if(RomData != NULL)
	{
		if(SRam.changed)
		{
		saveLoadGame(0,1);
		SRam.changed = 0;
		}

		Save_Patch_File();
		// Remove cartridge
		PicoCartInsert(NULL,0);
		PicoUnloadCart(RomData);
		RomData=NULL; RomSize=0;
	}
	PicoExit();
}

int CPicoDriveUi::InputFrame()
{
	if(PicoOpt & 3)
	{
		PsndOut=(short*)(iMonoSound.Ptr()+2*iCurrentSeg*PsndLen);
	}
	else
	{
		PsndOut=NULL;
	}

	Patch_Codes();

	PicoFrame();

	if(PicoOpt & 3)
	{
		iCurrentSeg++;
		if(iCurrentSeg==6)
		{
			iMonoSound.SetLength(PsndLen*2*6);
			iSndStream->WriteL(iMonoSound);
			iCurrentSeg=0;
		}
	}
	
	TCallBack callback(IdleCallBackStop,this);
	iIdleCallBack.Cancel();
	iIdleCallBack.Set(callback);
	iIdleCallBack.CallBack();
	CActiveScheduler::Start();
	
	
	PicoPad[0]=iPad1;
	return 0;
}



int CPicoDriveUi::EmulateFrame()
{
	int i=0,need=2;
	if (!iRomLoaded) 
		return 1;
	// Speed throttle:
	if(iFrameSkip ==-1) // auto skipping
	{
		int time=0,frame=0;
		TTime newtime;
		newtime.HomeTime();
#ifdef S60V3
		TInt64 tic=(newtime.Int64()/1000);
		time=(tic-LastSecond); // This will be about 0-1000 ms
#else
		TInt64 tic=(newtime.Int64()/1000).GetTInt();
		time=(tic-LastSecond).GetTInt(); // This will be about 0-1000 ms
#endif
		frame=time*FramesPerSecond/1000;
		need=frame-FramesDone;
		FramesDone=frame;
		
		if (FramesPerSecond>0)
		{
			// Carry over any >60 frame count to one second
			while (FramesDone>=FramesPerSecond) { FramesDone-=FramesPerSecond; LastSecond+=1000; }
		}
		
		if (need<=0) { 
			TTime nextTime;
			do
			{
			nextTime.HomeTime();
			}while((nextTime.Int64()-newtime.Int64())<15000);
		}
		if (need>10) need=10; // Limit frame skipping
	}
	else
	{
		need = iFrameSkip+1;
	}
	PicoSkipFrame=1;
	for (i=0;i<need-1;i++) InputFrame(); // Frame skip if needed
	PicoSkipFrame=0;

	// Now final frame is drawn:
	TBitmapUtil util(iBackBuffer);
	util.Begin(TPoint(0,0));
	Targ.screen=(unsigned char*)iBackBuffer->DataAddress();
	
	if (Targ.screen == NULL) 
	{
		util.End();
		return 1;
	}
	
	PicoScan=myPicoScan; // Setup scanline callback
	InputFrame();


	if(PicoOpt & 0x10) // need to render separatly
	{
		unsigned short* framebuffptr = framebuff+2632;

		if(!(Pico.video.reg[12]&1))
		{
			framebuffptr=framebuffptr-32;
		}

		TInt skipNext =0;
		for(TInt loop = 0;loop<224;loop++)
		{
			if(skipNext == 0)
			{
				skipNext = PicoScan(loop,framebuffptr);
			}
			else
				skipNext--;
			framebuffptr+=328;
		}
	}
	PicoScan=NULL;
	util.End();

	Targ.screen = NULL;
	
	iView->PutBitmap(iBackBuffer,iPutPoint,iPutRect);;
	
	return 0;
}


void CPicoDriveUi::MaoscOpenComplete(TInt aError)
{
	if(aError == KErrNone)
	{
		iSndStream->SetPriority(EPriorityMuchMore, EMdaPriorityPreferenceNone);
		iSndStream->SetVolume((iSndStream->MaxVolume()*iSoundVolume)/10);
		if(!UpdatePSndRate())
		{
			PsndRate = 8000;
			UpdatePSndRate();
		}
	}
	CActiveScheduler::Stop();
}

void CPicoDriveUi::MaoscBufferCopied(TInt /*aError*/, const TDesC8& /*aBuffer*/)
{
}

void CPicoDriveUi::MaoscPlayComplete(TInt aError)
{
	if(aError != KErrNone)
	{
		iSndStream->SetVolume((iSndStream->MaxVolume()*iSoundVolume)/10);
		UpdatePSndRate();
	}
}

TBool CPicoDriveUi::UpdatePSndRate()
{
	TInt sampleRate = TMdaAudioDataSettings::ESampleRate8000Hz;
	if(PsndRate == 11025)
		sampleRate = TMdaAudioDataSettings::ESampleRate11025Hz;
	else if (PsndRate == 16000)
		sampleRate = TMdaAudioDataSettings::ESampleRate16000Hz;
	else if (PsndRate == 22050)
		sampleRate = TMdaAudioDataSettings::ESampleRate22050Hz;

	TRAPD(err,iSndStream->SetAudioPropertiesL(sampleRate,TMdaAudioDataSettings::EChannelsMono));
	return (err == KErrNone);
}

size_t gzRead2(void *p, size_t _size, size_t _n, void *file)
{
	return gzread(file, p, _n);
}


size_t gzWrite2(void *p, size_t _size, size_t _n, void *file)
{
	return gzwrite(file, p, _n);
}



// this function is shared between both threads
int CPicoDriveUi::saveLoadGame(int load, int sram)
{
	int res = 0;
	
	if(!(iRomName.Length()>0)) return -1;
	
	// make save filename
	strcpy(saveFname,RomName);
	saveFname[KMaxFileName-5] = 0;
	if(saveFname[strlen(saveFname)-4] == '.') saveFname[strlen(saveFname)-4] = 0;
	strcat(saveFname, sram ? ".srm" : ".mds");
	
	if(sram) {
		int sram_size = SRam.end-SRam.start+1;
		if(SRam.reg_back & 4) sram_size=0x2000;
		if(!SRam.data) return 0; // SRam forcefully disabled for this game
		if(load) {
			PmovFile = fopen(saveFname, "rb");
			if(!PmovFile) return -1;
			fread(SRam.data, 1, sram_size, (FILE *) PmovFile);
			fclose((FILE *) PmovFile);
		} else {
			// sram save needs some special processing
			// see if we have anything to save
			for(; sram_size > 0; sram_size--)
				if(SRam.data[sram_size-1]) break;
				
				if(sram_size) {
					PmovFile = fopen(saveFname, "wb");
					res = fwrite(SRam.data, 1, sram_size, (FILE *) PmovFile);
					res = (res != sram_size) ? -1 : 0;
					fclose((FILE *) PmovFile);
				}
		}
		PmovFile = 0;
		return res;
	} else {
		// try gzip first
		//if(currentConfig.iFlags & 0x80) {
		strcat(saveFname, ".gz");
		if( (PmovFile = gzopen(saveFname, load ? "rb" : "wb")) ) {
			areaRead  = gzRead2;
			areaWrite = gzWrite2;
			if(!load) gzsetparams(PmovFile, 9, Z_DEFAULT_STRATEGY);
		} else
			saveFname[strlen(saveFname)-3] = 0;
		//	}
		
		if(!PmovFile) { // gzip failed or was disabled
			if( (PmovFile = fopen(saveFname, load ? "rb" : "wb")) ) {
				areaRead  = (arearw *) fread;
				areaWrite = (arearw *) fwrite;
			}
		}
		if(PmovFile) {
			PmovAction = load ? 6 : 5; // load/save
			PmovState();
			if(areaRead == gzRead2)
				gzclose(PmovFile);
			else fclose ((FILE *) PmovFile);
			PmovFile = 0;
		} else {
			res = -1;
		}
		
		return res;
	}
	
}

CQPicoDriveView::~CQPicoDriveView()
{
	iDsa->Cancel();
	delete iDsa;
}

void CQPicoDriveView::Restart(RDirectScreenAccess::TTerminationReasons /*aReason*/)
{
	if(iForeground)
	{
		iDsa->Cancel();
		iDsa->StartL();
		iDsa->Gc()->SetClippingRegion(iDsa->DrawingRegion());
		iDrawingOn=ETrue;
	}
}

void CQPicoDriveView::AbortNow(RDirectScreenAccess::TTerminationReasons /*aReason*/)
{
	iDsa->Cancel();
	iDrawingOn=EFalse;
}



void CQPicoDriveView::Draw(const TRect& aRect) const
{
	CWindowGc& gc=SystemGc();
	
	gc.SetBrushColor(KRgbBlack);
	gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
	gc.SetPenStyle(CGraphicsContext::ENullPen);
	gc.DrawRect(aRect);
	static_cast<CPicoDriveUi*>(iEikonEnv->EikAppUi())->StartAsynchUpdate();
}

void CQPicoDriveView::ConstructL()
{
	CreateWindowL();
	ActivateL();
	SetFocus(ETrue);
	SetRect(TRect(TPoint(0,0),iEikonEnv->ScreenDevice()->SizeInPixels())/*iEikonEnv->EikAppUi()->ClientRect()*/);
	iDsa=CDirectScreenAccess::NewL(iEikonEnv->WsSession(),*iEikonEnv->ScreenDevice(),Window(),*this);
	iDsa->StartL();
	iDsa->Gc()->SetClippingRegion(iDsa->DrawingRegion());
	iDrawingOn=ETrue;
	if(Window().DisplayMode() != EColor4K && Window().DisplayMode() != EColor64K)
	{
		Window().SetRequiredDisplayMode(EColor64K); // Try to set 64K color mode
	}
}
void CQPicoDriveView::Clear()
{
	CBitmapContext* gc;
	if(iDrawingOn)
	{
		gc=iDsa->Gc();
	}
	else
	{
		ActivateGc();
		gc=&SystemGc();
	}
	
	gc->SetBrushColor(KRgbBlack);
	gc->SetBrushStyle(CGraphicsContext::ESolidBrush);
	gc->SetPenStyle(CGraphicsContext::ENullPen);
	gc->DrawRect(Rect());	
	if(iDrawingOn)
	{
		iDsa->ScreenDevice()->Update();
		iEikonEnv->WsSession().Flush();
	}
	else
	{
		DeactivateGc();
	}
}

void CQPicoDriveView::DrawText(const TDesC& aText,TPoint aPoint,TBool aHighLight,TRgb aTextColour)
{
	CBitmapContext* gc;
	if(iDrawingOn)
	{
		gc=iDsa->Gc();
	}
	else
	{
		ActivateGc();
		gc=&SystemGc();
	}
	
	gc->SetBrushColor(KRgbBlack);
	gc->SetBrushStyle(CGraphicsContext::ESolidBrush);
	if(!aHighLight)
	{
		gc->SetPenColor(aTextColour);
	}
	else
	{
		gc->SetPenColor(KRgbRed);
	}
	gc->SetPenStyle(CGraphicsContext::ESolidPen);
	aPoint.iY+=iEikonEnv->NormalFont()->HeightInPixels()-2;
	aPoint.iX=Size().iWidth/2-iEikonEnv->NormalFont()->TextWidthInPixels(aText)/2;
	gc->UseFont(iEikonEnv->NormalFont());
	gc->DrawText(aText,aPoint);
	gc->DiscardFont();
	
	if(iDrawingOn)
	{
		iDsa->ScreenDevice()->Update();		
	}
	else
	{
		DeactivateGc();
	}
}

TInt CQPicoDriveView::DrawTextInRect(const TDesC& aText,TRect aRect,TInt aStartPos)
{
	CBitmapContext* gc;
	TInt pos = aStartPos;
	TInt len = aText.Length();
	if(iDrawingOn)
	{
		gc=iDsa->Gc();
	}
	else
	{
		ActivateGc();
		gc=&SystemGc();
	}
	
	gc->SetBrushColor(KRgbBlack);
	gc->SetBrushStyle(CGraphicsContext::ESolidBrush);
	
	gc->SetPenColor(KRgbWhite);
	
	gc->SetPenStyle(CGraphicsContext::ESolidPen);
	gc->UseFont(iEikonEnv->DenseFont());
	while(pos<len && aRect.iTl.iY<aRect.iBr.iY-(iEikonEnv->DenseFont()->HeightInPixels()+3))
	{
		TInt newline = aText.Right(len-pos).Locate('\n');
		if(newline == KErrNotFound)
			newline=(len-1)-pos;
		gc->DrawText(aText.Mid(pos,newline),aRect,iEikonEnv->DenseFont()->HeightInPixels());
		pos=pos+newline+1; // skip new line
		aRect.iTl+=TSize(0,iEikonEnv->DenseFont()->HeightInPixels()+3);
	}
	gc->DiscardFont();
	
	if(iDrawingOn)
	{
		iDsa->ScreenDevice()->Update();
	}
	else
	{
		DeactivateGc();
	}
	return pos;
}


void CQPicoDriveView::PutBitmap(CFbsBitmap* aBitmap,TPoint aPoint,TRect aRect)
{
	if(iDrawingOn)
	{
		//#ifdef __WINS__
		iDsa->Gc()->BitBlt(aPoint,aBitmap,aRect);
		iDsa->ScreenDevice()->Update();
		//#endif
		//iEikonEnv->WsSession().Flush();
	}
	else
	{
		ActivateGc();
		CWindowGc& gc=SystemGc();
		gc.BitBlt(aPoint,aBitmap,aRect);
		DeactivateGc();
	}
}



void Execute()
{
	__UHEAP_MARK;
	CTrapCleanup* cleanup = CTrapCleanup::New();
	
	// Create a eikenv
	CEikonEnv* eikenv = new CEikonEnv;
	if (!eikenv) 
	{
		return /*KErrNoMemory*/;
	}
	TRAPD(eikErr, eikenv->ConstructL());
	if (eikErr != KErrNone) 
	{
		delete eikenv;
		return /*eikErr*/;
	}
	CPicoDriveUi* appUi = new (ELeave) CPicoDriveUi;
	if (!appUi) 
	{
		delete eikenv;
		return /*KErrNoMemory*/;
	}
	
	TRAPD(constructErr,appUi->ConstructL());
	
	eikenv->SetAppUi(appUi);	// passing ownership of appUi to coe
	
	TInt leaveValue = KErrNone;	
	if (leaveValue != KErrNone)
	{
		delete eikenv;
	}
	else
	{
		// now accept request from clients (start the scheduler)
		eikenv->ExecuteD();
		//delete eikenv;							// ExecuteD kills eikenv
		
	}
	
	delete cleanup;
	
	__UHEAP_MARKEND;
}

//ARM build
#ifdef S60V3
CPicoDriveApp::CPicoDriveApp()
{
}

CPicoDriveApp::~CPicoDriveApp()
{
}

CApaDocument* CPicoDriveApp::CreateDocumentL()
{
	return new (ELeave) CPicoDriveDoc(*this);
}


TUid CPicoDriveApp::AppDllUid()const
{
	return TUid::Uid(0xA00007BE);
}

/**
   * From @c CApaApplication. Opens the .ini file associated with the
   * application. By default, ini files are not supported by SERIES60 
    * applications. If you want to use an ini file, either override this
    * function to base call @c CEikApplication::OpenIniFileLC, or call it
    * directly.
    * @param aFs File server session to use. Not used.
    * @return Pointer to the dictionary store object representing the
    * application's .ini file.
    */
CDictionaryStore* CPicoDriveApp::OpenIniFileLC(RFs& aFs) const
{
	return CEikApplication::OpenIniFileLC(aFs);
}

CPicoDriveDoc::CPicoDriveDoc(CEikApplication& aApp):CAknDocument(aApp)
{
}

CPicoDriveDoc::~CPicoDriveDoc()
{
}

CEikAppUi* CPicoDriveDoc::CreateAppUiL()
{
	return new (ELeave) CPicoDriveUi;
}

#ifdef S60V3
LOCAL_C
#endif
CApaApplication* NewApplication() {
	// Return pointer to newly created Application
	return new CPicoDriveApp;
}
#include <eikstart.h>
#endif
GLDEF_C TInt E32Main()
{
#ifdef S60V3	
 	return EikStart::RunApplication(NewApplication);
#else
	Execute();
#endif
	return KErrNone;
}
#if defined(__WINS__)

#ifndef S60V3
EXPORT_C TInt WinsMain()
{
	E32Main();
	return KErrNone;
}
#endif

extern "C" void my_free(void* anAddress)
{
	if(gChunk != NULL)
	{
		gChunk->Free(anAddress);
	}
}

extern "C" void* my_malloc(int aSize)
{
	if(gChunk != NULL)
	{
		return gChunk->Alloc(aSize);
	}
 return NULL;
}
#endif

