initial import
[picodrive.git] / platform / uiq3 / engine / main.cpp
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
34 enum TUsedScanCodes {\r
35         EStdKeyM600JogUp   = EStdKeyDevice1,\r
36         EStdKeyM600JogDown = EStdKeyDevice2,\r
37 };\r
38 \r
39 static unsigned char keyFlags[256];   // lsb->msb: key_down, pulse_only, ?, ?,  ?, ?, not_configurable, disabled\r
40 static unsigned char pressedKeys[11]; // List of pressed key scancodes, up to 10\r
41 \r
42 // list of areas\r
43 TPicoAreaConfigEntry 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
70 const 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
80 int gamestate = PGS_Paused, gamestate_next = PGS_Paused;\r
81 TPicoConfig *currentConfig = 0;\r
82 static char noticeMsg[64];                                      // notice msg to draw\r
83 static timeval noticeMsgTime = { 0, 0 };        // when started showing\r
84 static CGameAudioMS *gameAudio = 0;                     // the audio object itself\r
85 static int reset_timing, pico_was_reset;\r
86 static int state_slot = 0;\r
87 extern const char *RomFileName;\r
88 extern RSemaphore initSemaphore;\r
89 extern RSemaphore pauseSemaphore;\r
90 \r
91 // some forward declarations\r
92 static void MainInit();\r
93 static void MainExit();\r
94 static void DumpMemInfo();\r
95 void MainOldCleanup();\r
96 \r
97 \r
98 class TPicoDirectScreenAccess : public MDirectScreenAccess\r
99 {\r
100 public: // implements MDirectScreenAccess\r
101         void Restart(RDirectScreenAccess::TTerminationReasons aReason);\r
102 public: // 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
108 class CGameWindow\r
109 {\r
110 public:\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
131 static int snd_excess_add = 0, snd_excess_cnt = 0; // hack\r
132 \r
133 static 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
157 static void SkipFrame(void)\r
158 {\r
159         PicoSkipFrame=1;\r
160         PicoFrame();\r
161         PicoSkipFrame=0;\r
162 }\r
163 \r
164 \r
165 static 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
181 static 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
372 static 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
400 static 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
421 void 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
435 static 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
447 TInt 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
490 void 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
501 void 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
510 void 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
640 void 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
689 void 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
785 void 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
845 void 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
887 static 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
892 static 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
897 static 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
902 static 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
909 int 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
989 RWsSession*                             CGameWindow::iWsSession;\r
990 RWindowGroup                    CGameWindow::iWsWindowGroup;\r
991 RWindow                                 CGameWindow::iWsWindow;\r
992 CWsScreenDevice*                CGameWindow::iWsScreen = NULL;\r
993 CWindowGc*                              CGameWindow::iWindowGc = NULL;\r
994 TRequestStatus                  CGameWindow::iWsEventStatus = KRequestPending;\r
995 //RDirectScreenAccess*  CGameWindow::iDSA;\r
996 //TRequestStatus                        CGameWindow::iDSAstatus = KRequestPending;\r
997 TPicoDirectScreenAccess CGameWindow::iPDSA;\r
998 CDirectScreenAccess*    CGameWindow::iDSA = NULL;\r
999 \r