Sonic CD shows Sega logo
[picodrive.git] / platform / uiq3 / engine / main.cpp
CommitLineData
cc68a136 1// mainloop with window server event handling\r
2// event polling mechnism was taken from\r
3// Peter van Sebille's projects\r
4\r
5// (c) Copyright 2006, notaz\r
6// All Rights Reserved\r
7\r
8#include <e32base.h>\r
9#include <hal.h>\r
10#include <e32keys.h>\r
11#include <w32std.h>\r
12\r
13#include <stdio.h>\r
14#include <stdlib.h>\r
15#include <sys/time.h>\r
16\r
17#include "debug.h"\r
18#include "../Engine.h"\r
19\r
20#include "../../../pico/picoInt.h"\r
21#include "vid.h"\r
22#include "polledAS.h"\r
23//#include "audio.h"\r
24#include "audio_mediaserver.h"\r
25\r
26#include <EZlib.h>\r
27#include "../../../zlib/gzio_symb.h"\r
28\r
29\r
30//#define BENCHMARK\r
31\r
32\r
33// scancodes we care about\r
34enum TUsedScanCodes {\r
35 EStdKeyM600JogUp = EStdKeyDevice1,\r
36 EStdKeyM600JogDown = EStdKeyDevice2,\r
37};\r
38\r
39static unsigned char keyFlags[256]; // lsb->msb: key_down, pulse_only, ?, ?, ?, ?, not_configurable, disabled\r
40static unsigned char pressedKeys[11]; // List of pressed key scancodes, up to 10\r
41\r
42// list of areas\r
43TPicoAreaConfigEntry areaConfig[] = {\r
44 { TRect( 0, 0, 0, 0) },\r
45 // small corner bottons\r
46 { TRect( 0, 0, 15, 15) },\r
47 { TRect(224, 0, 239, 15) },\r
48 { TRect( 0, 304, 15, 319) },\r
49 { TRect(224, 304, 239, 319) },\r
50 // normal buttons\r
51 { TRect( 0, 0, 79, 63) },\r
52 { TRect( 80, 0, 159, 63) },\r
53 { TRect(160, 0, 239, 63) },\r
54 { TRect( 0, 64, 79, 127) },\r
55 { TRect( 80, 64, 159, 127) },\r
56 { TRect(160, 64, 239, 127) },\r
57 { TRect( 0, 128, 79, 191) },\r
58 { TRect( 80, 128, 159, 191) },\r
59 { TRect(160, 128, 239, 191) },\r
60 { TRect( 0, 192, 79, 255) },\r
61 { TRect( 80, 192, 159, 255) },\r
62 { TRect(160, 192, 239, 255) },\r
63 { TRect( 0, 256, 79, 319) },\r
64 { TRect( 80, 256, 159, 319) },\r
65 { TRect(160, 256, 239, 319) },\r
66 { TRect( 0, 0, 0, 0) }\r
67};\r
68\r
69// PicoPad[] format: SACB RLDU\r
70const char *actionNames[] = {\r
71 "UP", "DOWN", "LEFT", "RIGHT", "B", "C", "A", "START",\r
72 0, 0, 0, 0, 0, 0, 0, 0, // Z, Y, X, MODE (enabled only when needed), ?, ?, ?, ?\r
73 0, 0, 0, 0, "VOLUME@UP", "VOLUME@DOWN", "NEXT@SAVE@SLOT", "PREV@SAVE@SLOT", // ?, ?, ?, ?, vol_up, vol_down, next_slot, prev_slot\r
74 0, 0, "PAUSE@EMU", "SAVE@STATE", "LOAD@STATE", 0, 0, "DONE" // ?, switch_renderer, [...], "FRAMESKIP@8", "AUTO@FRAMESKIP"\r
75};\r
76\r
77\r
78// globals are allowed, so why not to (ab)use them?\r
79//TInt machineUid = 0;\r
80int gamestate = PGS_Paused, gamestate_next = PGS_Paused;\r
81TPicoConfig *currentConfig = 0;\r
82static char noticeMsg[64]; // notice msg to draw\r
83static timeval noticeMsgTime = { 0, 0 }; // when started showing\r
84static CGameAudioMS *gameAudio = 0; // the audio object itself\r
85static int reset_timing, pico_was_reset;\r
86static int state_slot = 0;\r
87extern const char *RomFileName;\r
88extern RSemaphore initSemaphore;\r
89extern RSemaphore pauseSemaphore;\r
90\r
91// some forward declarations\r
92static void MainInit();\r
93static void MainExit();\r
94static void DumpMemInfo();\r
95void MainOldCleanup();\r
96\r
97\r
98class TPicoDirectScreenAccess : public MDirectScreenAccess\r
99{\r
100public: // implements MDirectScreenAccess\r
101 void Restart(RDirectScreenAccess::TTerminationReasons aReason);\r
102public: // implements MAbortDirectScreenAccess\r
103 void AbortNow(RDirectScreenAccess::TTerminationReasons aReason);\r
104};\r
105\r
106\r
107// just for a nicer grouping of WS related stuff\r
108class CGameWindow\r
109{\r
110public:\r
111 static void ConstructResourcesL(void);\r
112 static void FreeResources(void);\r
113 static void DoKeys(void);\r
114 static void DoKeysConfig(TUint &which);\r
115 static void RunEvents(TUint32 which);\r
116\r
117 static RWsSession* iWsSession;\r
118 static RWindowGroup iWsWindowGroup;\r
119 static RWindow iWsWindow;\r
120 static CWsScreenDevice* iWsScreen;\r
121 static CWindowGc* iWindowGc;\r
122 static TRequestStatus iWsEventStatus;\r
123// static TThreadId iLauncherThreadId;\r
124// static RDirectScreenAccess* iDSA;\r
125// static TRequestStatus iDSAstatus;\r
126 static TPicoDirectScreenAccess iPDSA;\r
127 static CDirectScreenAccess* iDSA;\r
128};\r
129\r
130\r
131static int snd_excess_add = 0, snd_excess_cnt = 0; // hack\r
132\r
133static void updateSound(void)\r
134{\r
135 int len = PsndLen;\r
136\r
137 snd_excess_cnt += snd_excess_add;\r
138 if (snd_excess_cnt >= 0x10000) {\r
139 snd_excess_cnt -= 0x10000;\r
140 if (PicoOpt&8) {\r
141 PsndOut[len*2] = PsndOut[len*2-2];\r
142 PsndOut[len*2+1] = PsndOut[len*2-1];\r
143 } else {\r
144 PsndOut[len] = PsndOut[len-1];\r
145 }\r
146 len++;\r
147 }\r
148\r
149 PsndOut = gameAudio->NextFrameL(len);\r
150 if(!PsndOut) { // sound output problems?\r
151 strcpy(noticeMsg, "SOUND@OUTPUT@ERROR;@SOUND@DISABLED");\r
152 gettimeofday(&noticeMsgTime, 0);\r
153 }\r
154}\r
155\r
156\r
157static void SkipFrame(void)\r
158{\r
159 PicoSkipFrame=1;\r
160 PicoFrame();\r
161 PicoSkipFrame=0;\r
162}\r
163\r
164\r
165static void simpleWait(int thissec, int lim_time)\r
166{\r
167 struct timeval tval;\r
168 int sleep = 0;\r
169\r
170 gettimeofday(&tval, 0);\r
171 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
172\r
173 sleep = lim_time - tval.tv_usec - 2000;\r
174 if (sleep > 0) {\r
175// User::After((sleep = lim_time - tval.tv_usec));\r
176 User::AfterHighRes(sleep);\r
177 }\r
178}\r
179\r
180\r
181static void TargetEpocGameL()\r
182{\r
183 char buff[24]; // fps count c string\r
184 struct timeval tval; // timing\r
185 int thissec = 0, frames_done = 0, frames_shown = 0;\r
186 int target_fps, target_frametime;\r
187 int i, lim_time;\r
188 //TRawEvent blevent;\r
189\r
190 MainInit();\r
191 buff[0] = 0;\r
192\r
193 // just to keep the backlight on (works only on UIQ2)\r
194 //blevent.Set(TRawEvent::EActive);\r
195\r
196 // loop?\r
197 for(;;) {\r
198 if(gamestate == PGS_Running) {\r
199 // switch context to other thread\r
200 User::After(50000);\r
201 // prepare window and stuff\r
202 CGameWindow::ConstructResourcesL();\r
203\r
204 // if the system has something to do, it should better do it now\r
205 User::After(50000);\r
206 //CPolledActiveScheduler::Instance()->Schedule();\r
207\r
208 // pal/ntsc might have changed, reset related stuff\r
209 if(Pico.m.pal) {\r
210 target_fps = 50;\r
211 if(!noticeMsgTime.tv_sec) strcpy(noticeMsg, "PAL@SYSTEM@/@50@FPS");\r
212 } else {\r
213 target_fps = 60;\r
214 if(!noticeMsgTime.tv_sec) strcpy(noticeMsg, "NTSC@SYSTEM@/@60@FPS");\r
215 }\r
216 target_frametime = 1000000/target_fps;\r
217 if(!noticeMsgTime.tv_sec && pico_was_reset)\r
218 gettimeofday(&noticeMsgTime, 0);\r
219\r
220 if (PsndOut) {\r
221 snd_excess_cnt = 0;\r
222 snd_excess_add = ((PsndRate - PsndLen*target_fps)<<16) / target_fps;\r
223 }\r
224\r
225 pico_was_reset = 0;\r
226 reset_timing = 1;\r
227\r
228 while(gamestate == PGS_Running) {\r
229 gettimeofday(&tval, 0);\r
230 if(reset_timing) {\r
231 reset_timing = 0;\r
232 thissec = tval.tv_sec;\r
233 frames_done = tval.tv_usec/target_frametime;\r
234 }\r
235\r
236 // show notice message?\r
237 char *notice = 0;\r
238 if(noticeMsgTime.tv_sec) {\r
239 if((tval.tv_sec*1000000+tval.tv_usec) - (noticeMsgTime.tv_sec*1000000+noticeMsgTime.tv_usec) > 2000000) // > 2.0 sec\r
240 noticeMsgTime.tv_sec = noticeMsgTime.tv_usec = 0;\r
241 else notice = noticeMsg;\r
242 }\r
243\r
244 // second changed?\r
245 if(thissec != tval.tv_sec) {\r
246#ifdef BENCHMARK\r
247 static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];\r
248 if(++bench == 10) {\r
249 bench = 0;\r
250 bench_fps_s = bench_fps;\r
251 bf[bfp++ & 3] = bench_fps;\r
252 bench_fps = 0;\r
253 }\r
254 bench_fps += frames_shown;\r
255 sprintf(buff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);\r
256#else\r
257 if(currentConfig->iFlags & 2) \r
258 sprintf(buff, "%02i/%02i", frames_shown, frames_done);\r
259#endif\r
260\r
261\r
262 thissec = tval.tv_sec;\r
263\r
264 if(PsndOut == 0 && currentConfig->iFrameskip >= 0) {\r
265 frames_done = frames_shown = 0;\r
266 } else {\r
267 // it is quite common for this implementation to leave 1 fame unfinished\r
268 // when second changes, but we don't want buffer to starve.\r
269 if(PsndOut && frames_done < target_fps && frames_done > target_fps-5) {\r
270 SkipFrame(); frames_done++;\r
271 }\r
272\r
273 frames_done -= target_fps; if (frames_done < 0) frames_done = 0;\r
274 frames_shown -= target_fps; if (frames_shown < 0) frames_shown = 0;\r
275 if (frames_shown > frames_done) frames_shown = frames_done;\r
276 }\r
277 }\r
278\r
279\r
280 lim_time = (frames_done+1) * target_frametime;\r
281 if(currentConfig->iFrameskip >= 0) { // frameskip enabled\r
282 for(i = 0; i < currentConfig->iFrameskip; i++) {\r
283 CGameWindow::DoKeys();\r
284 SkipFrame(); frames_done++;\r
285 if (PsndOut) { // do framelimitting if sound is enabled\r
286 gettimeofday(&tval, 0);\r
287 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
288 if(tval.tv_usec < lim_time) { // we are too fast\r
289 simpleWait(thissec, lim_time);\r
290 }\r
291 }\r
292 lim_time += target_frametime;\r
293 }\r
294 } else if(tval.tv_usec > lim_time) { // auto frameskip\r
295 // no time left for this frame - skip\r
296 CGameWindow::DoKeys();\r
297 SkipFrame(); frames_done++;\r
298 continue;\r
299 }\r
300\r
301 CGameWindow::DoKeys();\r
302 PicoFrame();\r
303\r
304 // check time\r
305 gettimeofday(&tval, 0);\r
306 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
307\r
308 // sleep if we are still too fast\r
309 if(PsndOut != 0 || currentConfig->iFrameskip < 0)\r
310 {\r
311 // TODO: check if User::After() is accurate\r
312 gettimeofday(&tval, 0);\r
313 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
314 if(tval.tv_usec < lim_time)\r
315 {\r
316 // we are too fast\r
317 simpleWait(thissec, lim_time);\r
318 }\r
319 }\r
320\r
321 CPolledActiveScheduler::Instance()->Schedule();\r
322\r
323 if (gamestate != PGS_Paused)\r
324 vidDrawFrame(notice, buff, frames_shown);\r
325\r
326 frames_done++; frames_shown++;\r
327 }\r
328\r
329 // save SRAM\r
330 if((currentConfig->iFlags & 1) && SRam.changed) {\r
331 saveLoadGame(0, 1);\r
332 SRam.changed = 0;\r
333 }\r
334 CGameWindow::FreeResources();\r
335 } else if(gamestate == PGS_Paused) {\r
336 DEBUGPRINT(_L("pausing.."));\r
337 pauseSemaphore.Wait();\r
338 } else if(gamestate == PGS_KeyConfig) {\r
339 // switch context to other thread\r
340 User::After(50000);\r
341 // prepare window and stuff\r
342 CGameWindow::ConstructResourcesL();\r
343\r
344 TUint whichAction = 0;\r
345 while(gamestate == PGS_KeyConfig) {\r
346 CGameWindow::DoKeysConfig(whichAction);\r
347 CPolledActiveScheduler::Instance()->Schedule();\r
348 if (gamestate != PGS_Paused)\r
349 vidKeyConfigFrame(whichAction);\r
350 User::After(150000);\r
351 }\r
352\r
353 CGameWindow::FreeResources();\r
354 } else if(gamestate == PGS_DebugHeap) {\r
355 #ifdef __DEBUG_PRINT\r
356 TInt cells = User::CountAllocCells();\r
357 TInt mem;\r
358 User::AllocSize(mem);\r
359 DEBUGPRINT(_L("worker: cels=%d, size=%d KB"), cells, mem/1024);\r
360 gamestate = gamestate_next;\r
361 #endif\r
362 } else if(gamestate == PGS_Quit) {\r
363 break;\r
364 }\r
365 }\r
366\r
367 MainExit();\r
368}\r
369\r
370\r
371// main initialization\r
372static void MainInit()\r
373{\r
374 DEBUGPRINT(_L("\r\n\r\nstarting.."));\r
375\r
376 // our thread might have been crashed previously, so many other objects may be still floating around\r
377 MainOldCleanup();\r
378\r
379 DEBUGPRINT(_L("CPolledActiveScheduler::NewL()"));\r
380 CPolledActiveScheduler::NewL(); // create Polled AS for the sound engine\r
381\r
382// HAL::Get(HALData::EMachineUid, machineUid); // find out the machine UID\r
383\r
384 DumpMemInfo();\r
385\r
386 // try to start pico\r
387 DEBUGPRINT(_L("PicoInit();"));\r
388 PicoInit();\r
389 PicoDrawSetColorFormat(2);\r
390 PicoWriteSound = updateSound;\r
391\r
392// if (pauseSemaphore.Handle() <= 0)\r
393// pauseSemaphore.CreateLocal(0);\r
394 DEBUGPRINT(_L("initSemaphore.Signal()"));\r
395 initSemaphore.Signal();\r
396}\r
397\r
398\r
399// does not return\r
400static void MainExit()\r
401{\r
402 RThread thisThread;\r
403\r
404 DEBUGPRINT(_L("%i: cleaning up.."), (TInt32) thisThread.Id());\r
405\r
406 // save SRAM\r
407 if((currentConfig->iFlags & 1) && SRam.changed) {\r
408 saveLoadGame(0, 1);\r
409 SRam.changed = 0;\r
410 }\r
411\r
412 PicoExit();\r
413// pauseSemaphore.Close();\r
414\r
415 if(gameAudio) delete gameAudio;\r
416\r
417 // Polled AS\r
418 delete CPolledActiveScheduler::Instance();\r
419}\r
420\r
421void MainOldCleanup()\r
422{\r
423 DEBUGPRINT(_L("MainOldCleanup.."));\r
424\r
425 // There was previously a handle leak here, so thread stuff was not cleaned\r
426 // and I thought I would have to do it mself.\r
427\r
428 // clean any resources which might be left after a thread crash\r
429 //CGameWindow::FreeResources(ETrue);\r
430\r
431 //if(CPolledActiveScheduler::Instance())\r
432 // delete CPolledActiveScheduler::Instance();\r
433}\r
434\r
435static void DumpMemInfo()\r
436{\r
437 TInt ramSize, ramSizeFree, romSize;\r
438 \r
439 HAL::Get(HALData::EMemoryRAM, ramSize);\r
440 HAL::Get(HALData::EMemoryRAMFree, ramSizeFree);\r
441 HAL::Get(HALData::EMemoryROM, romSize);\r
442\r
443 DEBUGPRINT(_L("ram=%dKB, ram_free=%dKB, rom=%dKB"), ramSize/1024, ramSizeFree/1024, romSize/1024);\r
444}\r
445\r
446\r
447TInt EmuThreadFunction(TAny*)\r
448{\r
449 const TUint32 exs = KExceptionAbort|KExceptionKill|KExceptionUserInterrupt|KExceptionFpe|KExceptionFault|KExceptionInteger|KExceptionDebug;\r
450 \r
451 DEBUGPRINT(_L("EmuThreadFunction()"));\r
452 User::SetExceptionHandler(ExceptionHandler, exs/*(TUint32) -1*/); // does not work?\r
453\r
454 //TInt pc, sp;\r
455 //asm volatile ("str pc, %0" : "=m" (pc) );\r
456 //asm volatile ("str sp, %0" : "=m" (sp) );\r
457 //RDebug::Print(_L("executing @ 0x%08x, sp=0x%08x"), pc, sp);\r
458\r
459/*\r
460 RDebug::Print(_L("Base Bottom Top Size RW Name"));\r
461 TBuf<4> l_r(_L("R")), l_w(_L("W")), l_d(_L("-"));\r
462 RChunk chunk;\r
463 TFullName chunkname;\r
464 TFindChunk findChunk(_L("*"));\r
465 while( findChunk.Next(chunkname) != KErrNotFound ) {\r
466 chunk.Open(findChunk);\r
467 RDebug::Print(_L("%08x %08x %08x %08x %S%S %S"), chunk.Base(), chunk.Base()+chunk.Bottom(), chunk.Base()+chunk.Top(), chunk.Size(), chunk.IsReadable() ? &l_r : &l_d, chunk.IsWritable() ? &l_w : &l_d, &chunkname);\r
468 chunk.Close();\r
469 }\r
470*/\r
471\r
472 // can't do that, will crash here\r
473// if(cleanup) {\r
474// DEBUGPRINT(_L("found old CTrapCleanup, deleting.."));\r
475// delete cleanup;\r
476// }\r
477 \r
478 CTrapCleanup *cleanup = CTrapCleanup::New();\r
479\r
480 TRAPD(error, TargetEpocGameL());\r
481\r
482 __ASSERT_ALWAYS(!error, User::Panic(_L("Picosmall"), error));\r
483 delete cleanup;\r
484\r
485 DEBUGPRINT(_L("exitting..")); \r
486 return 1;\r
487}\r
488\r
489\r
490void TPicoDirectScreenAccess::Restart(RDirectScreenAccess::TTerminationReasons aReason)\r
491{\r
492 DEBUGPRINT(_L("TPicoDirectScreenAccess::Restart(%i)"), aReason);\r
493\r
494// if (CGameWindow::iDSA) {\r
495// TRAPD(error, CGameWindow::iDSA->StartL());\r
496// if (error) DEBUGPRINT(_L("iDSA->StartL() error: %i"), error);\r
497// }\r
498}\r
499\r
500\r
501void TPicoDirectScreenAccess::AbortNow(RDirectScreenAccess::TTerminationReasons aReason)\r
502{\r
503 DEBUGPRINT(_L("TPicoDirectScreenAccess::AbortNow(%i)"), aReason);\r
504\r
505 // the WS wants us to stop, so let's obey\r
506 gamestate = PGS_Paused;\r
507}\r
508\r
509\r
510void CGameWindow::ConstructResourcesL()\r
511{\r
512 DEBUGPRINT(_L("ConstructResourcesL()"));\r
513 // connect to window server\r
514 // tried to create it globally and not re-connect everytime,\r
515 // but my window started to lose focus strangely\r
516 iWsSession = new(ELeave) RWsSession();\r
517 User::LeaveIfError(iWsSession->Connect());\r
518\r
519 // * Tell the Window Server not to mess about with our process priority\r
520 // * Also, because of the way legacy games are written, they never sleep\r
521 // * and thus never voluntarily yield the CPU. We set our process priority\r
522 // * to EPriorityForeground and hope that a Telephony application on\r
523 // * this device runs at EPriorityForeground as well. If not, tough! ;-)\r
524\r
525 iWsSession->ComputeMode(RWsSession::EPriorityControlDisabled);\r
526 RProcess me;\r
527 me.SetPriority(EPriorityForeground);\r
528\r
529 iWsScreen = new(ELeave) CWsScreenDevice(*iWsSession);\r
530 User::LeaveIfError(iWsScreen->Construct());\r
531// User::LeaveIfError(iWsScreen->CreateContext(iWindowGc));\r
532\r
533 iWsWindowGroup = RWindowGroup(*iWsSession);\r
534 User::LeaveIfError(iWsWindowGroup.Construct((TUint32)&iWsWindowGroup));\r
535 //iWsWindowGroup.SetOrdinalPosition(0);\r
536 //iWsWindowGroup.SetName(KServerWGName);\r
537 iWsWindowGroup.EnableScreenChangeEvents(); // flip events (EEventScreenDeviceChanged)\r
538 iWsWindowGroup.EnableFocusChangeEvents(); // EEventFocusGroupChanged\r
539 iWsWindowGroup.SetOrdinalPosition(0, 1); // TInt aPos, TInt aOrdinalPriority\r
540\r
541 iWsWindow=RWindow(*iWsSession);\r
542 User::LeaveIfError(iWsWindow.Construct(iWsWindowGroup, (TUint32)&iWsWindow));\r
543 iWsWindow.SetSize(iWsScreen->SizeInPixels());\r
544 iWsWindow.PointerFilter(EPointerFilterDrag, 0);\r
545 iWsWindow.SetPointerGrab(ETrue);\r
546 iWsWindow.SetVisible(ETrue);\r
547 iWsWindow.Activate();\r
548\r
549#if 0\r
550 // request access through RDirectScreenAccess api, but don't care about the result\r
551 // hangs?\r
552 RRegion *dsa_region = 0;\r
553 iDSA = new(ELeave) RDirectScreenAccess(*iWsSession);\r
554 if(iDSA->Construct() == KErrNone)\r
555 iDSA->Request(dsa_region, iDSAstatus, iWsWindow);\r
556 DEBUGPRINT(_L("DSA: %i"), dsa_region ? dsa_region->Count() : -1);\r
557#endif\r
558\r
559 TInt ret;\r
560\r
561 // request access through CDirectScreenAccess\r
562 iDSA = CDirectScreenAccess::NewL(*iWsSession, *iWsScreen, iWsWindow, iPDSA);\r
563\r
564 // now get the screenbuffer\r
565 TScreenInfoV01 screenInfo;\r
566 TPckg<TScreenInfoV01> sI(screenInfo);\r
567 UserSvr::ScreenInfo(sI);\r
568\r
569 if(!screenInfo.iScreenAddressValid)\r
570 User::Leave(KErrNotSupported);\r
571\r
572 DEBUGPRINT(_L("framebuffer=0x%08x (%dx%d)"), screenInfo.iScreenAddress,\r
573 screenInfo.iScreenSize.iWidth, screenInfo.iScreenSize.iHeight);\r
574 \r
575 // vidInit\r
576 DEBUGPRINT(_L("vidInit()"));\r
577 ret = vidInit((void *)screenInfo.iScreenAddress, 0);\r
578 DEBUGPRINT(_L("vidInit() done (%i)"), ret);\r
579\r
580 User::LeaveIfError(ret);\r
581\r
582 memset(keyFlags, 0, 256);\r
583 keyFlags[EStdKeyM600JogUp] = keyFlags[EStdKeyM600JogDown] = 2; // add "pulse only" for jog up/down\r
584 keyFlags[EStdKeyOff] = 0x40; // not configurable\r
585\r
586 // try to start the audio engine\r
587 static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0;\r
588\r
589 if(gamestate == PGS_Running && (currentConfig->iFlags & 4)) {\r
590 TInt err = 0;\r
591 if(PsndRate != PsndRate_old || (PicoOpt&11) != (PicoOpt_old&11) || Pico.m.pal != pal_old) {\r
592 // if rate changed, reset all enabled chips, else reset only those chips, which were recently enabled\r
593 //sound_reset(PsndRate != PsndRate_old ? PicoOpt : (PicoOpt&(PicoOpt^PicoOpt_old)));\r
594 sound_rerate();\r
595 }\r
596 if(!gameAudio || PsndRate != PsndRate_old || ((PicoOpt&8) ^ (PicoOpt_old&8)) || Pico.m.pal != pal_old) { // rate or stereo or pal/ntsc changed\r
597 if(gameAudio) delete gameAudio; gameAudio = 0;\r
598 DEBUGPRINT(_L("starting audio: %i len: %i stereo: %i, pal: %i"), PsndRate, PsndLen, PicoOpt&8, Pico.m.pal);\r
599 TRAP(err, gameAudio = CGameAudioMS::NewL(PsndRate, (PicoOpt&8) ? 1 : 0, Pico.m.pal ? 50 : 60));\r
600 }\r
601 if( gameAudio) {\r
602 TRAP(err, PsndOut = gameAudio->ResumeL());\r
603 }\r
604 if(err) {\r
605 if(gameAudio) delete gameAudio;\r
606 gameAudio = 0;\r
607 PsndOut = 0;\r
608 strcpy(noticeMsg, "SOUND@STARTUP@FAILED");\r
609 gettimeofday(&noticeMsgTime, 0);\r
610 }\r
611 PsndRate_old = PsndRate;\r
612 PicoOpt_old = PicoOpt;\r
613 pal_old = Pico.m.pal;\r
614 } else {\r
615 if(gameAudio) delete gameAudio;\r
616 gameAudio = 0;\r
617 PsndOut = 0;\r
618 }\r
619\r
620 CPolledActiveScheduler::Instance()->Schedule();\r
621\r
622 // start key WS event polling\r
623 iWsSession->EventReady(&iWsEventStatus);\r
624\r
625 iWsSession->Flush(); // check: short hang in UIQ2\r
626 User::After(1);\r
627\r
628 // I don't know why but the Window server sometimes hangs completely (hanging the phone too) after calling StartL()\r
629 // Is this a sync broblem? weird bug?\r
630 TRAP(ret, iDSA->StartL());\r
631 if (ret) DEBUGPRINT(_L("iDSA->StartL() error: %i"), ret);\r
632\r
633// User::After(1);\r
634// CPolledActiveScheduler::Instance()->Schedule();\r
635\r
636 DEBUGPRINT(_L("CGameWindow::ConstructResourcesL() finished."));\r
637}\r
638\r
639// this may be run even if there is nothing to free\r
640void CGameWindow::FreeResources()\r
641{\r
642 if(gameAudio) gameAudio->Pause();\r
643\r
644 //DEBUGPRINT(_L("CPolledActiveScheduler::Instance(): %08x"), CPolledActiveScheduler::Instance());\r
645 if(CPolledActiveScheduler::Instance())\r
646 CPolledActiveScheduler::Instance()->Schedule();\r
647\r
648#if 0\r
649 // free RDirectScreenAccess stuff (seems to be deleted automatically after crash?)\r
650 if(iDSA) {\r
651 iDSA->Cancel();\r
652 iDSA->Close();\r
653 delete iDSA;\r
654 }\r
655 iDSA = NULL;\r
656#endif\r
657 if(iDSA) delete iDSA;\r
658 iDSA = 0;\r
659\r
660 if(iWsSession->WsHandle() > 0 && iWsEventStatus != KRequestPending) // TODO: 2 UIQ2 (?)\r
661 iWsSession->EventReadyCancel();\r
662\r
663 if(iWsWindow.WsHandle() > 0)\r
664 iWsWindow.Close();\r
665\r
666 if(iWsWindowGroup.WsHandle() > 0)\r
667 iWsWindowGroup.Close();\r
668\r
669 // these must be deleted before calling iWsSession->Close()\r
670 if(iWsScreen) {\r
671 delete iWsScreen;\r
672 iWsScreen = NULL;\r
673 }\r
674\r
675 if(iWsSession->WsHandle() > 0) {\r
676 iWsSession->Close();\r
677 delete iWsSession;\r
678 }\r
679 \r
680 vidFree();\r
681\r
682 // emu might change renderer by itself, so we may need to sync config\r
683 if(currentConfig && currentConfig->iPicoOpt != PicoOpt) {\r
684 currentConfig->iFlags |= 0x80;\r
685 }\r
686}\r
687\r
688\r
689void CGameWindow::DoKeys(void)\r
690{\r
691 TWsEvent iWsEvent;\r
692 TInt iWsEventType;\r
693 unsigned long allActions = 0;\r
694 static unsigned long areaActions = 0, forceUpdate = 0;\r
695 int i, nEvents;\r
696\r
697 for(nEvents = 0; iWsEventStatus != KRequestPending; nEvents++)\r
698 {\r
699 iWsSession->GetEvent(iWsEvent);\r
700 iWsEventType = iWsEvent.Type();\r
701\r
702 // pointer events?\r
703 if(iWsEventType == EEventPointer) {\r
704 if(iWsEvent.Pointer()->iType == TPointerEvent::EButton1Up) {\r
705 areaActions = 0; // remove all directionals\r
706 } else { // if(iWsEvent.Pointer()->iType == TPointerEvent::EButton1Down) {\r
707 TPoint p = iWsEvent.Pointer()->iPosition;\r
708 const TPicoAreaConfigEntry *e = areaConfig + 1;\r
709 for(i = 0; !e->rect.IsEmpty(); e++, i++)\r
710 if(e->rect.Contains(p)) {\r
711 areaActions = currentConfig->iAreaBinds[i];\r
712 break;\r
713 }\r
714 //DEBUGPRINT(_L("pointer event: %i %i"), p.iX, p.iY);\r
715 }\r
716 }\r
717 else if(iWsEventType == EEventKeyDown || iWsEventType == EEventKeyUp) {\r
718 TInt iScanCode = iWsEvent.Key()->iScanCode;\r
719 //DEBUGPRINT(_L("key event: 0x%02x"), iScanCode);\r
720\r
721 if(iScanCode < 256)\r
722 {\r
723 if(iWsEventType == EEventKeyDown) {\r
724 keyFlags[iScanCode] |= 1;\r
725 for(i=0; i < 10; i++) {\r
726 if( pressedKeys[i] == (TUint8) iScanCode) break;\r
727 if(!pressedKeys[i]) { pressedKeys[i] = (TUint8) iScanCode; break; }\r
728 }\r
729 } else if(!(keyFlags[iScanCode]&2)) {\r
730 keyFlags[iScanCode] &= ~1;\r
731 for(i=0; i < 10; i++) {\r
732 if(pressedKeys[i] == (TUint8) iScanCode) { pressedKeys[i] = 0; break; }\r
733 }\r
734 }\r
735\r
736 // power?\r
737 if(iScanCode == EStdKeyOff) gamestate = PGS_Paused;\r
738 } else {\r
739 DEBUGPRINT(_L("weird scancode: 0x%02x"), iScanCode);\r
740 }\r
741 }\r
742 else if(iWsEventType == EEventScreenDeviceChanged) {\r
743 // ???\r
744 //User::After(500000);\r
745 //reset_timing = 1;\r
746 DEBUGPRINT(_L("EEventScreenDeviceChanged, focus: %i, our: %i"),\r
747 iWsSession->GetFocusWindowGroup(), iWsWindowGroup.Identifier());\r
748 }\r
749 else if(iWsEventType == EEventFocusGroupChanged) {\r
750 TInt focusGrpId = iWsSession->GetFocusWindowGroup();\r
751 DEBUGPRINT(_L("EEventFocusGroupChanged: %i, our: %i"),\r
752 focusGrpId, iWsWindowGroup.Identifier());\r
753 // if it is not us and not launcher that got focus, pause emu\r
754 if(focusGrpId != iWsWindowGroup.Identifier())\r
755 gamestate = PGS_Paused;\r
756 }\r
757\r
758 iWsEventStatus = KRequestPending;\r
759 iWsSession->EventReady(&iWsEventStatus);\r
760 }\r
761\r
762 if(nEvents || forceUpdate) {\r
763 allActions = areaActions;\r
764 forceUpdate = 0;\r
765\r
766 // add all pushed button actions\r
767 for(i = 9; i >= 0; i--) {\r
768 int scan = pressedKeys[i];\r
769 if(scan) {\r
770 if(keyFlags[scan] & 1) allActions |= currentConfig->iKeyBinds[scan];\r
771 if((keyFlags[scan]& 3)==3) forceUpdate = 1;\r
772 if(keyFlags[scan] & 2) keyFlags[scan] &= ~1;\r
773 }\r
774 }\r
775\r
776 PicoPad[0] = (unsigned short) allActions;\r
777 if(allActions & 0xFFFF0000) {\r
778 RunEvents(allActions >> 16);\r
779 areaActions = 0;\r
780 }\r
781 }\r
782}\r
783\r
784\r
785void CGameWindow::DoKeysConfig(TUint &which)\r
786{\r
787 TWsEvent iWsEvent;\r
788 int i;\r
789\r
790 while(iWsEventStatus != KRequestPending)\r
791 {\r
792 TUint currentActCode = 1 << which;\r
793\r
794 iWsSession->GetEvent(iWsEvent);\r
795\r
796 // pointer events?\r
797 if(iWsEvent.Type() == EEventPointer) {\r
798 TPoint p = iWsEvent.Pointer()->iPosition;\r
799 TRect prev(56, 0, 120, 26);\r
800 TRect next(120, 0, 180, 26);\r
801\r
802 if(iWsEvent.Pointer()->iType == TPointerEvent::EButton1Down) {\r
803 if(prev.Contains(p)) do { which = (which-1) & 0x1F; } while(!actionNames[which]);\r
804 else if(next.Contains(p)) do { which = (which+1) & 0x1F; } while(!actionNames[which]);\r
805 else if(which == 31) gamestate = PGS_Paused; // done\r
806 else {\r
807 const TPicoAreaConfigEntry *e = areaConfig + 1;\r
808 for(i = 0; e->rect != TRect(0,0,0,0); e++, i++)\r
809 if(e->rect.Contains(p)) {\r
810 currentConfig->iAreaBinds[i] ^= currentActCode;\r
811 break;\r
812 }\r
813 }\r
814 }\r
815 }\r
816 else if(iWsEvent.Type() == EEventKeyDown || iWsEvent.Type() == EEventKeyUp)\r
817 {\r
818 TUint scan = (TUint) iWsEvent.Key()->iScanCode;\r
819\r
820 // key events?\r
821 if(iWsEvent.Type() == EEventKeyDown) {\r
822 if(which == 31) {\r
823 gamestate = PGS_Paused;\r
824 } else if (scan < 256) {\r
825 if(!(keyFlags[scan]&0x40)) currentConfig->iKeyBinds[scan] ^= currentActCode;\r
826 }\r
827 }\r
828\r
829 // power?\r
830 if(iWsEvent.Key()->iScanCode == EStdKeyOff) gamestate = PGS_Paused;\r
831 }\r
832 else if(iWsEvent.Type() == EEventFocusGroupChanged) {\r
833 TInt focusGrpId = iWsSession->GetFocusWindowGroup();\r
834 // if we lost focus, exit config mode\r
835 if(focusGrpId != iWsWindowGroup.Identifier())\r
836 gamestate = PGS_Paused;\r
837 }\r
838\r
839// iWsEventStatus = KRequestPending;\r
840 iWsSession->EventReady(&iWsEventStatus);\r
841 }\r
842}\r
843\r
844\r
845void CGameWindow::RunEvents(TUint32 which)\r
846{\r
847 if(which & 0x4000) currentConfig->iFrameskip = -1;\r
848 if(which & 0x2000) currentConfig->iFrameskip = 8;\r
849 if(which & 0x1800) { // save or load (but not both)\r
850 if(PsndOut) gameAudio->Pause(); // this may take a while, so we pause sound output\r
851\r
852 vidDrawNotice((which & 0x1000) ? "LOADING@GAME" : "SAVING@GAME");\r
853 saveLoadGame(which & 0x1000);\r
854\r
855 if(PsndOut) PsndOut = gameAudio->ResumeL();\r
856 reset_timing = 1;\r
857 }\r
858 if(which & 0x0400) gamestate = PGS_Paused;\r
859 if(which & 0x0200) { // switch renderer\r
860 if(!(currentConfig->iScreenMode == TPicoConfig::PMFit &&\r
861 (currentConfig->iScreenRotation == TPicoConfig::PRot0 || currentConfig->iScreenRotation == TPicoConfig::PRot180))) {\r
862\r
863 PicoOpt^=0x10;\r
864 vidInit(0, 1);\r
865\r
866 strcpy(noticeMsg, (PicoOpt&0x10) ? "ALT@RENDERER" : "DEFAULT@RENDERER");\r
867 gettimeofday(&noticeMsgTime, 0);\r
868 }\r
869 }\r
870 if(which & 0x00c0) {\r
871 if(which&0x0080) {\r
872 state_slot -= 1;\r
873 if(state_slot < 0) state_slot = 9;\r
874 } else {\r
875 state_slot += 1;\r
876 if(state_slot > 9) state_slot = 0;\r
877 }\r
878 sprintf(noticeMsg, "SAVE@SLOT@%i@SELECTED", state_slot);\r
879 gettimeofday(&noticeMsgTime, 0);\r
880 }\r
881 if(which & 0x0020) if(gameAudio) gameAudio->ChangeVolume(0);\r
882 if(which & 0x0010) if(gameAudio) gameAudio->ChangeVolume(1);\r
883}\r
884\r
885\r
886// must use wrappers, or else will run into some weird loader error (see pico/area.c)\r
887static size_t fRead2(void *p, size_t _s, size_t _n, void *file)\r
888{\r
889 return fread(p, _s, _n, (FILE *) file);\r
890}\r
891\r
892static size_t fWrite2(void *p, size_t _s, size_t _n, void *file)\r
893{\r
894 return fwrite(p, _s, _n, (FILE *) file);\r
895}\r
896\r
897static size_t gzRead2(void *p, size_t, size_t _n, void *file)\r
898{\r
899 return gzread(file, p, _n);\r
900}\r
901\r
902static size_t gzWrite2(void *p, size_t, size_t _n, void *file)\r
903{\r
904 return gzwrite(file, p, _n);\r
905}\r
906\r
907\r
908// this function is shared between both threads\r
909int saveLoadGame(int load, int sram)\r
910{\r
911 int res = 0;\r
912\r
913 if(!RomFileName) return -1;\r
914\r
915 // make save filename\r
916 char saveFname[KMaxFileName];\r
917 strcpy(saveFname, RomFileName);\r
918 saveFname[KMaxFileName-8] = 0;\r
919 if(saveFname[strlen(saveFname)-4] == '.') saveFname[strlen(saveFname)-4] = 0;\r
920 if(sram) strcat(saveFname, ".srm");\r
921 else {\r
922 if(state_slot > 0 && state_slot < 10) sprintf(saveFname, "%s.%i", saveFname, state_slot);\r
923 strcat(saveFname, ".mds");\r
924 }\r
925\r
926 DEBUGPRINT(_L("saveLoad (%i, %i): %S"), load, sram, DO_CONV(saveFname));\r
927\r
928 if(sram) {\r
929 FILE *sramFile;\r
930 int sram_size = SRam.end-SRam.start+1;\r
931 if(SRam.reg_back & 4) sram_size=0x2000;\r
932 if(!SRam.data) return 0; // SRam forcefully disabled for this game\r
933 if(load) {\r
934 sramFile = fopen(saveFname, "rb");\r
935 if(!sramFile) return -1;\r
936 fread(SRam.data, 1, sram_size, sramFile);\r
937 fclose(sramFile);\r
938 } else {\r
939 // sram save needs some special processing\r
940 // see if we have anything to save\r
941 for(; sram_size > 0; sram_size--)\r
942 if(SRam.data[sram_size-1]) break;\r
943 \r
944 if(sram_size) {\r
945 sramFile = fopen(saveFname, "wb");\r
946 res = fwrite(SRam.data, 1, sram_size, sramFile);\r
947 res = (res != sram_size) ? -1 : 0;\r
948 fclose(sramFile);\r
949 }\r
950 }\r
951 return res;\r
952 } else {\r
953 void *PmovFile = NULL;\r
954 // try gzip first\r
955 if(currentConfig->iFlags & 0x80) {\r
956 strcat(saveFname, ".gz");\r
957 if( (PmovFile = gzopen(saveFname, load ? "rb" : "wb")) ) {\r
958 areaRead = gzRead2;\r
959 areaWrite = gzWrite2;\r
960 if(!load) gzsetparams(PmovFile, 9, Z_DEFAULT_STRATEGY);\r
961 } else\r
962 saveFname[strlen(saveFname)-3] = 0;\r
963 }\r
964 if(!PmovFile) { // gzip failed or was disabled\r
965 if( (PmovFile = fopen(saveFname, load ? "rb" : "wb")) ) {\r
966 areaRead = fRead2;\r
967 areaWrite = fWrite2;\r
968 }\r
969 }\r
970 if(PmovFile) {\r
971 PmovState(load ? 6 : 5, PmovFile); // load/save\r
972 strcpy(noticeMsg, load ? "GAME@LOADED" : "GAME@SAVED");\r
973 if(areaRead == gzRead2)\r
974 gzclose(PmovFile);\r
975 else fclose ((FILE *) PmovFile);\r
976 PmovFile = 0;\r
977 if (load) Pico.m.dirtyPal=1;\r
978 } else {\r
979 strcpy(noticeMsg, load ? "LOAD@FAILED" : "SAVE@FAILED");\r
980 res = -1;\r
981 }\r
982\r
983 gettimeofday(&noticeMsgTime, 0);\r
984 return res;\r
985 }\r
986}\r
987\r
988// static class members\r
989RWsSession* CGameWindow::iWsSession;\r
990RWindowGroup CGameWindow::iWsWindowGroup;\r
991RWindow CGameWindow::iWsWindow;\r
992CWsScreenDevice* CGameWindow::iWsScreen = NULL;\r
993CWindowGc* CGameWindow::iWindowGc = NULL;\r
994TRequestStatus CGameWindow::iWsEventStatus = KRequestPending;\r
995//RDirectScreenAccess* CGameWindow::iDSA;\r
996//TRequestStatus CGameWindow::iDSAstatus = KRequestPending;\r
997TPicoDirectScreenAccess CGameWindow::iPDSA;\r
998CDirectScreenAccess* CGameWindow::iDSA = NULL;\r
999\r