1 /*******************************************************************
\r
5 * Author: Peter van Sebille (peter@yipton.net)
\r
7 * Modified/adapted for picodriveN by notaz, 2006
\r
9 * (c) Copyright 2006, notaz
\r
10 * (c) Copyright 2002, Peter van Sebille
\r
11 * All Rights Reserved
\r
13 *******************************************************************/
\r
20 #include <e32math.h>
\r
25 #include "version.h"
\r
26 #include "../../pico/picoInt.h"
\r
27 #include "engine/debug.h"
\r
30 // this is where we start to break a bunch of symbian rules
\r
31 extern TInt machineUid;
\r
32 extern int gamestate, gamestate_next;
\r
33 extern TPicoConfig *currentConfig;
\r
34 extern const char *actionNames[];
\r
35 RSemaphore pauseSemaphore;
\r
36 RSemaphore initSemaphore;
\r
37 const char *RomFileName = 0;
\r
38 int pico_was_reset = 0;
\r
39 unsigned char *rom_data = 0;
\r
40 static CPicolAppView *appView = 0;
\r
43 TInt CPicoGameSession::Do(const TPicoServRqst what, TAny *param)
\r
46 case PicoMsgLoadState:
\r
47 if(!rom_data) return -1; // no ROM
\r
48 return saveLoadGame(1);
\r
50 case PicoMsgSaveState:
\r
51 if(!rom_data) return -1;
\r
52 return saveLoadGame(0);
\r
54 case PicoMsgLoadROM:
\r
55 return loadROM((TPtrC16 *)param);
\r
58 DEBUGPRINT(_L("resume with rom %08x"), rom_data);
\r
60 return ChangeRunState(PGS_Running);
\r
68 return ChangeRunState(PGS_Running);
\r
73 return ChangeRunState(PGS_KeyConfig);
\r
76 return ChangeRunState(PGS_Paused);
\r
79 DEBUGPRINT(_L("got quit msg."));
\r
80 return ChangeRunState(PGS_Quit);
\r
83 case PicoMsgConfigChange:
\r
84 return changeConfig((TPicoConfig *)param);
\r
86 case PicoMsgSetAppView:
\r
87 appView = (CPicolAppView *)param;
\r
95 TInt EmuThreadFunction(TAny* anArg);
\r
97 TInt CPicoGameSession::StartEmuThread()
\r
100 iEmuRunning = EFalse;
\r
102 if (initSemaphore.Handle() > 0)
\r
103 initSemaphore.Close();
\r
104 initSemaphore.CreateLocal(0);
\r
105 if (pauseSemaphore.Handle() <= 0)
\r
106 pauseSemaphore.CreateLocal(0);
\r
109 if(iThreadWatcher && (res = thread.Open(iThreadWatcher->iTid)) == KErrNone) {
\r
110 // should be a dead thread in some strange state.
\r
111 DEBUGPRINT(_L("found thread with the same id (id=%i, RequestCount=%i), killing.."),
\r
112 (TInt32)thread.Id(), thread.RequestCount());
\r
113 // what can we do in this situation? Nothing seems to help, it just stays in this state.
\r
114 delete iThreadWatcher;
\r
115 iThreadWatcher = 0;
\r
117 thread.Terminate(1);
\r
121 //semaphore.CreateLocal(0); // create a semaphore so we know when thread init is finished
\r
122 res=thread.Create(_L("PicoEmuThread"), // create new server thread
\r
123 EmuThreadFunction, // thread's main function
\r
127 0 // &semaphore // passed as TAny* argument to thread function
\r
130 if(res == KErrNone) { // thread created ok - now start it going
\r
131 thread.SetPriority(EPriorityMore);
\r
132 iEmuRunning = ETrue;
\r
133 if (iThreadWatcher) delete iThreadWatcher;
\r
134 iThreadWatcher = CThreadWatcher::NewL(thread.Id());
\r
135 thread.Resume(); // start it going
\r
136 DEBUGPRINT(_L("initSemaphore.Wait()"));
\r
137 res = initSemaphore.Wait(1000*1000); // wait until it's initialized
\r
138 DEBUGPRINT(_L("initSemaphore resume, ExitReason() == %i"), thread.ExitReason());
\r
139 res |= thread.ExitReason();
\r
140 thread.Close(); // we're no longer interested in the other thread
\r
141 if(res != KErrNone) iEmuRunning = EFalse;
\r
148 TInt CPicoGameSession::ChangeRunState(TPicoGameState newstate, TPicoGameState newstate_next)
\r
150 if (!iEmuRunning) {
\r
151 gamestate = PGS_Paused;
\r
152 TInt res = StartEmuThread();
\r
153 if(res != KErrNone) DEBUGPRINT(_L("StartEmuThread() returned %i"), res);
\r
154 if (!iEmuRunning) return PicoErrEmuThread;
\r
157 int oldstate = gamestate;
\r
158 gamestate = newstate;
\r
159 gamestate_next = newstate_next ? newstate_next : PGS_Paused;
\r
160 if (oldstate == PGS_Paused) pauseSemaphore.Signal();
\r
165 TInt CPicoGameSession::loadROM(TPtrC16 *pptr)
\r
171 // save SRAM for previous ROM
\r
172 if(currentConfig->iFlags & 1)
\r
173 saveLoadGame(0, 1);
\r
182 // read the contents of the client pointer into a TPtr.
\r
183 static TBuf8<KMaxFileName> writeBuf;
\r
184 writeBuf.Copy(*pptr);
\r
186 // detect wrong extensions (.srm and .mds)
\r
188 ext.Copy(writeBuf.Right(4));
\r
190 if(!strcmp((char *)ext.PtrZ(), ".srm") || !strcmp((char *)ext.PtrZ(), "s.gz") || // .mds.gz
\r
191 !strcmp((char *)ext.PtrZ(), ".mds")) {
\r
192 return PicoErrNotRom;
\r
195 FILE *rom = fopen((char *) writeBuf.PtrZ(), "rb");
\r
197 DEBUGPRINT(_L("failed to open rom."));
\r
198 return PicoErrRomOpenFailed;
\r
201 // make sure emu thread is ok
\r
202 res = ChangeRunState(PGS_Paused);
\r
208 unsigned int rom_size = 0;
\r
210 if(!strcmp((char *)ext.PtrZ(), ".zip")) {
\r
212 res = CartLoadZip((const char *) writeBuf.PtrZ(), &rom_data, &rom_size);
\r
214 DEBUGPRINT(_L("CartLoadZip() failed (%i)"), res);
\r
218 if( (res = PicoCartLoad(rom, &rom_data, &rom_size)) ) {
\r
219 DEBUGPRINT(_L("PicoCartLoad() failed (%i)"), res);
\r
221 return PicoErrOutOfMem;
\r
226 // detect wrong files (Pico crashes on very small files), also see if ROM EP is good
\r
227 if(rom_size <= 0x200 || strncmp((char *)rom_data, "Pico", 4) == 0 ||
\r
228 ((*(TUint16 *)(rom_data+4)<<16)|(*(TUint16 *)(rom_data+6))) >= (int)rom_size) {
\r
231 return PicoErrNotRom;
\r
234 DEBUGPRINT(_L("PicoCartInsert(0x%08X, %d);"), rom_data, rom_size);
\r
235 if(PicoCartInsert(rom_data, rom_size)) {
\r
236 return PicoErrOutOfMem;
\r
239 pico_was_reset = 1;
\r
241 // global ROM file name for later use
\r
242 RomFileName = (const char *) writeBuf.PtrZ();
\r
244 // name from the ROM itself
\r
245 for(i = 0; i < 0x30; i++)
\r
246 buff[i] = rom_data[0x150 + (i^1)]; // unbyteswap
\r
247 for(buff[i] = 0, i--; i >= 0; i--) {
\r
248 if(buff[i] != ' ') break;
\r
251 TPtrC8 buff8((TUint8*) buff);
\r
252 iRomInternalName.Copy(buff8);
\r
254 // load SRAM for this ROM
\r
255 if(currentConfig->iFlags & 1)
\r
256 saveLoadGame(1, 1);
\r
259 #ifdef __DEBUG_PRINT
\r
260 TInt cells = User::CountAllocCells();
\r
262 User::AllocSize(mem);
\r
263 DEBUGPRINT(_L("comm: cels=%d, size=%d KB"), cells, mem/1024);
\r
264 ChangeRunState(PGS_DebugHeap, PGS_Running);
\r
266 ChangeRunState(PGS_Running);
\r
273 TInt CPicoGameSession::changeConfig(TPicoConfig *aConfig)
\r
275 DEBUGPRINT(_L("got new config."));
\r
277 currentConfig = aConfig;
\r
279 // set PicoOpt and rate
\r
280 PicoRegionOverride = currentConfig->PicoRegion;
\r
281 PicoOpt = currentConfig->iPicoOpt;
\r
282 switch((currentConfig->iFlags>>3)&7) {
\r
283 case 1: PsndRate=11025; break;
\r
284 case 2: PsndRate=16000; break;
\r
285 case 3: PsndRate=22050; break;
\r
286 case 4: PsndRate=44100; break;
\r
287 default: PsndRate= 8000; break;
\r
290 // 6 button pad, enable XYZM config if needed
\r
291 if(PicoOpt & 0x20) {
\r
292 actionNames[8] = "Z";
\r
293 actionNames[9] = "Y";
\r
294 actionNames[10] = "X";
\r
295 actionNames[11] = "MODE";
\r
297 actionNames[8] = actionNames[9] = actionNames[10] = actionNames[11] = 0;
\r
300 // if we are in center 90||270 modes, we can bind renderer switcher
\r
301 if(currentConfig->iScreenMode == TPicoConfig::PMFit &&
\r
302 (currentConfig->iScreenRotation == TPicoConfig::PRot0 || currentConfig->iScreenRotation == TPicoConfig::PRot180))
\r
303 actionNames[25] = 0;
\r
304 else actionNames[25] = "RENDERER";
\r
310 void MainOldCleanup(); // from main.cpp
\r
311 #ifdef __DEBUG_PRINT_FILE
\r
312 extern RMutex logMutex;
\r
315 void CPicoGameSession::freeResources()
\r
320 DEBUGPRINT(_L("CPicoGameSession::freeResources()"));
\r
322 if(iThreadWatcher && thread.Open(iThreadWatcher->iTid) == KErrNone)
\r
324 // try to stop our emu thread
\r
325 gamestate = PGS_Quit;
\r
326 if(pauseSemaphore.Handle() > 0)
\r
327 pauseSemaphore.Signal();
\r
329 if(thread.Handle() > 0)
\r
331 // tried reopening thread handle here over time intervals to detect if thread is alive,
\r
332 // but would run into handle panics.
\r
334 for(i = 0; i < 8; i++) {
\r
335 User::After(100 * 1000);
\r
336 if(thread.ExitReason() != 0) break;
\r
339 if(thread.ExitReason() == 0) {
\r
340 // too late, time to die
\r
341 DEBUGPRINT(_L("thread %i not responding, killing.."), (TInt32) thread.Id());
\r
342 thread.Terminate(1);
\r
349 if(iThreadWatcher != NULL)
\r
351 DEBUGPRINT(_L("delete iThreadWatcher"));
\r
352 delete iThreadWatcher;
\r
353 DEBUGPRINT(_L("after delete iThreadWatcher"));
\r
354 iThreadWatcher = NULL;
\r
359 if (initSemaphore.Handle() > 0)
\r
360 initSemaphore.Close();
\r
361 if (pauseSemaphore.Handle() > 0)
\r
362 pauseSemaphore.Close();
\r
363 #ifdef __DEBUG_PRINT_FILE
\r
364 if (logMutex.Handle() > 0)
\r
369 TBool CPicoGameSession::iEmuRunning = EFalse;
\r
370 CThreadWatcher *CPicoGameSession::iThreadWatcher = 0;
\r
371 TBuf<0x30> CPicoGameSession::iRomInternalName;
\r
374 void TPicoConfig::SetDefaults()
\r
376 iLastROMFile.SetLength(0);
\r
377 iScreenRotation = PRot270;
\r
378 iScreenMode = PMCenter;
\r
379 iFlags = 1; // use_sram
\r
380 iPicoOpt = 0; // all off
\r
381 iFrameskip = PFSkipAuto;
\r
383 Mem::FillZ(iKeyBinds, sizeof(iKeyBinds));
\r
384 Mem::FillZ(iAreaBinds, sizeof(iAreaBinds));
\r
385 iKeyBinds[0xd5] = 1<<26; // bind back
\r
389 void TPicoConfig::InternalizeL(RReadStream &aStream)
\r
391 TInt32 version, fname_len;
\r
392 version = aStream.ReadInt32L();
\r
393 fname_len = aStream.ReadInt32L();
\r
395 // not sure if this is safe
\r
396 iLastROMFile.SetMax();
\r
397 aStream.ReadL((TUint8 *) iLastROMFile.Ptr(), KMaxFileName*2);
\r
398 iLastROMFile.SetLength(fname_len);
\r
400 iScreenRotation = aStream.ReadInt32L();
\r
401 iScreenMode = aStream.ReadInt32L();
\r
402 iFlags = aStream.ReadUint32L();
\r
403 iPicoOpt = aStream.ReadInt32L();
\r
404 iFrameskip = aStream.ReadInt32L();
\r
406 aStream.ReadL((TUint8 *)iKeyBinds, sizeof(iKeyBinds));
\r
407 aStream.ReadL((TUint8 *)iAreaBinds, sizeof(iAreaBinds));
\r
409 PicoRegion = aStream.ReadInt32L();
\r
413 void TPicoConfig::ExternalizeL(RWriteStream &aStream) const
\r
415 TInt version = (KPicoMajorVersionNumber<<24)+(KPicoMinorVersionNumber<<16);
\r
417 aStream.WriteInt32L(version);
\r
418 aStream.WriteInt32L(iLastROMFile.Length());
\r
419 aStream.WriteL((const TUint8 *)iLastROMFile.Ptr(), KMaxFileName*2);
\r
421 aStream.WriteInt32L(iScreenRotation);
\r
422 aStream.WriteInt32L(iScreenMode);
\r
423 aStream.WriteUint32L(iFlags);
\r
424 aStream.WriteInt32L(iPicoOpt);
\r
425 aStream.WriteInt32L(iFrameskip);
\r
427 aStream.WriteL((const TUint8 *)iKeyBinds, sizeof(iKeyBinds));
\r
428 aStream.WriteL((const TUint8 *)iAreaBinds, sizeof(iAreaBinds));
\r
430 aStream.WriteInt32L(PicoRegion);
\r
435 CThreadWatcher::~CThreadWatcher()
\r
438 DEBUGPRINT(_L("after CThreadWatcher::Cancel();"));
\r
441 CThreadWatcher::CThreadWatcher(const TThreadId& aTid)
\r
442 : CActive(CActive::EPriorityStandard), iTid(aTid)
\r
447 CThreadWatcher* CThreadWatcher::NewL(const TThreadId& aTid)
\r
449 CThreadWatcher* self = new(ELeave) CThreadWatcher(aTid);
\r
450 CleanupStack::PushL(self);
\r
451 self->ConstructL();
\r
452 CleanupStack::Pop(); // self
\r
456 void CThreadWatcher::ConstructL()
\r
458 CActiveScheduler::Add(this);
\r
460 if(thread.Open(iTid) == KErrNone) {
\r
461 thread.Logon(iStatus);
\r
467 void CThreadWatcher::RunL()
\r
469 DEBUGPRINT(_L("CThreadWatcher::RunL()"));
\r
470 CPicoGameSession::iEmuRunning = EFalse;
\r
471 if(appView) appView->UpdateCommandList();
\r
472 //initSemaphore.Signal(); // no point to do that here, AS can't get here if it is waiting
\r
475 void CThreadWatcher::DoCancel()
\r
478 DEBUGPRINT(_L("CThreadWatcher::DoCancel()"));
\r
479 if(thread.Open(iTid) == KErrNone) {
\r
480 DEBUGPRINT(_L("thread.LogonCancel(iStatus);"));
\r
481 thread.LogonCancel(iStatus);
\r