activegs-ios/Common/CEmulatorCtrl.cpp
2017-11-09 23:28:16 +01:00

2997 lines
69 KiB
C++

/*
ActiveGS, Copyright 2004-2016 Olivier Goguel, https://github.com/ogoguel/ActiveGS
Based on Kegs, Copyright 2004 Kent Dickey, https://kegs.sourceforge.net
This code is covered by the GNU GPL licence
*/
#include "CEMulatorCtrl.h"
#include "svnversion.h"
#include "../kegs/Src/adb.h"
#include "../kegs/Src/clock.h"
#include "../kegs/Src/moremem.h"
#include "../kegs/Src/paddles.h"
#include "../kegs/Src/sim65816.h"
#include "../kegs/Src/iwm.h"
#include "../kegs/Src/SaveState.h"
#include "../kegs/Src/raster.h"
#include "../kegs/Src/graphcounter.h"
#include "../kegs/Src/driver.h"
#include "../kegs/Src/video.h"
#include "../kegs/Src/async_event.h"
extern void joystick_shut();
#define CONFIG_VERSION 3
const int joyint[] = { (int)JOYSTICK_TYPE_NATIVE_1, (int)JOYSTICK_TYPE_KEYPAD, (int)JOYSTICK_TYPE_MOUSE, /* JOYSTICK_TYPE_NATIVE_2,*/ JOYSTICK_TYPE_NONE, JOYSTICK_TYPE_ICADE, -1 } ;
const char* joyalias[] = { "joystick1","keypad", "mouse", /*, "Joystick2",*/ "none", "iCade", NULL };
const char* joydesc[] = { "Joystick1","Keypad", "Mouse", /*, "Joystick2",*/ "None", "iCade", NULL };
const int colorint[] = { (int)COLORMODE_AUTO, (int)COLORMODE_BW, (int)COLORMODE_GREEN, (int)COLORMODE_AMBER,(int)COLORMODE_MONO, -1 };
const char* coloralias[] = { "auto", "bw", "green", "amber", "mono",NULL };
const char* colordesc[] = { "Colors", "B&W", "Green", "Amber", "Mono", NULL };
const char* videoalias[] = { "crt" , "lcd" ,"default", NULL };
const char* videodesc[] = { "CRT" , "LCD",NULL, NULL };
const int videoint[] = { (int)VIDEOFX_CRT, (int)VIDEOFX_LCD, (int)VIDEOFX_LCD, -1 };
const char* speeddesc[] = { "Normal", "Slow", "Zip", "Unlimited", NULL };
const char* speedalias[] = { "normal", "slow", "zip", "unlimited", NULL };
const int speedint[] = { (int)SPEED_GS, (int)SPEED_1MHZ, (int)SPEED_ZIP, (int)SPEED_UNLIMITED, -1 };
#define DEFAULT_AUDIO_RATE 44100
#define DEFAULT_LOCKZOOM 0
#define DEFAULT_DEBUGMODE 0
#define DEFAULT_DISKSOUND 1
#define DEFAULT_VIDEO VIDEOFX_CRT
const namevaluedef defoptions[] =
{
{ 1,0, "configVersion" , CONFIG_VERSION,NULL, NULL, NULL },
{ 1,0, "guid" , 0, NULL, NULL, NULL },
{ 1,1, "haltOnBadRead" , 0, NULL, NULL, NULL },
{ 1,1, "haltOnBadAcc" , 0, NULL, NULL, NULL },
{ 1,1, "haltOnHalts" , 0, NULL, NULL, NULL },
{ 1,1, "enableRewind" ,0, NULL, NULL, NULL },
{ 1,1, "colorMode" , 0, NULL, colordesc,coloralias , colorint },
{ 1,1, "videoFX", (int)DEFAULT_VIDEO, NULL, videodesc, videoalias , videoint },
{ 1,1, "joystickMode", 0, NULL, joydesc, joyalias, joyint },
{ 0,0, "browserName" , 0, NULL, NULL, NULL },
{ 0,0, "browserVer" , 0, NULL, NULL, NULL },
{ 2,0, "latestRunBuild" , 0, NULL, NULL, NULL },
{ 2,0, "latestUpdateCheck" , 0, NULL, NULL, NULL },
{ 1,0, "doNotShowUpdateAlert" , 0, NULL, NULL, NULL },
{ 0,0, "doNotShowDiskStatus" , 0, NULL, NULL, NULL },
{ 0,0, "PNGBorder" , 16, NULL, NULL, NULL },
{ 0,0, "simulateSpace" , 0X31, NULL, NULL, NULL }, // 0X31=SPACE KEY
{ 0,0, "latestVersion" , 0, NULL, NULL, NULL },
{ 0,0, "speed" , 0, NULL, speeddesc, speedalias, speedint },
{ 0,0, "latestVersionDate" , 0, NULL, NULL, NULL },
{ 1,1, "audioRate" , DEFAULT_AUDIO_RATE, NULL, NULL, NULL },
{ 1,1, "memorySize" , 2, NULL, NULL, NULL },
{ 0,0, "joyXScale" , 1000, NULL, NULL, NULL },
{ 0,0, "joyYScale" , 1000, NULL, NULL, NULL },
{ 1,1, "diskSound" , DEFAULT_DISKSOUND, NULL, NULL, NULL },
{ 0,1, "fixedVBlank" , 0, NULL, NULL, NULL },
{ 0,1, "adbDelay" , 45, NULL, NULL, NULL },
{ 0,1, "adbRate" , 3, NULL, NULL, NULL }
#ifdef DRIVER_IOS
,
{ 1,1, "frameRate" , 0, NULL, NULL, NULL },
{ 1,1, "lockZoom" , DEFAULT_LOCKZOOM, NULL, NULL, NULL },
{ 1,1, "lastTab" , 0, NULL, NULL, NULL },
{ 1,1, "debugMode" , DEFAULT_DEBUGMODE, NULL, NULL, NULL },
{ 1,1, "externalKBD" , 0, NULL, NULL, NULL }
#endif
#ifdef ACTIVEGSKARATEKA
,
{ 1,1, "showIcons" , 1, NULL, NULL, NULL },
{ 0,1, "enableCheats" , 0, NULL, NULL, NULL },
{ 1,1, "soundMode" , 0, NULL, NULL, NULL },
{ 1,1, "karatekaRewind" , 1, NULL, NULL, NULL },
#endif
#ifdef VIRTUALAPPLE
{ 1,1, "debug" , 0, NULL, NULL, NULL },
{ 1,1, "scores" , 0, NULL, NULL, NULL },
{ 1,1, "gamecenter" , 0, NULL, NULL, NULL },
#endif
};
COption option;
const char* COption::getDescription(option_id _id, int _value)
{
namevalue& nv = find(_id);;
return nv.getDescription(_value);
}
int namevalue::belongsToValue(int _v)
{
if (!def->convertTableInt) return 1;
int i=0;
while(def->convertTableInt[i]!=-1)
{
if (def->convertTableInt[i]==_v)
return 1;
i++;
}
return 0;
}
int namevalue::getOrder(int _value)
{
int p = 0;
while(def->convertTableInt[p] != -1)
{
if (def->convertTableInt[p]==_value)
return p;
p++;
}
return -1;
}
const char* namevalue::getDescription(int _value)
{
const char* ret = "n/a";
if (!def->convertTableInt) return ret;
int o = getOrder(_value);
if (o<0) return ret;
return def->convertTableDesc[o];
}
CEmulator* CEmulator::theEmulator = NULL;
char bram_default[]={
(char)0x00, (char)0x00, (char)0x00, (char)0x01, (char)0x00, (char)0x00, (char)0x0D, (char)0x06, (char)0x02, (char)0x01, (char)0x01, (char)0x00, (char)0x01, (char)0x00, (char)0x00, (char)0x00,
(char)0x00, (char)0x00, (char)0x07, (char)0x06, (char)0x02, (char)0x01, (char)0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x0F, (char)0x06, (char)0x06, (char)0x00, (char)0x05, (char)0x06,
(char)0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x03, (char)0x02, (char)0x02, (char)0x02,
(char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x01, (char)0x02, (char)0x03, (char)0x04, (char)0x05, (char)0x06,
(char)0x07, (char)0x00, (char)0x00, (char)0x01, (char)0x02, (char)0x03, (char)0x04, (char)0x05, (char)0x06, (char)0x07, (char)0x08, (char)0x09, (char)0x0A, (char)0x0B, (char)0x0C, (char)0x0D,
(char)0x0E, (char)0x0F, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF,
(char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF,
(char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF,
(char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF,
(char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF,
(char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF,
(char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF,
(char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF,
(char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF,
(char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF,
(char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFD, (char)0x96, (char)0x57, (char)0x3C
};
void updateBramCheckSum(unsigned char* buf) // original code in FF/B61D
{
register short int crc=0; // LDA #0
int i; // CLC
for(i=255-4-1;i>=0;i--) // LDX #FA
{
crc = (crc << 1) | ((crc>>15) & 1); // ROL (1-bits)
#ifdef UNDER_CE
crc += buf[i];
crc += buf[i+1]*256;
#else
crc += *(unsigned short*)(buf+i); // ADC bram,X
#endif
} // DEX
// CPX #FF
// BNE Loop
*(unsigned short*)(buf+252) = crc; // TAX
*(unsigned short*)(buf+254) = crc ^0xAAAA; // EOR #AAAA
}
void initializeBram(unsigned char* _bram_ptr)
{
memcpy(_bram_ptr,bram_default,256);
updateBramParameters(_bram_ptr);
updateBramCheckSum(_bram_ptr);
}
void COption::generateGUID(MyString& guid)
{
int crc = calcCRC(CDownload::getPersistentPath());
time_t now;
time( &now );
int lowtime = (int)now;
guid.Format("%08X%08X",crc,lowtime);
}
void CEmulatorConfig::resetConfig()
{
speed = SPEED_AUTO;
bootslot = -1; //auto
// fastConfig = false;
// pXML=NULL;
// vire tous les slots
for(int i=0;i<3;i++)
{
for(int m=0;m<MAXSLOT7DRIVES;m++)
{
activeImages[i][m]=CActiveImage();
for(int a=0;a<ACTIVEGSMAXIMAGE;a++)
localImages[i][m][a]=CSlotInfo();
}
}
xmlSystemParam.Empty();
xmlEmulatorParam.Empty();
xmlAlreadyLoaded = 0;
}
int namevalue::convertToInt(const char* _value)
{
if (!def->convertTableInt) return 0;
int i=0;
const char* str;
while( (str=def->convertTableAlias[i]) != NULL )
{
if(!strcasecmp(_value,str))
return def->convertTableInt[i];
i++;
}
printf("err: could not convert %s : returning %s (%d)\n",_value,def->convertTableAlias[0],def->convertTableInt[0]);
return def->convertTableInt[0];
}
option_id COption::findByName(const char* _name)
{
if (!_name) return (option_id)-1;
for (int i=0;i<NB_OPTIONS;i++)
{
if (!strcasecmp(_name,options[i].def->name))
return (option_id)i;
}
#ifdef _DEBUG
printf("cannot find option : %s\n",_name);
#endif
return (option_id)-1;
}
void COption::setValue(option_id _id,const char* _value,int _confOverrided)
{
namevalue& nv = find(_id);
int isnum=1;
const char*p=_value;
while(*p)
{
if (*p<'0' || *p>'9')
{
isnum=0;
break;
}
p++;
}
int v;
if (isnum)
v=atoi(_value);
if (!nv.def->convertTableInt)
{
if (!isnum)
{
nv.strvalue = _value;
printf("> option %s: %s\n",nv.def->name,_value);
}
else
{
nv.intvalue = v;
printf("> option %s: (%d)\n",nv.def->name,nv.intvalue);
}
nv.confOverrided = _confOverrided;
}
else
{
if (isnum && nv.belongsToValue(v))
{
nv.intvalue = v;
nv.confOverrided = _confOverrided;
printf("> option %s: %s (%d)\n",nv.def->name,nv.getDescription(v),nv.intvalue);
}
else
{
// regarde si c'est une valeur en texte?
int pos=0;
const char* s;
while( (s = nv.def->convertTableAlias[pos]) != NULL )
{
if (!strcasecmp(s,_value))
{
nv.intvalue = nv.def->convertTableInt[pos];
nv.confOverrided = _confOverrided;
printf("* option %s: %s (%d)\n",nv.def->name,_value,nv.intvalue);
return ;
}
pos++;
}
printf("non supported value:%s for option:%s\n",_value,nv.def->name);
}
}
}
void namevalue::getAlias(MyString& _str)
{
if (this->strvalue.IsEmpty())
{
// convert to int first
int v = this->intvalue;
if (!this->def->convertTableInt)
_str.Format("%d",v);
else
{
int i = getOrder(v);
_str = this->def->convertTableAlias[i];
}
return ;
}
else
_str = this->strvalue;
}
void COption::initOptions()
{
for(int i=0;i<NB_OPTIONS;i++)
{
options[i].intvalue = defoptions[i].defaultint;
options[i].strvalue = defoptions[i].defaultstr;
}
initiliazed = 1;
setDefaultOptions();
}
void COption::setDefaultOptions()
{
if (!initiliazed)
x_fatal_exit("option not initialized");
for(int i =0;i<NB_OPTIONS;i++)
{
options[i].def = &defoptions[i];
if (options[i].def->inDefault)
{
if (options[i].def->convertTableInt)
options[i].intvalue =options[i].def->convertTableInt[0];
else
{
options[i].intvalue = options[i].def->defaultint;
options[i].strvalue = options[i].def->defaultstr;
}
}
}
if (g_driver.x_apply_default_options!=NULL)
g_driver.x_apply_default_options((void*)this);
printf("*restored default options\n");
}
void COption::setIntValue(option_id _id, int _v, int _confOverrided)
{
MyString str;
namevalue& nv =find(_id);
if (!nv.belongsToValue(_v))
{
printf("err: unrecognized value %d for %s\n",_v,nv.def->name);
return ;
}
nv.intvalue = _v;
if (nv.def->convertTableInt)
printf("option %s set to %s (%d)\n",nv.def->name,nv.getDescription(_v),_v);
else
printf("option %s set to (%d)\n",nv.def->name,_v);
nv.confOverrided = _confOverrided;
}
void COption::getStrValue(MyString& _str,option_id _id)
{
namevalue& nv =find(_id);
if (nv.strvalue.IsEmpty())
{
if (nv.def->convertTableInt)
_str = nv.getDescription(nv.intvalue);
else
_str.Format("%d",nv.intvalue);
}
else
_str = nv.strvalue.c_str();
}
int COption::getIntValue(option_id _id)
{
namevalue& nv =find(_id);
if (!nv.def->convertTableInt || nv.strvalue.IsEmpty())
return nv.intvalue;
else
{
printf("getintvalue: mismatched type for %s\n",nv.def->name);
return 0;
}
}
void COption::saveOptions(int _nb)
{
MyString filename;
getParameterFilename(filename,_nb);
FILE* f = fopen(filename.c_str(),"wt");
if (!f)
{
outputInfo("cannot create %s for saving options\n",filename.c_str());
return ;
}
for(int i=0;i<NB_OPTIONS;i++)
{
if (options[i].def->inConfig == _nb)
{
MyString line;
MyString desc;
options[i].getAlias(desc);
line.Format("%s:%s\n",options[i].def->name,desc.c_str());
fputs(line.c_str(),f);
}
}
fclose(f);
//outputInfo("options saved (%s)\n",filename.c_str());
enableOptions();
}
void COption::loadOptions()
{
int b = loadParameters(1);
if (!b)
{
// si il n'y avait pas de fichier conf - rajoute le guid au option par défaut et sauve le
MyString guid;
generateGUID(guid);
setValue(OPTION_GUID,guid.c_str());
saveOptions(1);
}
// vérifie le numéro de version
int v = getIntValue(OPTION_CONFIGVERSION);
if (v < CONFIG_VERSION)
{
// réinitialise
initOptions();
setIntValue(OPTION_CONFIGVERSION,CONFIG_VERSION);
printf("upgrading config file to v" TOSTRING(CONFIG_VERSION));
saveOptions(1);
}
loadParameters(2);
}
void COption::getParameterFilename(MyString& filename,int _nb)
{
filename = CDownload::getPersistentPath();
filename += ACTIVEGS_DIRECTORY_SEPARATOR ;
filename += "activegs";
if (_nb==1)
filename += ".conf";
else
filename += ".ext";
}
int COption::loadParameters(int _nb)
{
MyString filename;
getParameterFilename(filename,_nb);
FILE* f = fopen(filename,"rt");
if (f)
{
outputInfo("loadOptions from %s\n",filename.c_str());
char line[1025];
line[1024]=0;
while(fgets(line,1024,f))
{
int i=0;
while(line[i]!=':' && line[i])i++;
if (line[i]==':')
{
line[i++]=0;
option_id id = findByName(line);
if (id == OPTION_ERROR)
outputInfo("option %s ignored\n",line);
else
{
int vpos = i;
while( line[i]
&&
(line[i]!='\t')
&&
(line[i]!='\r')
&&
(line[i]!='\n')
/*
&&
(line[i]!=' ') */) i++;
if (line[vpos])
{
// trim
while(line[i-1]==' ')i--;
line[i]=0;
setValue(id,line+vpos);
}
}
}
}
fclose(f);
return 1;
}
else
return 0;
}
CEmulatorConfig::CEmulatorConfig()
{
xmlAlreadyLoaded = 0;
pXML=NULL;
bramParam.Empty();
emulatorParam.Empty();
nb_bps=0;
resetConfig();
}
void CEmulatorConfig::setBaseURL(const char* _url)
{
MyString p = _url;
if (!strncasecmp(p.c_str(),"file://localhost/",17))
baseURL = p.substr(16,p.length()-16); // absolute path OG 250810 - leave the trailing slash
else
if (!strncasecmp(p.c_str(),"file:///",8))
#ifdef WIN32
baseURL = p.substr(8,p.length()-8); // absolute path
#else
baseURL = p.substr(7,p.length()-7); // keep the / on mac
#endif
else
if (!strncasecmp(p.c_str(),"file://",7))
baseURL = p.substr(7,p.length()-7); // absolute path
else
baseURL = p;
baseURL.Replace("%20"," ");
printf("baseURL set to %s\n",baseURL.c_str());
/*
CDownload dl;
dl.setBaseURL(baseURL.c_str());
*/
}
CSlotInfo& CEmulatorConfig::getLocalImage(int _slot, int _drive, int _pos)
{
return localImages[_slot-5][_drive-1][_pos];
}
CActiveImage& CEmulatorConfig::getActiveImage(int _slot, int _drive)
{
return activeImages[_slot-5][_drive-1];
}
CSlotInfo* CEmulatorConfig::setSlot(int _slot, int _disk, const char * _url_normalized, enum eSetSlotPos _pos)
{
if (_slot<5 || _slot > 7 || _disk < 1 || (_slot==7 && _disk > MAXSLOT7DRIVES) || (_slot!=7 && _disk > 2) )
{
printf("invalid slot : %d:%d\n",_slot,_disk);
return NULL;
}
// normalise pour garder la compatibilité avec les version < 3.5
MyString url = _url_normalized;
int pos = url.ReverseFind('?');
if (pos != -1 )
{
int i = url.at(pos+1);
size_t l = url.length();
if (l==(pos+2) && i>='0' && i<='9')
{
url = url.Left(pos);
url += "#pos=";
url += i;
}
}
const char* _url = url.c_str();
if (_url && !_url[0]) _url=0;
CActiveImage& ai = activeImages[_slot-5][_disk-1 ];
int active=0;
if (_pos==REPLACECURRENT) // posactive
{
active = ai.iActive;
if (!ai.iNb)
{
// ignore SetSlot = NULL si aucun disk n'avait été préalablement chargé
if (!_url) return NULL;
ai.iNb=1;
}
}
else
// if (_pos==ADD)
{
// regarde si le disk était déjà présent dans la liste
for(int i =0;i<ai.iNb;i++)
{
CSlotInfo& ref = localImages[_slot-5][_disk-1 ][i];
if (!ref.url.CompareNoCase(_url))
{
printf("image %s already in the list... ignoring!\n",_url);
return NULL ;
}
}
active = ai.iNb++;
}
CSlotInfo& ref = localImages[_slot-5][_disk-1 ][active];
if (ref.status == MOUNTED || _url )
{
if (!_url)
{
ref.status = EJECTED ;
outputInfo("status set to EJECTED for S%dD%d\n",_slot,_disk);
}
else
{
size_t len = strlen(_url);
if (len>2048)
{
// overflow!
outputInfo("status set to FAILED(invalid length) for S%dD%d\n",_slot,_disk);
ref.status = FAILED;
}
else
{
ref.url = _url;
ref.url.Trim(); // OG Fix ending space
ref.shortname = getfile(_url);
ref.status = READY2MOUNT;
outputInfo("status set to READY2MOUNT for S%dD%d, image: %s\n",_slot,_disk,ref.shortname.c_str());
}
}
need2remount=true;
x_refresh_panel(PANEL_DISK);
}
return &ref;
}
const char* CEmulatorConfig::getSlot(int _slot, int _disk)
{
if (_slot<5 || _slot > 7 || _disk < 1 || _disk > MAXSLOT7DRIVES)
return NULL;
CActiveImage active = activeImages[_slot-5][_disk-1 ];
CSlotInfo& ref = localImages[_slot-5][_disk-1 ][active.iActive];
return STRING2CONSTCHAR(ref.url);
}
void CEmulatorConfig::processCommandLine(int argc, char** argv)
{
for(int i=1;i<argc;i++)
{
bool bOption = argv[i][0]=='-';
if (bOption)
{
const char* option = argv[i]+1;
if (!strcasecmp(option,"help"))
{
#if defined(WIN32) && !defined(UNDER_CE)
::MessageBox(NULL,"ActiveGS.exe [<filename>.activegsxml|<filename.2mg>|<filename.dsk>]", ACTIVEGSVERSIONSTRFULL,MB_OK);
::PostQuitMessage(0);
#endif
}
else
if ((i+1)<argc)
{
const char* value = argv[++i];
if (!strcasecmp(option,"slot51"))
this->setSlot(5,1,value,ADD);
else
if (!strcasecmp(option,"slot52"))
this->setSlot(5,2,value,ADD);
else
if (!strcasecmp(option,"slot61"))
this->setSlot(6,1,value,ADD);
else
if (!strcasecmp(option,"slot62"))
this->setSlot(6,2,value,ADD);
else
if (!strcasecmp(option,"slot71"))
this->setSlot(7,1,value,ADD);
else
if (!strcasecmp(option,"slot72"))
this->setSlot(7,2,value,ADD);
else
if (!strcasecmp(option,"BootSlot"))
{
int bs = atoi(value);
this->bootslot = bs;
}
else
if (!strcasecmp(option,"Speed"))
{
int s = atoi(value);
this->speed=s;
}
else
if (!strcasecmp(option,"EmulatorParam"))
this->emulatorParam=value;
else
if (!strcasecmp(option,"SystemParam"))
this->systemParam=value;
else
if (!strcasecmp(option,"BramParam"))
this->bramParam=value;
else
if (!strcasecmp(option,"XMLConfig"))
this->xmlconfig=value;
else
if (!strcasecmp(option,"state"))
{
CDownload dl(this->baseURL);
// dl.setBaseURL(_path);
MyString path;
MyString shortn;
dl.retrieveFile(value,path,shortn);
this->stateFilename=path.c_str();
}
}
}
else
{
// vérifie l'extension (mais strip le #)
MyString extv(argv[i]);
int l = extv.ReverseFind("#");
if (l>0)
extv = extv.Left(l);
const char* ext = getext(extv.c_str());
if ( !strcasecmp(ext,"activegsxml") || !strcasecmp(ext,"xml"))
{
this->xmlconfig = argv[i] ;
}
else
{
MyString path;
MyString file;
// CDownload dl;
int slot = 0;
// dl.deleteCachedFile(argv[i]);
CDownload dl(this->baseURL);
size_t size = dl.retrieveFile(argv[i],path,file);
ext = getext(path.c_str());
if (!strcasecmp(ext,"hdv"))
{
slot = 7;
}
else
if (!strcasecmp(ext,"2mg") || !strcasecmp(ext,"po"))
{
if (size> 819712) // OG Fix 2MG size recognitioN (CF. RASTAN 819264 & POMS 7 819712)
slot = 7;
else
slot = 5;
}
else
if ( !strcasecmp(ext,"bin") || !strcasecmp(ext,"dsk") || !strcasecmp(ext,"nib") || !strcasecmp(ext,"po") || !strcasecmp(ext,"do") )
slot = 6;
if (slot)
{
setSlot(slot,1,path,REPLACECURRENT);
// bootslot = slot; OG Laisse la valeur par défaut!
xmlconfig.Empty();
}
else
outputInfo("Unknown extension .%s\n",ext);
}
}
}
}
int CEmulatorConfig::processXML(const char*_path,simplexml* ptrconfig)
{
const char* ver = ptrconfig->property("version");
if (!ver || atoi(ver)!=2)
{
outputInfo("invalid xml version - xml config skipped\n");
return 0;
}
int child = ptrconfig->number_of_children();
for(int i=0;i<child;i++)
{
simplexml* ptr = ptrconfig->child(i);
if (!strcasecmp(ptr->key(),"uid"))
{
const char* s = ptr->value();
uid = s;
}
else
if (/*!fastConfig && */!strcasecmp(ptr->key(),"image"))
{
int slot = atoi(ptr->property("slot"));
int disk = atoi(ptr->property("disk"));
if (
( ( slot==5 || slot==6 ) && ( disk==1 || disk==2) )
||
( (slot==7) && ( disk>=1 || disk<=MAXSLOT7DRIVES) )
)
{
MyString absp ;
const char* s = ptr->value();
int patch_addr = 0;
const char* patch_value = NULL;
for(int idisk=0;idisk<ptr->number_of_children();idisk++)
{
simplexml* ptrd = ptr->child(idisk);
if (!strcmp(ptrd->key(),"name"))
s = ptrd->value();
else
if (!strcmp(ptrd->key(),"patch"))
{
const char* addr_str = ptrd->property("addr");
if (addr_str)
{
sscanf(addr_str,"%X",&patch_addr);
patch_value = ptrd->value();
if (strlen(patch_value)&1)
patch_value=NULL; // doit être pair
}
}
}
if (s)
{
// CDownload dl(this->baseURL);
CDownload::makeAbsolutePath(s,_path,absp);
CSlotInfo* pslot = setSlot(slot,disk,absp.c_str(),ADD);
if (pslot && patch_value && patch_addr)
{
pslot->patch_addr = patch_addr;
pslot->patch_value = patch_value;
pslot->patch_value.MakeUpper();
}
}
}
}
else
if (/*!fastConfig && */!strcasecmp(ptr->key(),"bootslot"))
{
int slot = atoi(ptr->value());
if (slot>=5 && slot<=7)
{
bootslot = slot;
}
}
else
if (!strcasecmp(ptr->key(),"runtime"))
{
const char* mode_str = ptr->property("mode");
if (mode_str)
{
enum mode_breakpoint mode = BRK_DISABLED;
if (strchr(mode_str,'X'))
mode = (enum mode_breakpoint)(mode|BRK_X);
if (strchr(mode_str,'R'))
mode = (enum mode_breakpoint)(mode|BRK_READ);
if (strchr(mode_str,'W'))
mode = (enum mode_breakpoint)(mode|BRK_WRITE);
if (strchr(mode_str,'P'))
mode = (enum mode_breakpoint)(mode|BRK_PATCH);
if (strchr(mode_str,'B'))
mode = (enum mode_breakpoint)(mode|BRK_STOP);
if (strchr(mode_str,'S'))
mode = (enum mode_breakpoint)(mode|BRK_SLOWSPEED);
if (strchr(mode_str,'D'))
mode = (enum mode_breakpoint)(mode|BRK_DEFAULTSPEED);
if (strchr(mode_str,'M'))
mode = (enum mode_breakpoint)(mode|BRK_MESSAGE);
const char* addr_str = ptr->property("addr");
if (addr_str && !(strlen(addr_str)&1))
{
word32 patch_addr;
sscanf(addr_str,"%X",&patch_addr);
const char* value_str = ptr->value();
if (value_str == NULL || !(strlen(addr_str)&1))
{
int bp = nb_bps;
if (bp != MAX_BREAK_POINTS)
{
const char* check = ptr->property("check");
memset(BPs[bp].check,0,MAX_CHECK);
if (check!=NULL)
{
// printf("found check:%s\n",check);
int i=0;
while(i<MAX_CHECK-3) // 3x 00 A LA FIN
{
char c1 = *check++;
if (!c1) break; // done
char c2 = *check++;
if (!c2) break; // misformed
if (c1>='a')
c1 -= ('a'-10);
else
if (c1>='A')
c1 -= ('A'-10);
else
c1 -= '0';
if (c2>='a')
c2 -= ('a'-10);
else
if (c2>='A')
c2 -= ('A'-10);
else
c2 -= '0';
BPs[bp].check[i++]= c1*16+c2;
}
}
BPs[bp].addr= patch_addr;
BPs[bp].value = value_str;
BPs[bp].mode = mode;
nb_bps++;
}
}
}
}
}
else
if (!strcasecmp(ptr->key(),"speed"))
{
int sp = atoi(ptr->value());
if (sp>=0 && sp<SPEED_ENUMSIZE)
{
speed = sp;
}
}
else
if (!strcasecmp(ptr->key(),"bramparam"))
{
const char* s = ptr->value();
xmlBramParam = s;
}
else
if (!strcasecmp(ptr->key(),"emulatorparam"))
{
const char* s = ptr->value();
xmlEmulatorParam = s;
}
else
if (!strcasecmp(ptr->key(),"systemparam"))
{
const char* s = ptr->value();
xmlSystemParam = s;
}
else
if (!strcasecmp(ptr->key(),"name"))
{
const char* s = ptr->value();
name = s;
}
else
if (!strcasecmp(ptr->key(),"desc"))
{
const char* s = ptr->value();
desc = s;
}
else
if (!strcasecmp(ptr->key(),"publisher"))
{
const char* s = ptr->value();
publisher = s;
}
else
if (!strcasecmp(ptr->key(),"year"))
{
const char* s = ptr->value();
year = s;
}
if (!strcasecmp(ptr->key(),"state"))
{
const char* s = ptr->value();
CDownload dl(_path); // ???
MyString path;
MyString shortn;
dl.retrieveFile(s,path,shortn);
stateFilename=path.c_str();
}
}
return 1;
}
void CEmulatorConfig::loadXMLFile()
{
if (xmlconfig.IsEmpty() && !pXML)
{
xmlEmulatorParam.Empty();
xmlSystemParam.Empty();
xmlBramParam.Empty();
return ;
}
if (xmlAlreadyLoaded)
{
printf("discard xml...\n");
return ;
}
resetConfig();
xmlAlreadyLoaded = 1;
// If XML already present, use preparsed structrue
if (pXML)
{
processXML(baseURL.c_str(),pXML);
return ;
}
MyString fullpath;
MyString fulldir;
//CDownload dl;
CDownload::makeAbsolutePath(STRING2CONSTCHAR(xmlconfig),baseURL.c_str(),fullpath);
getdir(fullpath.c_str(),fulldir);
xmlconfig = fullpath.c_str() ;
ActiveGSList list;
if (!list.loadFromFile(STRING2CONSTCHAR(xmlconfig)))
return ;
simplexml* ptrconfig = NULL;
if (list.elements.size()==1)
ptrconfig = list.elements.at(0).pXML;
else
{
CStdStringA xmlconfig2 = xmlconfig;
int pos = xmlconfig2.ReverseFind("#id=");
if (pos==-1)
{
printf("No key requested with .activegsxml list format\n");
return ;
}
MyString key = xmlconfig2.Mid(pos+4);
for(unsigned int i=0;i<list.elements.size();i++)
{
if (list.elements.at(i).theid == key)
{
ptrconfig = list.elements.at(i).pXML;
break;
}
}
if (!ptrconfig)
{
printf("id: %s not found in %s\n",key.c_str(),STRING2CONSTCHAR(xmlconfig));
return ;
}
}
ASSERT(ptrconfig);
processXML(fulldir.c_str(),ptrconfig);
/*
delete root;
delete s;
*/
}
//void geturldomain(const char* p,MyString& _domain);
void CEmulator::sendStats()
{
/*
#pragma message("TODO refaire les stats!")
#ifndef DRIVER_IOS
MyString str="http://analytics.freetoolsassociation.com/analytics.php?";
const char* app =ACTIVEGSAPP ;
str +="&p=";
str += app;
str +="&b=";
const char* ver = TOSTRING(ACTIVEGSMAJOR) "." TOSTRING(ACTIVEGSMINOR) "." TOSTRING(ACTIVEGSBUILD);
str += ver;
str +="&n=";
MyString browser; option.getStrValue(browser,OPTION_BROWSERNAME);
str += browser.substr(0,4);
str +="&v=";
MyString browseroption; option.getStrValue(browseroption,OPTION_BROWSERVERSION);
str += browseroption;
str +="&g=";
MyString guid; option.getStrValue(guid,OPTION_GUID);
str += guid;
MyString domain;
geturldomain(config->baseURL.c_str(),domain);
str += "&d=";
str += domain.c_str();
#ifdef ACTIVEGSPLUGIN
CDownload dl;
dl.GetFile(str.c_str(),NULL);
#endif
#endif
*/
}
void CEmulator::setConfig(CEmulatorConfig* _config)
{
config = _config;
sendStats();
config->loadXMLFile();
/*
CDownload dl;
dl.setBaseURL(config->baseURL.c_str());
*/
}
char read2MG(const char* ref,int block, int byte)
{
return ref[0x40+block*512+byte];
}
int find2MG(const char* ref,int block, char header, const char* str)
{
int byte = 4;
while (1)
{
char r = read2MG(ref,block,byte);
if (!r) return 0;
if (r==header)
{
int c=0;
while(str[c])
{
if (read2MG(ref,block,byte+c+1)!=str[c]) break;
c++;
}
if (!str[c])
{
// found!
return block*512+byte;
}
}
byte+=0x27;
if (byte==0x1FF)
{
byte =4;
block = read2MG(ref,block,2) + read2MG(ref,block,3)*256;
if (!block) return 0;
}
}
return 0;
}
int findSetStart(const char* buffer)
{
// start dir
int sys = find2MG(buffer,2,(char)0xD6,"SYSTEM");
if (!sys)
{
outputInfo("cannot find System dir!");
return false;
}
int sysbloc = buffer[sys+0x40+0x11]+buffer[sys+0x40+0x12]*256;
int setstart = find2MG(buffer,sysbloc,0x1D,"SETSTART.DATA");
if (!setstart)
{
outputInfo("cannot find setstart file");
return false;
}
return setstart;
}
bool CEmulator::setStartParam()
{
// prend le nom du fichier en 71
int slot = setStart.slot/10;
int drive = setStart.slot%10;
CSlotInfo& ref = getLocalIMGInfo(slot,drive);
const char* filename= ref.filename.c_str(); //getLocalIMG(setStart.slot/10,setStart.slot%10);
if (!filename) return false;
FILE* f=fopen(filename,"rb");
if (!f) return false;
// get file size
fseek(f,0,SEEK_END);
int filesize=ftell(f);
char* buffer=(char*)malloc(filesize);
fseek(f,0,SEEK_SET);
fread(buffer,1,filesize,f);
fclose(f);
//
int offset = findSetStart(buffer);
if (!offset)
{
free(buffer);
return false;
}
char *entry = buffer+offset+0x40;
/*
char *entry = buffer+setStart.addr;
*/
if ((entry[0]!=0x1D) || (strcmp(entry+1,"SETSTART.DATA")) )
{
free(buffer);
return false;
}
// buffer + 17 = block
int blockpos = (entry[17]+entry[18]*256)*512+64;
// buffer + 19 = size
int size = setStart.file.length();
entry[0x15]=(size+3)&0xFF;
entry[0x16]=((size+3)>>8)&0xFF;
entry[0x17]=0;
char* block = buffer+blockpos;
memset(block,0,256);
block[0]=size&0xFF;
block[1]=(size>>8)&0xFF;
strcpy(block+2,setStart.file.c_str());
block[size+2]=0x31; // ????
f=fopen(filename,"wb");
if (!f)
{
free(buffer);
return false;
}
fseek(f,0,SEEK_SET);
fwrite(buffer,1,filesize,f);
fclose(f);
free(buffer);
struct param p;
p.data = "SetStart";
p.value = setStart.file;
p.hex = offset+0x40; //setStart.addr;
addUniqueParam(p);
// params.push_back(p);
x_refresh_panel(PANEL_PARAMS);
return true;
}
int CEmulator::acceptEvents()
{
return r_sim65816.get_state()==RUNNING;
}
void CEmulator::onEmulatorParamChanged(const char* _param)
{
if (!acceptEvents())
{
outputInfo("### onEmulatorParamChanged Ignored");
return;
}
option.addEmulatorParam(_param);
setEmulatorParam(_param);
option.enableOptions();
}
void CEmulator::onBootSlotChanged(int _bootslot)
{
if (!acceptEvents())
{
outputInfo("### OnBootSlotChanged Ignored");
return;
}
config->bootslot = _bootslot;
relaunchEmulator();
}
void CEmulator::relaunchEmulator()
{
outputInfo("Trying to relaunch Emulator()\n");
if (this !=theEmulator)
{
outputInfo("Another instance was running in another process : killing it()\n");
delete theEmulator;
}
forceFocus();
if (bInThread)
{
outputInfo("Asking for current instance to restart\n");
r_sim65816.restart_emulator();
}
else
{
outputInfo("reactivate emulator!\n");
launchEmulator();
}
}
void CEmulator::onSpeedChanged(int _speed, bool _ignoreAcceptEvents)
{
if (!_ignoreAcceptEvents && !acceptEvents())
{
outputInfo("### onSpeedChanged Ignored");
return;
}
/*
int sp = g_sim65816.set_limit_speed((speedenum)_speed);
config->speed = sp;
*/
option.setIntValue(OPTION_SPEED,_speed);
option.enableOptions();
forceFocus();
}
void CEmulator::addUniqueParam(struct param _p)
{
int i;
for(i=0;i<nbparams;i++)
if (params[i].data == _p.data)
{
params[i].value = _p.value ;
params[i].hex = _p.hex ;
return ;
}
if (i<EMULATOR_MAX_PARAMS)
{
params[i].value = _p.value ;
params[i].hex = _p.hex ;
params[i].data = _p.data ;
nbparams++;
}
/*
vector <struct param> params;
vector<struct param>::iterator v;
v = params.begin();
while(v!=params.end())
{
if (v->data == _p.data)
{
v->value = _p.value;
v->hex = _p.hex;
return ;
}
v++;
}
params.push_back(_p);
*/
}
const char* currentBuild=TOSTRING(ACTIVEGSMAJOR)"." TOSTRING(ACTIVEGSMINOR) "." TOSTRING(ACTIVEGSBUILD);
void CEmulator::checkAlerts()
{
static int bRunOnlyOnce=0;
if (bRunOnlyOnce) return ;
bRunOnlyOnce=1;
#ifdef _DEBUG
option.setValue(OPTION_LATESTVERSION,"5.1.4");
#endif
if (!option.getIntValue(OPTION_DONOTSHOWUPDATEALERT))
{
MyString newestBuild; option.getStrValue(newestBuild,OPTION_LATESTVERSION);
MyString lastestUpdatedBuild; option.getStrValue(lastestUpdatedBuild,OPTION_LATESTUPDATECHECK);
if ( !newestBuild.IsEmpty()
&& lastestUpdatedBuild.compare(newestBuild)
&& newestBuild.compare(currentBuild) )
{
option.setValue(OPTION_LATESTUPDATECHECK,newestBuild);
option.saveOptions(2);
x_alert("A newer version (%s) of ActiveGS is available!\nCheck http://activegs.freetoolsassociation.com for more info.",newestBuild.c_str(),currentBuild);
return ;
}
}
}
bool CEmulator::setParam(eParamType _type, const char* _data, const char* _value)
{
static const struct { const char* name; int* address; } variables[] =
{
// boot
{ "boot", (int*)0x28 },
// drive emulation
{ "driveemulation", &g_iwm.g_fast_disk_emul },
{ "slow", (int*)0 },
{ "fast", (int*)1 },
// slot
{ "slot6", (int*)0x26 },
{ "drive", (int*)0 },
{ "disk", (int*)0 },
{ "card",(int*)1},
// columns
{ "columns", (int*)0x19 },
{ "40", (int*)0 },
{ "80", (int*)1 },
// speed
{ "forcespeed", (int*)0x20 },
{ "normal",(int*)0},
{ "fast",(int*)1},
{ "zipspeed",&g_moremem.g_zipgs_reg_c05a},
// { "simulatespace",&g_adb.g_simulate_space},
{ "emulate6502",&g_sim65816.g_6502_emulation },
{ "apple2erom",&g_sim65816.g_use_apple2e_rom },
// bram
{ "font", (int*)0x1A },
{ "background", (int*)0x1B },
{ "border", (int*)0x1C },
{ "black", (int*)0x00 },
{ "dark-blue", (int*)0x02 },
{ "dark-green", (int*)0x04 },
{ "medium-blue", (int*)0x06 },
{ "brown", (int*)0x08 },
{ "light-gray", (int*)0x0A },
{ "green", (int*)0x0C },
{ "aquamarine", (int*)0x0E },
{ "deep-red", (int*)0x01 },
{ "purple", (int*)0x03 },
{ "dark-gray", (int*)0x05 },
{ "light-blue", (int*)0x07 },
{ "orange", (int*)0x09 },
{ "pink", (int*)0x0b },
{ "yellow", (int*)0x0d },
{ "white", (int*)0x0f },
{ "language", (int*)0x29 },
{ "keyboard", (int*)0x2a },
{ "french", (int*)0x02 },
/*
* $0 = USA Dvorak
* $1 = UK USA
* $2 = French USA
* $3 = Danish USA
* $4 = Spanish USA
* $5 = Italian USA
* $6 = German USA
* $7 = Swedish
*/
{ NULL, NULL }
};
int value = -1000;
int* data = 0;
if (!strcasecmp(_data,"driveemulation"))
data=0;
int i=0;
while(variables[i].name)
{
if (!strcasecmp(_data,variables[i].name))
{
data = (int*)variables[i++].address;
while(variables[i].name)
{
if (!strcasecmp(_value,variables[i].name))
{
value = (intptr_t)(variables[i].address);
break;
}
i++;
}
}
else
i++;
}
if (value==-1000)
value=atoi(_value);
if (!data)
data=(int*)atoi(_data);
if ( data && (value!=-1000))
{
outputInfo("%s = %s (%d)\n",_data,_value,value);
struct param p;
p.data = _data;
p.value = _value;
p.hex = value;
if (data==&g_moremem.g_zipgs_reg_c05a)
{
g_moremem.g_zipgs_reg_c05a = ((g_moremem.g_zipgs_reg_c05a&0x0F)|(value&0x0F)<<4);
p.value.Format("%3d%%",(int)((16-((g_moremem.g_zipgs_reg_c05a>>4)&0x0F))*100/16));
}
else
if ((intptr_t)data > 256)
// if (_type==EMULATOR)
*data = value;
else
{
// bram
int idx = (intptr_t)data;
if (idx>0 && idx<256)
g_clock.g_bram_ptr[idx]=value;
else
printf("invalid bram index : %s (%d)\n",_data,idx);
}
addUniqueParam(p);
}
else
{
#ifdef _DEBUG
outputInfo("unknown parameters (%s)(%s)\n",_data,_value);
#endif
}
return true;
}
bool CEmulator::parseParam(const char* _param,eParamType _type)
{
// Format
// data=value;data2=value;
const char* run = _param;
if (!run) return false;
MyString data;
MyString value;
while(1)
{
// data
data.Empty();
// skip ;
while (*run == ';') run++;
if (!*run) return false; // done
while(*run!='=' && *run!=':')
{
if (!*run) return false;
data += *run;
run++;
}
run++; // passe le =
value.Empty();
while(*run && *run!=';')
value += *run++;
if (!strcasecmp(data.c_str(),"setstart"))
extractSetStartParam(value.c_str());
else
{
if (data[0]!='_')
setParam(_type,data.c_str(),value.c_str());
}
if (!*run) break;
run++;
}
return true;
}
bool COption::addEmulatorParam(const char* _param)
{
const char* run = _param;
if (!_param) return true;
// printf("loading system param\n");
MyString data;
MyString value;
while(1)
{
// data
data.Empty();
// ignore the initial _ (for backward compatibility)
if (*run=='_') run++;
while(*run!='=' && *run!=':')
{
if (!*run) return false;
data += *run;
run++;
}
run++; // passe le =
value.Empty();
while(*run && *run!=';')
value += *run++;
// regarde si c'est une option
option_id id = findByName(data.c_str());
if (id!=OPTION_ERROR)
setValue(id,value.c_str(),1);
/*
if (data[0]=='_')
{
MyString data2;
data2 = data.substr(1);
option_id id = findByName(data2.c_str());
if (id==OPTION_ERROR)
printf("could not process : %s %s\n",data2.c_str(),value.c_str());
else
setValue(id,value.c_str(),1);
}
*/
if (!*run) break;
run++;
}
return true;
}
bool CEmulator::extractSetStartParam(const char* run)
{
// Format slot|file
setStart.slot = 0;
int slot;
MyString data;
while(*run!=':' && *run)
data += *run++;
slot = atoi(data.c_str());
if ( (slot!=51) && (slot!=52) && (slot!=71) && (slot!=72) ) return false;
data.Empty();
while(*run)
data += *run++;
setStart.slot = slot;
setStart.file = data;
return true;
}
// CChildView
void CEmulator::updateBramParameters()
{
if (!this) return ;
parseParam(bramParam.c_str(),BRAM);
x_refresh_panel(PANEL_PARAMS);
}
void CEmulator::setBramParam(const char* _p)
{
if (bramParam.IsEmpty())
bramParam = _p;
else
{
bramParam += ';';
bramParam += _p;
}
}
void CEmulator::ejectDisk(int _slot,int _drive)
{
if (!this) return ;
CSlotInfo& ref = getLocalIMGInfo(_slot,_drive);
if (ref.status == UNDEFINED) return ;
ref.status = EJECTED ;
outputInfo("status set to EJECTED for S%dD%d\n",_slot,_drive);
config->need2remount=true;
x_refresh_panel(PANEL_DISK);
if(g_sim65816.g_enable_disk_sound)
play_sound(SOUND_EJECTION);
}
void CEmulator::reloadDisk(int _slot,int _drive)
{
if (!this) return ;
CSlotInfo& ref = getLocalIMGInfo(_slot,_drive);
if (
(ref.status == EJECTED )||
(ref.status == FAILED ) )
{
ref.status = READY2MOUNT;
outputInfo("status set to READY2MOUNT for S%dD%d\n",_slot,_drive);
config->need2remount=true;
x_refresh_panel(PANEL_DISK);
}
}
CEmulator::CEmulator(CEmulator** _parent)
{
if (theEmulator)
x_fatal_exit("MULTIPLE INSTANCES");
bInThread = false;
theEmulator= this;
parent = _parent ;
#ifdef ACTIVEGSPLUGIN
pInstance = NULL;
#endif
theWindow = NULL;
g_sim65816.halt_sim = 0;
x=0;
y=0;
memset(lights,0,sizeof(lights));
memset(tracks,0,sizeof(tracks));
config = NULL;
}
#ifdef ACTIVEGSPLUGIN
void CEmulator::setPluginInstance(void* _instance)
{
pInstance = _instance;
}
#endif
void CEmulator::mountDisks()
{
config->need2remount=true;
forceFocus();
}
int CEmulator::theThread()
{
#ifdef TEST_RASTER
x_test_raster();
#else
if (!config)
{
x_fatal_exit("!!!MISSING CONFIG!!!\n");
return 0;
}
if (bInThread)
{
x_fatal_exit("THREAD ALREADY RUNNING\n");
return 0;
}
bInThread=true;
outputInfo("emulator thread launched!\n");
while(1)
{
config->need2remount=true;
mountDelayedDisk();
nbparams=0;
bramParam.Empty();
// 1- OPTION PAR DEFAULT DE L'EMULATEUR
// initialise les options par défault
setStart.slot=0;
g_sim65816.g_6502_emulation = 0;
g_sim65816.g_use_apple2e_rom = 0;
// paramètre auto!
if (config->bootslot==-1)
{
// default
config->bootslot=5;
for(int b = 5;b<=7;b++)
{
const char* s = config->getSlot(b,1);
if (s && s[0])
{
printf("bootslot set to %d\n",b);
config->bootslot=b;
break;
}
}
}
switch(config->bootslot)
{
case 6:
setEmulatorParam("driveemulation:slow;emulate6502:1;");
setBramParam("slot6:card;boot:6;border:black;background:black;font:white");
break;
case 5:
setBramParam("boot=5");
break;
default:
setEmulatorParam("driveemulation:fast;");
setBramParam("slot6:disk");
break;
}
// 1 la vitesse
g_moremem.g_zipgs_reg_c05a = 0x0F; // MAX SPEED
int sp = config->speed;
if (sp == SPEED_AUTO)
{
if (config->bootslot==6)
sp = SPEED_1MHZ;
else
sp = SPEED_GS;
printf("speed auto set to %d\n",sp);
}
onSpeedChanged(sp,true);
// 2 - LES OPTIONS DU FICHIERS DE CONF
option.loadOptions();
// 3 - LES OPTIONS DU FICHIER XML CONFIG
printf("*loading option from xml\n");
printf("EmulatorParam: %s\n",config->xmlEmulatorParam.c_str());
printf("BramParam: %s\n",config->xmlBramParam.c_str());
printf("SystemParam: %s\n",config->xmlSystemParam.c_str());
option.addEmulatorParam(config->xmlSystemParam.c_str());
setBramParam(config->xmlSystemParam.c_str());
setBramParam(config->xmlBramParam.c_str());
option.addEmulatorParam(config->xmlEmulatorParam.c_str());
setEmulatorParam(config->xmlEmulatorParam.c_str());
// 4 - LES OPTIONS JAVASCRIPT
printf("*loading option from javascript\n");
option.addEmulatorParam(STRING2CONSTCHAR(config->systemParam));
setBramParam(STRING2CONSTCHAR(config->systemParam));
setBramParam(STRING2CONSTCHAR(config->bramParam));
setEmulatorParam(STRING2CONSTCHAR(config->emulatorParam));
option.addEmulatorParam(STRING2CONSTCHAR(config->emulatorParam));
option.enableOptions();
mountDisks();
g_adb.g_adb_repeat_delay = option.getIntValue(OPTION_ADBDELAY);
g_adb.g_adb_repeat_rate = option.getIntValue(OPTION_ADBRATE);
int ar = option.getIntValue(OPTION_AUDIORATE);
if (ar)
{
if (ar<11025 || ar>44100)
ar = 22100;
g_sound.g_audio_enable = 1;
g_sound.g_audio_rate = ar;
}
else
g_sound.g_audio_enable = 0;
int mem = option.getIntValue(OPTION_MEMORYSIZE);
if (mem <=8 && mem>=0)
mem *= 1024*1024;
else
if (mem>=128 && mem<=8*1024)
{
mem *= 1024;
}
else
if (mem>=128*1024 && mem<=8*1024*1024)
{
}
else
mem = 2*1024*1024;
g_sim65816.g_mem_size_exp = mem;
if (config->bootslot!=6 && g_sim65816.g_use_apple2e_rom)
{
printf("disabling apple 2e rom (only works when booting from slot6\n");
g_sim65816.g_use_apple2e_rom = 0;
}
g_savestate.init();
if (config->stateFilename.IsEmpty())
{
}
else
{
printf("about to restore : %s\n",config->stateFilename.c_str());
g_savestate.restoreState(config->stateFilename.c_str());
}
printf("*** notifying boot option ***\n");
x_refresh_panel(PANEL_BOOT);
printf("*** launching Emulator Mainloop\n");
#ifndef MAC
kegsmain(0,NULL);
#else
extern int macmain(int,char**);
macmain(0,NULL);
#endif
g_savestate.shut();
joystick_shut();
if (!r_sim65816.restart_requested)
break;
else
{
outputInfo("*** Restarting emulator\n");
r_sim65816.restart_requested = 0;
outputInfo("*** bRestarting\n");
setConfig(config);
}
}
#endif // TEST_RASTER
outputInfo("end of emulator thread\n");
bInThread=false;
return 0;
}
void CEmulator::forceFocus()
{
// TODO OG
}
void CEmulator::readImageInfo(int _slot,int _drive, int _imagelen, int _imagestart, int _isprodos)
{
if (!this) return ;
CSlotInfo& ref = getLocalIMGInfo(_slot,_drive);
ref.size = _imagelen;
MyString prefix("?");
if (_isprodos) //_info.track0_len!=-1)
{
char ptr[32];
memset(ptr,0,sizeof(ptr));
const char* filename = ref.filename.c_str();
if (filename)
{
FILE* f = fopen(filename,"rb");
if (f)
{
fseek(f,_imagestart+0x404,SEEK_SET);
if (16==fread(ptr,1,16,f))
if ( ((ptr[0]&0xF0)==0xF0) )
{
int len= ptr[0]&0x0F;
ptr[len+1] = 0;
prefix = "/";
prefix += (ptr+1);
}
fclose(f);
}
}
}
else
prefix = "NON-PRODOS";
ref.prefix = prefix;
}
int CEmulator::getLocalMultipleIMGInfo(int _slot,int _drive,CSlotInfo& info, int _pos)
{
CActiveImage& active = config->getActiveImage(_slot,_drive);
if (_pos>=active.iNb) return 0;
info = config->getLocalImage(_slot,_drive,_pos);
if (active.iActive==_pos) return 1;
else
return -1;
}
int CEmulator::mountDelayedDisk()
{
for(int slot=5;slot<=7;slot++)
for(int disk=1;disk<=2;disk++)
{
CSlotInfo& ref = getLocalIMGInfo(slot,disk);
if (ref.status==DELAYEDMOUNT)
{
outputInfo("status set to READY2MOUNT for S%dD%d\n",slot,disk);
ref.status=READY2MOUNT;
}
}
return 0;
}
bool CEmulator::remainingDelayedDisk()
{
for(int slot=5;slot<=7;slot++)
for(int disk=1;disk<=2;disk++)
{
CSlotInfo& ref = getLocalIMGInfo(slot,disk);
if (ref.status==DELAYEDMOUNT)
return true;
}
return false;
}
int CEmulator::getSmartSwap()
{
int found = 0;
for(int slot=5;slot<=6;slot++)
{
for(int disk=1;disk<=2;disk++)
{
CActiveImage& active = config->getActiveImage(slot,disk);
if (active.iNb > 1)
{
if (found)
{
// multiple multiple : does not know how to handle
return 0;
}
else
found = slot*10+disk;
}
}
}
return found;
}
int CEmulator::smartSwap()
{
int smart = getSmartSwap();
if (!smart)
{
outputInfo("smart swap not available\n");
return 0;
}
return swapDisk(smart/10,smart%10);
}
int CEmulator::swapDisk(int _slot,int _drive, int pos )
{
CActiveImage& active = config->getActiveImage(_slot,_drive);
if (active.iNb <=1)
{
outputInfo("nothing to swap\n");
return 0;
}
CSlotInfo& ref = getLocalIMGInfo(_slot,_drive);
if (ref.status != UNDEFINED)
{
ref.status = EJECTED ;
outputInfo("status set to EJECTED for S%dD%d\n",_slot,_drive);
}
//ejectDisk(_slot,_drive);
// passe à la suivante
if (pos<0)
pos = active.iActive+1;
if (pos>=active.iNb)
pos=0;
active.iActive = pos;
outputInfo("swapping to entry %d\n",pos);
CSlotInfo& info = getLocalIMGInfo(_slot,_drive);
if (!info.url.IsEmpty())
{
outputInfo("status set to DELAYEDMOUNT for S%dD%d\n",_slot,_drive);
info.status = DELAYEDMOUNT;
info.delay = (int)(60*1.5); // 5s
}
config->need2remount=true;
x_refresh_panel(PANEL_DISK);
return 1;
}
CSlotInfo& CEmulator::getLocalIMGInfo(int _slot,int _drive)
{
CActiveImage& active = config->getActiveImage(_slot,_drive);
return config->getLocalImage(_slot,_drive,active.iActive);
}
int CEmulator::mountImages()
{
if (!this) return 0;
#if defined(ACTIVEGSPLUGIN) && !defined(DRIVER_IOS)
checkAlerts();
#endif
if (config->need2remount)
{
//outputInfo("mounting images...\n");
int ri = 0;
for(int slot=5;slot<=7;slot++)
{
int maxdrive=2;
if (slot==7) maxdrive=MAXSLOT7DRIVES;
for(int drive=1;drive<=maxdrive;drive++)
{
ri |= loadDiskImage(/*name,*/slot,drive);
if (r_sim65816.should_emulator_terminate())
return 0 ;
// regarde si il faut patcher le disque systËme
if (setStart.slot == (slot*10)+drive)
setStartParam();
}
}
config->need2remount = (int)remainingDelayedDisk();
if (ri)
x_refresh_panel(PANEL_DISK);
return 1;
}
else
return 0;
}
int CEmulator::loadDiskImage(int _slot,int _drive)
{
ASSERT(theEmulator==this);
CSlotInfo& ref = getLocalIMGInfo(_slot,_drive);
if (ref.status != READY2MOUNT)
return 0;
if (ref.url.IsEmpty())
{
ref.status = NOTHING;
outputInfo("status set to NOTHING for S%dD%d\n",_slot,_drive);
ref.filename.Empty();
ref.shortname.Empty();
return 1;
}
MyString path;
MyString shortname;
CDownload dl(this->config->baseURL);
#ifdef DRIVER_ANDROID
dl.fromMainThread = false;
#endif
if (dl.retrieveFile(STRING2CONSTCHAR(ref.url),path,shortname))
{
ref.filename = path ;
ref.shortname = shortname ;
ref.status = MOUNTED ;
outputInfo("status set to MOUNTED for S%dD%d\n",_slot,_drive);
//ref.delay = 1;
::showStatus("Image %s mounted [S%dD%d]",ref.shortname.c_str(),_slot,_drive);
return 1 ;
}
else
{
ref.status = FAILED ;
outputInfo("status set to FAILED for S%dD%d\n",_slot,_drive);
ref.shortname = getfile(STRING2CONSTCHAR(ref.url));
return 1;
}
}
void CEmulator::resetLocalIMG(void)
{
}
// Interface
const char* getLocalIMG(int _slot,int _drive)
{
CEmulator* emu = CEmulator::theEmulator;
CSlotInfo& ref = emu->getLocalIMGInfo(_slot,_drive);
if (ref.status == DELAYEDMOUNT)
{
ref.delay--;
if (ref.delay<=0)
{
ref.status = READY2MOUNT;
outputInfo("status set to READY2MOUNT for S%dD%d : image %s\n",_slot,_drive,ref.shortname.c_str());
}
emu->config->need2remount=true;
return NULL;
}
if (ref.status == MOUNTED)
return ref.filename.c_str() ;
else
return NULL;
}
int mountImages()
{
return CEmulator::theEmulator->mountImages();
}
void updateBramParameters(unsigned char* bramPtr)
{
return CEmulator::theEmulator->updateBramParameters();
}
void readImageInfo(int _slot,int _drive, int _imagelen, int _imagestart, int _isprodos)
{
return CEmulator::theEmulator->readImageInfo(_slot,_drive,_imagelen,_imagestart,_isprodos);
}
/*
void refreshInfo()
{
CEmulator* emu = CEmulator::theEmulator;
if (!emu) return ;
emu->infoRefresh();
}
*/
void disableConsole()
{
CEmulator* emu = CEmulator::theEmulator;
if (!emu) return ;
emu->disableConsole();
}
int activeRead(char* buf)
{
CEmulator* emu = CEmulator::theEmulator;
if (!emu)
{
buf[0]=0;
return 0;
}
else
return (emu->activeRead(buf));
}
void COption::enableOptions()
{
r_sim65816.g_ignore_bad_acc = !getIntValue(OPTION_HALTONBADACC);
r_sim65816.g_ignore_halts = !getIntValue(OPTION_HALTONHALTS);
r_sim65816.g_halt_on_bad_read = getIntValue(OPTION_HALTONBADREAD);
r_sim65816.set_rewind_enable(getIntValue(OPTION_ENABLEREWIND));
r_sim65816.set_color_mode( (colormodeenum)getIntValue(OPTION_COLORMODE));
r_sim65816.set_video_fx((videofxenum)getIntValue(OPTION_VIDEOFX));
g_joystick_type = getIntValue(OPTION_JOYSTICKMODE);
#ifndef VIRTUALAPPLE
g_sim65816.set_fixed_vblank( getIntValue(OPTION_FIXEDVBLANK),(speedenum)getIntValue(OPTION_SPEED) );
#endif
g_sim65816.g_enable_disk_sound = getIntValue(OPTION_DISKSOUND);
g_paddles.g_joystick_scale_factor_x = (getIntValue(OPTION_JOYXSCALE)*256)/1000; // 0x100 = 1
g_paddles.g_joystick_scale_factor_y = (getIntValue(OPTION_JOYYSCALE)*256)/1000; // 0x100 = 1
g_adb.g_simulate_space = getIntValue(OPTION_SIMULATESPACE);
if (r_sim65816.get_state()==RUNNING)
paddle_fixup_joystick_type();
#ifdef DRIVER_IOS
extern int x_frame_rate;
extern int x_lock_zoom;
x_frame_rate = getIntValue(OPTION_FRAMERATE);
#warning "TODO CHANGE FPS IN REALTIME"
x_lock_zoom = getIntValue(OPTION_LOCKZOOM);
#endif
x_refresh_panel(PANEL_RUNTIME);
}
void CEmulator::setLight(int _motorOn,int _slot, int _drive, int _track)
{
int id=0;
for(int slot=5;slot<=6;slot++)
for(int drive=1; drive<=2; drive++)
{
if ( (_slot==slot) && (_drive==drive) )
{
lights[id]=_motorOn;
tracks[id]=_track;
}
else
lights[id]=0;
id++;
}
}
//extern char targetSpeed[256];
extern char estimatedSpeed[256];
void CEmulator::processCommand( const char* _val, MyString& ret)
{
// outputInfo("processingCommand:%s\n",_val);
if (!strcmp(_val,"help"))
ret = "({ commands : [ \"help\" , \"version\", \"system\", \"status\", \"runtime\", \"reboot\",\"savestate\", \"restorestate\", \"pause\", \"resume\" ,\"pauseresume\", \"swap\", \"screenshot\",\"saveoptions\",\"defaultoptions\",\"sendkey:ctrl;alt;esc\" ] })";
else
if (!strcasecmp(_val,"saveoptions"))
{
option.saveOptions(1);
ret = "({ \"info\" : \"options saved\"})";
}
else
if (!strcasecmp(_val,"defaultOptions"))
{
option.setDefaultOptions();
option.enableOptions();
ret = "({ \"info\" : \"default options restored\"})";
}
else
if (!strncasecmp(_val,"sendkey:",8 /* sizeof ("sendkey:")*/))
{
#if defined (DRIVER_OSX) && !defined(ACTIVEGSPLUGIN)
ret = "({ \"info\" : \"not implemented\"})";
#else
const char* keystr[] = { "^ctrl" , "^alt", "^esc", "^reset", "^option", NULL };
const int keycode[] = { 0x36 , 0x37 , 0x35, 0x7F, 0x3A, 0 };
#define MAX_CODE 100
int code[MAX_CODE];
int nbcode=0;
const char* ptr = _val+8;
MyString keysent;
while(*ptr)
{
const char* p = ptr;
while(*p && *p!=';') p++;
int l = p-ptr;
int k = 0;
while(keystr[k])
{
if (!strncasecmp(keystr[k],ptr,l))
{
code[nbcode++]=k;
// printf("found %s (%X)\n",keystr[k],keycode[k]);
keysent+=keystr[k];
keysent+=";";
break;
}
k++;
}
if (!keystr[k])
printf("key not valid %s\n",ptr);
ptr = p;
if (*ptr==';') ptr++;
if (nbcode==MAX_CODE) break;
}
for(int i=0;i<nbcode;i++)
{
add_event_key(keycode[code[i]],0);
for(int j=0;j<30;j++)
add_event_delay();
}
for(int i=0;i<60;i++)
add_event_delay();
for(int i=nbcode-1;i>=0;i--)
{
add_event_key(keycode[code[i]],1);
}
ret.Format("({ \"info\" : \"key sent %s\"})",keysent.c_str());
#endif
}
else
if (!strcasecmp(_val,"version"))
{
ret = "({ \"info\" : \"" ACTIVEGSVERSIONSTRFULL "\"})";
}
else
/*
if (!strcmp(_val,"rewind"))
{
stateActionRequired = STATE_REWIND;
ret = "({ \"info\" : \"rewind activated\"})";
}
else
*/
if (!strcasecmp(_val,"system"))
{
ret = "( { ";
ret += " \"currentBuild\" : \"" TOSTRING(ACTIVEGSMAJOR) "." TOSTRING(ACTIVEGSMINOR) "." TOSTRING(ACTIVEGSBUILD) "\",";
MyString str;
int g_joystick_plugged = (g_joystick_native_type1 != -1) || (g_joystick_native_type2 != -1) ? 1 : 0 ;
str.Format("\"joystickPlugged\" : %d,",g_joystick_plugged);
ret += str.c_str();
MyString engine;
getEngineString(engine);
str.Format("\"renderingEngine\" : \"%s\",",engine.c_str());
ret += str.c_str();
str.Format("\"memorySize\" : %d,",option.getIntValue(OPTION_MEMORYSIZE));
ret += str.c_str();
str.Format("\"audioRate\" : %d",option.getIntValue(OPTION_AUDIORATE));
ret += str.c_str();
ret +="})";
}
else
if (!strcasecmp(_val,"reboot"))
{
// Force la relecture du fichier XML!
config->xmlAlreadyLoaded = 0;
onBootSlotChanged(-1); // Bootslot Auto
ret = "({ \"info\" : \"Rebooted!\" })";
}
else
if (!strcasecmp(_val,"status"))
{
MyString str;
ret = "( {";
str.Format("\"vbl\" : %d,",g_sim65816.g_vbl_count);
ret += str;
str.Format("\"speed\" : { estimated:\"%s\", target:\"",estimatedSpeed);
if (!g_sim65816.g_speed_fast)
str +="1";
else
str += option.getDescription(OPTION_SPEED,g_sim65816.get_limit_speed());
str +="\"},";
ret += str;
ret += " \"lights\" : {";
for(int i=0;i<4;i++)
{
str.Format(" \"slot%d%d\" : { \"on\" : %d, \"track\" : %d }",(i/2)+5,(i&1)+1,lights[i],tracks[i]);
ret += str;
if (i!=3)
ret+=",";
}
ret += "}";
ret += "} )";
}
else
if (!strcasecmp(_val,"runtime"))
{
MyString str;
ret = "( {";
str.Format("\"pause\" : %d,",r_sim65816.get_state()==IN_PAUSE);
ret += str;
str.Format("\"swap\" : %d,",getSmartSwap());
ret += str;
str.Format("\"rewindAvailable\" : %d,",r_sim65816.get_rewind_enable());
ret += str.c_str();
str.Format("\"savedState\" : %d,", g_savestate.getSavedStateVBL());
ret += str.c_str();
ret += "\"slots\" : [";
int nb = 0;
for(int slot=5;slot<=7;slot++)
{
int maxdrive=2;
if (slot==7) maxdrive=MAXSLOT7DRIVES;
for(int disk=1;disk<=maxdrive;disk++)
{
for(int active=0;active<ACTIVEGSMAXIMAGE;active++)
{
CSlotInfo info;
int g = getLocalMultipleIMGInfo(slot,disk,info,active);
if (!g) continue;
if (info.status==UNDEFINED || info.status ==NOTHING) continue;
if (g!=1) g=0;
MyString prefix;
switch(info.status)
{
case MOUNTED:
prefix = "MOUNTED";;
break;