| 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 <hal.h>\r |
| 9 | \r |
| 10 | #include <stdio.h>\r |
| 11 | #include <stdlib.h>\r |
| 12 | #include <sys/time.h>\r |
| 13 | \r |
| 14 | #include "debug.h"\r |
| 15 | \r |
| 16 | #include "pico/picoInt.h"\r |
| 17 | #include "vid.h"\r |
| 18 | #include "SimpleServer.h"\r |
| 19 | #include "ClientServer.h"\r |
| 20 | //#include "polledAS.h"\r |
| 21 | #include "audio.h"\r |
| 22 | \r |
| 23 | #include <EZlib.h>\r |
| 24 | #include "zlib/gzio_symb.h"\r |
| 25 | \r |
| 26 | \r |
| 27 | #define BENCHMARK\r |
| 28 | //#define TEST_FRAMEBUFF\r |
| 29 | \r |
| 30 | // keycodes we care about\r |
| 31 | enum TPxxxKeyCodes {\r |
| 32 | EKeyPxxxPower = EKeyDevice0, //0xF842\r |
| 33 | EKeyPxxxBrowser = EKeyApplication0,\r |
| 34 | EKeyPxxxCamera = EKeyApplication1,\r |
| 35 | EKeyPxxxJogUp = EKeyDevice1,\r |
| 36 | EKeyPxxxJogDown = EKeyDevice2,\r |
| 37 | EKeyPxxxJogLeft = EKeyDevice3,\r |
| 38 | EKeyPxxxJogRight = EKeyDevice9,\r |
| 39 | EKeyPxxxJogInward= EKeyDevice8,\r |
| 40 | // FC keys\r |
| 41 | //EKeyPxxxFcOk = EKeyDevice8, // don't care about FC keycodes\r |
| 42 | };\r |
| 43 | // EKeyScreenDimension1 ~ EStdKeyF24 is sent when flip is closed,\r |
| 44 | // EKeyScreenDimension0 ~ EStdKeyF23 when opened\r |
| 45 | \r |
| 46 | enum TMotAKeyCodes {\r |
| 47 | EKeyMotAUp = EKeyDevice4, //0xF846\r |
| 48 | EKeyMotADown = EKeyDevice5,\r |
| 49 | EKeyMotALeft = EKeyDevice6,\r |
| 50 | EKeyMotARight = EKeyDevice7,\r |
| 51 | EKeyMotASelect = EKeyDevice8,\r |
| 52 | EKeyMotAButton1 = EKeyApplicationA,\r |
| 53 | EKeyMotAButton2 = EKeyApplicationB,\r |
| 54 | EKeyMotAHome = EKeyApplication0,\r |
| 55 | EKeyMotAShortcut = EKeyApplication1,\r |
| 56 | EKeyMotAVoice = EKeyDeviceA,\r |
| 57 | EKeyMotACamera = EKeyDeviceB,\r |
| 58 | EKeyMotAVolUp = EKeyIncVolume,\r |
| 59 | EKeyMotAVolDn = EKeyDecVolume,\r |
| 60 | EKeyMotASend = EKeyYes,\r |
| 61 | EKeyMotAEnd = EKeyNo,\r |
| 62 | };\r |
| 63 | \r |
| 64 | // scancodes we care about\r |
| 65 | enum TPxxxScanCodes {\r |
| 66 | EScanPxxxPower = EStdKeyDevice0, // 0xa4\r |
| 67 | EScanPxxxBrowser = EStdKeyApplication0,\r |
| 68 | EScanPxxxCamera = EStdKeyApplication1,\r |
| 69 | EScanPxxxJogUp = EStdKeyDevice1,\r |
| 70 | EScanPxxxJogDown = EStdKeyDevice2,\r |
| 71 | EScanPxxxJogLeft = EStdKeyDeviceE, // not consistent\r |
| 72 | EScanPxxxJogRight = EStdKeyDeviceD,\r |
| 73 | EScanPxxxJogInward= EStdKeyDevice8,\r |
| 74 | // FC keys\r |
| 75 | EScanPxxxFcOk = EStdKeyDeviceF,\r |
| 76 | EScanPxxxFcBack = EStdKeyDevice3,\r |
| 77 | EScanPxxxFcC = EStdKeyDeviceA,\r |
| 78 | EScanPxxxFcMenu = EStdKeyDevice9,\r |
| 79 | EScanPxxxFc0 = '0',\r |
| 80 | EScanPxxxFc1 = '1',\r |
| 81 | EScanPxxxFc2 = '2',\r |
| 82 | EScanPxxxFc3 = '3',\r |
| 83 | EScanPxxxFc4 = '4',\r |
| 84 | EScanPxxxFc5 = '5',\r |
| 85 | EScanPxxxFc6 = '6',\r |
| 86 | EScanPxxxFc7 = '7',\r |
| 87 | EScanPxxxFc8 = '8',\r |
| 88 | EScanPxxxFc9 = '9',\r |
| 89 | EScanPxxxFcHash = EStdKeyHash,\r |
| 90 | EScanPxxxFcAsterisk= EStdKeyNkpAsterisk,\r |
| 91 | };\r |
| 92 | \r |
| 93 | enum TMotAScanCodes {\r |
| 94 | EScanMotAUp = EStdKeyDevice4,\r |
| 95 | EScanMotADown = EStdKeyDevice5,\r |
| 96 | EScanMotALeft = EStdKeyDevice6,\r |
| 97 | EScanMotARight = EStdKeyDevice7,\r |
| 98 | EScanMotASelect = EStdKeyDevice8,\r |
| 99 | EScanMotAButton1 = EStdKeyApplicationA,\r |
| 100 | EScanMotAButton2 = EStdKeyApplicationB,\r |
| 101 | EScanMotAHome = EStdKeyApplication0,\r |
| 102 | EScanMotAShortcut = EStdKeyApplication1,\r |
| 103 | EScanMotAVoice = EStdKeyDeviceA,\r |
| 104 | EScanMotACamera = EStdKeyDeviceB,\r |
| 105 | EScanMotAVolUp = EStdKeyIncVolume,\r |
| 106 | EScanMotAVolDn = EStdKeyDecVolume,\r |
| 107 | EScanMotASend = EStdKeyYes,\r |
| 108 | EScanMotAEnd = EStdKeyNo,\r |
| 109 | // some extra codes, don't know if these are actually used\r |
| 110 | EScanMotAExtra = EStdKeyApplicationC,\r |
| 111 | EScanMotAEsc = EStdKeyApplicationD,\r |
| 112 | EScanMotAStart = EStdKeyApplicationE,\r |
| 113 | EScanMotASelect2 = EStdKeyApplicationF,\r |
| 114 | };\r |
| 115 | \r |
| 116 | \r |
| 117 | // list of key names and codes\r |
| 118 | TPicoKeyConfigEntry keyConfigPXXX[] = {\r |
| 119 | { EKeyPxxxPower, EScanPxxxPower, 0, -1, -1, "POWER" }, // 0\r |
| 120 | { EKeyPxxxBrowser, EScanPxxxBrowser, 0, -1, -1, "BROWSER" },\r |
| 121 | { EKeyPxxxCamera, EScanPxxxCamera, 0, -1, -1, "CAMERA" },\r |
| 122 | { EKeyPxxxJogUp, EScanPxxxJogUp, 2, -1, -1, "JOG@UP" },\r |
| 123 | { EKeyPxxxJogDown, EScanPxxxJogDown, 2, -1, -1, "JOG@DOWN" },\r |
| 124 | { EKeyPxxxJogLeft, EScanPxxxJogLeft, 0, -1, -1, "JOG@LEFT" }, // 5\r |
| 125 | { EKeyPxxxJogRight, EScanPxxxJogRight, 0, -1, -1, "JOG@RIGHT" },\r |
| 126 | { 0, EScanPxxxJogInward, 0, -1, -1, "JOG@INWARD" },\r |
| 127 | { 0, EScanPxxxFcOk, 0, -1, -1, "FC@OK" },\r |
| 128 | { 0, EScanPxxxFcBack, 0, -1, -1, "FC@BACK" },\r |
| 129 | { 0, EScanPxxxFcC, 0, -1, -1, "FC@C" }, // 10\r |
| 130 | { 0, EScanPxxxFcMenu, 0, -1, -1, "FC@MENU" },\r |
| 131 | { 0, EScanPxxxFc0, 0, -1, -1, "FC@0" },\r |
| 132 | { 0, EScanPxxxFc1, 0, -1, -1, "FC@1" },\r |
| 133 | { 0, EScanPxxxFc2, 0, -1, -1, "FC@2" },\r |
| 134 | { 0, EScanPxxxFc3, 0, -1, -1, "FC@3" },\r |
| 135 | { 0, EScanPxxxFc4, 0, -1, -1, "FC@4" },\r |
| 136 | { 0, EScanPxxxFc5, 0, -1, -1, "FC@5" },\r |
| 137 | { 0, EScanPxxxFc6, 0, -1, -1, "FC@6" },\r |
| 138 | { 0, EScanPxxxFc7, 0, -1, -1, "FC@7" },\r |
| 139 | { 0, EScanPxxxFc8, 0, -1, -1, "FC@8" },\r |
| 140 | { 0, EScanPxxxFc9, 0, -1, -1, "FC@9" },\r |
| 141 | { 0, EScanPxxxFcHash, 0, -1, -1, "FC@HASH" },\r |
| 142 | { 0, EScanPxxxFcAsterisk,0, -1, -1, "FC@AST" },\r |
| 143 | { 0, 0, 0, 0, 0, 0 }\r |
| 144 | };\r |
| 145 | \r |
| 146 | // Motorola A92x & A1000 support\r |
| 147 | TPicoKeyConfigEntry keyConfigMotA[] = {\r |
| 148 | { EKeyMotAUp, EScanMotAUp, 0, -1, -1, "UP" }, // 0\r |
| 149 | { EKeyMotADown, EScanMotADown, 0, -1, -1, "DOWN" },\r |
| 150 | { EKeyMotALeft, EScanMotALeft, 0, -1, -1, "LEFT" },\r |
| 151 | { EKeyMotARight, EScanMotARight, 0, -1, -1, "RIGHT" },\r |
| 152 | { EKeyMotASelect, EScanMotASelect, 0, -1, -1, "SELECT" },\r |
| 153 | { EKeyMotAButton1, EScanMotAButton1, 0, -1, -1, "BUTTON1" }, // 5\r |
| 154 | { EKeyMotAButton2, EScanMotAButton2, 0, -1, -1, "BUTTON2" },\r |
| 155 | { EKeyMotAHome, EScanMotAHome, 0, -1, -1, "HOME" },\r |
| 156 | { EKeyMotAShortcut, EScanMotAShortcut, 0, -1, -1, "SHORTCUT" },\r |
| 157 | { EKeyMotAVoice, EScanMotAVoice, 0, -1, -1, "VOICE" },\r |
| 158 | { EKeyMotACamera, EScanMotACamera, 0, -1, -1, "CAMERA" }, // 10\r |
| 159 | { EKeyMotAVolUp, EScanMotAVolUp, 0, -1, -1, "VOL@UP" },\r |
| 160 | { EKeyMotAVolDn, EScanMotAVolDn, 0, -1, -1, "VOL@DOWN" },\r |
| 161 | { EKeyMotASend, EScanMotASend, 0, -1, -1, "SEND" },\r |
| 162 | { EKeyMotAEnd, EScanMotAEnd, 0, -1, -1, "END" },\r |
| 163 | { 0, EScanMotAExtra, 0, -1, -1, "EXTRA" },\r |
| 164 | { 0, EScanMotAEsc, 0, -1, -1, "ESC" },\r |
| 165 | { 0, EScanMotAStart, 0, -1, -1, "START" },\r |
| 166 | { 0, EScanMotASelect2, 0, -1, -1, "SELECT" },\r |
| 167 | { 0, 0, 0, 0, 0, 0 }\r |
| 168 | };\r |
| 169 | \r |
| 170 | \r |
| 171 | // list of areas\r |
| 172 | TPicoAreaConfigEntry areaConfig[] = {\r |
| 173 | { TRect( 0, 0, 0, 0) },\r |
| 174 | // small corner bottons\r |
| 175 | { TRect( 0, 0, 15, 15) },\r |
| 176 | { TRect(192, 0, 207, 15) },\r |
| 177 | { TRect( 0, 304, 15, 319) },\r |
| 178 | { TRect(192, 304, 207, 319) },\r |
| 179 | // normal buttons\r |
| 180 | { TRect( 0, 0, 68, 63) },\r |
| 181 | { TRect( 69, 0, 138, 63) },\r |
| 182 | { TRect(139, 0, 207, 63) },\r |
| 183 | { TRect( 0, 64, 68, 127) },\r |
| 184 | { TRect( 69, 64, 138, 127) },\r |
| 185 | { TRect(139, 64, 207, 127) },\r |
| 186 | { TRect( 0, 128, 68, 191) },\r |
| 187 | { TRect( 69, 128, 138, 191) },\r |
| 188 | { TRect(139, 128, 207, 191) },\r |
| 189 | { TRect( 0, 192, 68, 255) },\r |
| 190 | { TRect( 69, 192, 138, 255) },\r |
| 191 | { TRect(139, 192, 207, 255) },\r |
| 192 | { TRect( 0, 256, 68, 319) },\r |
| 193 | { TRect( 69, 256, 138, 319) },\r |
| 194 | { TRect(139, 256, 207, 319) },\r |
| 195 | { TRect( 0, 0, 0, 0) }\r |
| 196 | };\r |
| 197 | \r |
| 198 | // PicoPad[] format: SACB RLDU\r |
| 199 | const char *actionNames[] = {\r |
| 200 | "UP", "DOWN", "LEFT", "RIGHT", "B", "C", "A", "START",\r |
| 201 | 0, 0, 0, 0, 0, 0, 0, 0, // Z, Y, X, MODE (enabled only when needed), ?, ?, ?, ?\r |
| 202 | 0, 0, 0, 0, 0, 0, "NEXT@SAVE@SLOT", "PREV@SAVE@SLOT", // ?, ?, ?, ?, mot_vol_up, mot_vol_down, next_slot, prev_slot\r |
| 203 | 0, 0, "PAUSE@EMU", "SAVE@STATE", "LOAD@STATE", "FRAMESKIP@8", "AUTO@FRAMESKIP", "DONE" // ?, switch_renderer\r |
| 204 | };\r |
| 205 | \r |
| 206 | \r |
| 207 | // globals are allowed, so why not to (ab)use them?\r |
| 208 | TInt machineUid = 0;\r |
| 209 | int gamestate = PGS_Paused, gamestate_prev = PGS_Paused;\r |
| 210 | TPicoConfig currentConfig;\r |
| 211 | TPicoKeyConfigEntry *keyConfig = 0; // currently used keys\r |
| 212 | static char noticeMsg[64]; // notice msg to draw\r |
| 213 | static timeval noticeMsgTime = { 0, 0 }; // when started showing\r |
| 214 | static RLibrary gameAudioLib; // audio object library\r |
| 215 | static _gameAudioNew gameAudioNew; // audio object maker\r |
| 216 | static IGameAudio *gameAudio = 0; // the audio object itself\r |
| 217 | static TProcessId launcherProcessId;\r |
| 218 | static int reset_timing, state_slot = 0;\r |
| 219 | extern const char *RomFileName;\r |
| 220 | extern int pico_was_reset; \r |
| 221 | #ifdef TEST_FRAMEBUFF\r |
| 222 | static TUint8 *iFrameBuffer = 0;\r |
| 223 | #endif\r |
| 224 | \r |
| 225 | // some forward declarations\r |
| 226 | static void MainInit();\r |
| 227 | static void MainExit();\r |
| 228 | static void CheckForLauncher();\r |
| 229 | static void DumpMemInfo();\r |
| 230 | \r |
| 231 | // just for a nicer grouping of WS related stuff\r |
| 232 | class CGameWindow\r |
| 233 | {\r |
| 234 | public:\r |
| 235 | static void ConstructResourcesL();\r |
| 236 | static void FreeResources();\r |
| 237 | static void DoKeys(timeval &time);\r |
| 238 | static void DoKeysConfig(TUint &which);\r |
| 239 | static void RunEvents(TUint32 which);\r |
| 240 | static void SendClientWsEvent(TInt type);\r |
| 241 | \r |
| 242 | static RWsSession iWsSession;\r |
| 243 | static RWindowGroup iWsWindowGroup;\r |
| 244 | static RWindow iWsWindow;\r |
| 245 | static CWsScreenDevice* iWsScreen;\r |
| 246 | static CWindowGc* iWindowGc;\r |
| 247 | static TRequestStatus iWsEventStatus;\r |
| 248 | static TThreadId iLauncherThreadId;\r |
| 249 | static RDirectScreenAccess* iDSA;\r |
| 250 | static TRequestStatus iDSAstatus;\r |
| 251 | };\r |
| 252 | \r |
| 253 | \r |
| 254 | void SkipFrame(int do_sound)\r |
| 255 | {\r |
| 256 | PicoSkipFrame=1;\r |
| 257 | PicoFrame();\r |
| 258 | PicoSkipFrame=0;\r |
| 259 | \r |
| 260 | if(do_sound && PsndOut) {\r |
| 261 | PsndOut = gameAudio->NextFrameL();\r |
| 262 | if(!PsndOut) { // sound output problems?\r |
| 263 | strcpy(noticeMsg, "SOUND@OUTPUT@ERROR;@SOUND@DISABLED");\r |
| 264 | gettimeofday(¬iceMsgTime, 0);\r |
| 265 | }\r |
| 266 | }\r |
| 267 | \r |
| 268 | /*\r |
| 269 | int total=0;\r |
| 270 | \r |
| 271 | // V-Blanking period:\r |
| 272 | if (Pico.video.reg[1]&0x20) SekInterrupt(6); // Set IRQ\r |
| 273 | Pico.video.status|=0x88; // V-Int happened / go into vblank\r |
| 274 | total+=SekRun(18560);\r |
| 275 | \r |
| 276 | // Active Scan:\r |
| 277 | if (Pico.video.reg[1]&0x40) Pico.video.status&=~8; // Come out of vblank if display is enabled\r |
| 278 | SekInterrupt(0); // Clear IRQ\r |
| 279 | total+=SekRun(127969-total);\r |
| 280 | */\r |
| 281 | }\r |
| 282 | \r |
| 283 | \r |
| 284 | void TargetEpocGameL()\r |
| 285 | {\r |
| 286 | char buff[24]; // fps count c string\r |
| 287 | struct timeval tval; // timing\r |
| 288 | int thissec = 0, frames_done = 0, frames_shown = 0;\r |
| 289 | int target_fps, target_frametime, too_fast, too_fast_time;\r |
| 290 | int i, underflow;\r |
| 291 | TRawEvent blevent;\r |
| 292 | \r |
| 293 | MainInit();\r |
| 294 | buff[0] = 0;\r |
| 295 | \r |
| 296 | // just to keep the backlight on..\r |
| 297 | blevent.Set(TRawEvent::EActive);\r |
| 298 | \r |
| 299 | // loop?\r |
| 300 | for(;;) {\r |
| 301 | if(gamestate == PGS_Running) {\r |
| 302 | // prepare window and stuff\r |
| 303 | CGameWindow::ConstructResourcesL();\r |
| 304 | \r |
| 305 | // if the system has something to do, it should better do it now\r |
| 306 | User::After(50000);\r |
| 307 | //CPolledActiveScheduler::Instance()->Schedule();\r |
| 308 | \r |
| 309 | // pal/ntsc might have changed, reset related stuff\r |
| 310 | if(Pico.m.pal) {\r |
| 311 | target_fps = 50;\r |
| 312 | if(!noticeMsgTime.tv_sec) strcpy(noticeMsg, "PAL@SYSTEM@/@50@FPS");\r |
| 313 | } else {\r |
| 314 | target_fps = 60;\r |
| 315 | if(!noticeMsgTime.tv_sec) strcpy(noticeMsg, "NTSC@SYSTEM@/@60@FPS");\r |
| 316 | }\r |
| 317 | target_frametime = 1000000/target_fps;\r |
| 318 | if(!noticeMsgTime.tv_sec && pico_was_reset)\r |
| 319 | gettimeofday(¬iceMsgTime, 0);\r |
| 320 | \r |
| 321 | pico_was_reset = too_fast = 0;\r |
| 322 | reset_timing = 1;\r |
| 323 | \r |
| 324 | while(gamestate == PGS_Running) {\r |
| 325 | gettimeofday(&tval, 0);\r |
| 326 | if(reset_timing) {\r |
| 327 | reset_timing = 0;\r |
| 328 | thissec = tval.tv_sec;\r |
| 329 | frames_done = tval.tv_usec/target_frametime;\r |
| 330 | }\r |
| 331 | \r |
| 332 | // show notice message?\r |
| 333 | char *notice = 0;\r |
| 334 | if(noticeMsgTime.tv_sec) {\r |
| 335 | if((tval.tv_sec*1000000+tval.tv_usec) - (noticeMsgTime.tv_sec*1000000+noticeMsgTime.tv_usec) > 2000000) // > 2.0 sec\r |
| 336 | noticeMsgTime.tv_sec = noticeMsgTime.tv_usec = 0;\r |
| 337 | else notice = noticeMsg;\r |
| 338 | }\r |
| 339 | \r |
| 340 | // second changed?\r |
| 341 | if(thissec != tval.tv_sec) {\r |
| 342 | #ifdef BENCHMARK\r |
| 343 | static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];\r |
| 344 | if(++bench == 10) {\r |
| 345 | bench = 0;\r |
| 346 | bench_fps_s = bench_fps;\r |
| 347 | bf[bfp++ & 3] = bench_fps;\r |
| 348 | bench_fps = 0;\r |
| 349 | }\r |
| 350 | bench_fps += frames_shown;\r |
| 351 | sprintf(buff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);\r |
| 352 | #else\r |
| 353 | if(currentConfig.iFlags & 2) \r |
| 354 | sprintf(buff, "%02i/%02i", frames_shown, frames_done);\r |
| 355 | #endif\r |
| 356 | thissec = tval.tv_sec;\r |
| 357 | if((thissec & 7) == 7) UserSvr::AddEvent(blevent);\r |
| 358 | // in is quite common for this implementation to leave 1 fame unfinished\r |
| 359 | // when second changes. This creates sound clicks, so it's probably better to\r |
| 360 | // skip that frame and render sound\r |
| 361 | if(PsndOut && frames_done < target_fps && frames_done > target_fps-5) {\r |
| 362 | SkipFrame(1); frames_done++;\r |
| 363 | }\r |
| 364 | // try to prevent sound buffer underflows by making sure we did _exactly_\r |
| 365 | // target_fps sound updates and copying last samples over and over again\r |
| 366 | if(PsndOut && frames_done < target_fps)\r |
| 367 | for(; frames_done < target_fps; frames_done++) {\r |
| 368 | PsndOut = gameAudio->DupeFrameL(underflow);\r |
| 369 | if(!PsndOut) { // sound output problems?\r |
| 370 | strcpy(noticeMsg, "SOUND@OUTPUT@ERROR;@SOUND@DISABLED");\r |
| 371 | gettimeofday(¬iceMsgTime, 0);\r |
| 372 | break;\r |
| 373 | }\r |
| 374 | if(underflow) break;\r |
| 375 | }\r |
| 376 | frames_done = frames_shown = 0;\r |
| 377 | }\r |
| 378 | \r |
| 379 | if(currentConfig.iFrameskip >= 0) { // frameskip enabled\r |
| 380 | for(i = 0; i < currentConfig.iFrameskip; i++) {\r |
| 381 | SkipFrame(frames_done < target_fps); frames_done++;\r |
| 382 | CGameWindow::DoKeys(tval);\r |
| 383 | }\r |
| 384 | } else if(tval.tv_usec > (frames_done+1)*target_frametime) { // auto frameskip\r |
| 385 | // no time left for this frame - skip\r |
| 386 | SkipFrame(1); frames_done++;\r |
| 387 | CGameWindow::DoKeys(tval);\r |
| 388 | too_fast = 0;\r |
| 389 | continue;\r |
| 390 | } else if(tval.tv_usec < (too_fast_time=frames_done*target_frametime)) { // we are too fast\r |
| 391 | if(++too_fast > 2) { User::After(too_fast_time-tval.tv_usec); too_fast = 0; }// sleep, but only if we are _really_ fast\r |
| 392 | }\r |
| 393 | \r |
| 394 | // draw\r |
| 395 | vidDrawFrame(notice, buff, frames_shown);\r |
| 396 | if(PsndOut && frames_done < target_fps) {\r |
| 397 | PsndOut = gameAudio->NextFrameL();\r |
| 398 | if(!PsndOut) { // sound output problems?\r |
| 399 | strcpy(noticeMsg, "SOUND@OUTPUT@ERROR;@SOUND@DISABLED");\r |
| 400 | gettimeofday(¬iceMsgTime, 0);\r |
| 401 | }\r |
| 402 | }\r |
| 403 | frames_done++; frames_shown++;\r |
| 404 | CGameWindow::DoKeys(tval);\r |
| 405 | }\r |
| 406 | \r |
| 407 | // save SRAM\r |
| 408 | if((currentConfig.iFlags & 1) && SRam.changed) {\r |
| 409 | saveLoadGame(0, 1);\r |
| 410 | SRam.changed = 0;\r |
| 411 | }\r |
| 412 | CGameWindow::SendClientWsEvent(EEventGamePaused);\r |
| 413 | CGameWindow::FreeResources();\r |
| 414 | } else if(gamestate == PGS_Paused) {\r |
| 415 | for(i = 0; gamestate == PGS_Paused; i++) {\r |
| 416 | User::After(250000);\r |
| 417 | if(!(i & 0x7F)) CheckForLauncher(); // every 32 secs\r |
| 418 | }\r |
| 419 | } else if(gamestate == PGS_KeyConfig) {\r |
| 420 | // prepare window and stuff\r |
| 421 | CGameWindow::ConstructResourcesL();\r |
| 422 | \r |
| 423 | TUint whichAction = 0;\r |
| 424 | while(gamestate == PGS_KeyConfig) {\r |
| 425 | vidKeyConfigFrame(whichAction, CGameWindow::iWsScreen->CurrentScreenMode());\r |
| 426 | CGameWindow::DoKeysConfig(whichAction);\r |
| 427 | User::After(200000);\r |
| 428 | }\r |
| 429 | \r |
| 430 | CGameWindow::SendClientWsEvent(EEventKeyCfgDone);\r |
| 431 | CGameWindow::SendClientWsEvent(EEventGamePaused);\r |
| 432 | CGameWindow::FreeResources();\r |
| 433 | } else if(gamestate == PGS_DebugHeap) {\r |
| 434 | #ifdef __DEBUG_PRINT\r |
| 435 | TInt cells = User::CountAllocCells();\r |
| 436 | TInt mem;\r |
| 437 | User::AllocSize(mem);\r |
| 438 | DEBUGPRINT(_L("worker: cels=%d, size=%d KB"), cells, mem/1024);\r |
| 439 | gamestate = gamestate_prev;\r |
| 440 | #endif\r |
| 441 | } else if(gamestate == PGS_Quit) {\r |
| 442 | break;\r |
| 443 | }\r |
| 444 | }\r |
| 445 | \r |
| 446 | MainExit();\r |
| 447 | }\r |
| 448 | \r |
| 449 | \r |
| 450 | // gameAudio default "maker", which simply leaves\r |
| 451 | IGameAudio *gameAudioNew_failer(TInt aRate, TBool aStereo, TInt aPcmFrames, TInt aBufferedFrames)\r |
| 452 | {\r |
| 453 | User::Leave(1);\r |
| 454 | return 0; // shouldn't happen\r |
| 455 | }\r |
| 456 | \r |
| 457 | \r |
| 458 | // main initialization\r |
| 459 | static void MainInit()\r |
| 460 | {\r |
| 461 | RProcess thisProcess, launcherProcess;\r |
| 462 | TInt err = KErrGeneral;\r |
| 463 | \r |
| 464 | DEBUGPRINT(_L("\r\n\r\nstarting.."));\r |
| 465 | \r |
| 466 | //CPolledActiveScheduler::NewL(); // create Polled AS for the sound engine\r |
| 467 | \r |
| 468 | // get launcher id\r |
| 469 | if(thisProcess.Owner(launcherProcess) == KErrNone) {\r |
| 470 | launcherProcessId = launcherProcess.Id();\r |
| 471 | launcherProcess.Close(); // messing with launcherProcess too much strangely reboots my phone\r |
| 472 | } else {\r |
| 473 | DEBUGPRINT(_L("%i: couldn't find owner, terminating.."), thisProcess.Id());\r |
| 474 | thisProcess.Terminate(1);\r |
| 475 | }\r |
| 476 | \r |
| 477 | // also get launcher thread id (for sending events, nasty way)\r |
| 478 | TFindThread findThread;\r |
| 479 | TFullName dummy1;\r |
| 480 | RThread tmpThread;\r |
| 481 | RProcess tmpProcess;\r |
| 482 | \r |
| 483 | while(findThread.Next(dummy1) == KErrNone)\r |
| 484 | {\r |
| 485 | tmpThread.Open(findThread);\r |
| 486 | tmpThread.Process(tmpProcess);\r |
| 487 | if(tmpProcess.Id() == launcherProcessId) {\r |
| 488 | CGameWindow::iLauncherThreadId = tmpThread.Id();\r |
| 489 | break;\r |
| 490 | }\r |
| 491 | tmpThread.Close();\r |
| 492 | tmpProcess.Close();\r |
| 493 | }\r |
| 494 | \r |
| 495 | // start event listening thread, which waits for GUI commands\r |
| 496 | if(StartThread() < 0) {\r |
| 497 | // communication thread failed to start, we serve no purpose now, so suicide\r |
| 498 | DEBUGPRINT(_L("%i: StartThread() failed, terminating.."), thisProcess.Id());\r |
| 499 | thisProcess.Terminate(1);\r |
| 500 | }\r |
| 501 | \r |
| 502 | HAL::Get(HALData::EMachineUid, machineUid); // find out the machine UID\r |
| 503 | \r |
| 504 | // get current dir\r |
| 505 | TFileName pfName = thisProcess.FileName();\r |
| 506 | TParse parse;\r |
| 507 | parse.Set(pfName, 0, 0);\r |
| 508 | TPtrC currDir = parse.DriveAndPath();\r |
| 509 | DEBUGPRINT(_L("current dir: %S"), &currDir);\r |
| 510 | \r |
| 511 | static TPtrC audio_dlls[] = { _L("audio_motorola.dll"), _L("audio_mediaserver.dll") };\r |
| 512 | \r |
| 513 | // find our audio object maker\r |
| 514 | for(TInt i=0; i < 2; i++) {\r |
| 515 | DEBUGPRINT(_L("trying audio DLL: %S"), &audio_dlls[i]);\r |
| 516 | err = gameAudioLib.Load(audio_dlls[i], currDir);\r |
| 517 | if(err == KErrNone) { // great, we loaded a dll!\r |
| 518 | gameAudioNew = (_gameAudioNew) gameAudioLib.Lookup(1);\r |
| 519 | if(!gameAudioNew) {\r |
| 520 | gameAudioLib.Close();\r |
| 521 | err = KErrGeneral;\r |
| 522 | DEBUGPRINT(_L(" loaded, but Lookup(1) failed."));\r |
| 523 | } else\r |
| 524 | break; // done\r |
| 525 | } else\r |
| 526 | DEBUGPRINT(_L(" load failed! (%i)"), err);;\r |
| 527 | }\r |
| 528 | \r |
| 529 | if(err != KErrNone)\r |
| 530 | gameAudioNew = gameAudioNew_failer;\r |
| 531 | else DEBUGPRINT(_L(" audio dll loaded!"));;\r |
| 532 | \r |
| 533 | DumpMemInfo();\r |
| 534 | \r |
| 535 | // try to start pico\r |
| 536 | DEBUGPRINT(_L("PicoInit();"));\r |
| 537 | PicoInit();\r |
| 538 | \r |
| 539 | #ifdef TEST_FRAMEBUFF\r |
| 540 | iFrameBuffer = (TUint8 *) malloc(208*320*4);\r |
| 541 | #endif\r |
| 542 | }\r |
| 543 | \r |
| 544 | \r |
| 545 | // does not return\r |
| 546 | static void MainExit()\r |
| 547 | {\r |
| 548 | RProcess thisProcess;\r |
| 549 | \r |
| 550 | DEBUGPRINT(_L("%i: cleaning up.."), thisProcess.Id());\r |
| 551 | \r |
| 552 | // save SRAM\r |
| 553 | if((currentConfig.iFlags & 1) && SRam.changed) {\r |
| 554 | saveLoadGame(0, 1);\r |
| 555 | SRam.changed = 0;\r |
| 556 | }\r |
| 557 | \r |
| 558 | PicoExit();\r |
| 559 | \r |
| 560 | if(gameAudio) delete gameAudio;\r |
| 561 | \r |
| 562 | if(gameAudioLib.Handle()) gameAudioLib.Close();\r |
| 563 | \r |
| 564 | // Polled AS\r |
| 565 | //delete CPolledActiveScheduler::Instance();\r |
| 566 | \r |
| 567 | DEBUGPRINT(_L("%i: terminating.."), thisProcess.Id()); \r |
| 568 | thisProcess.Terminate(0);\r |
| 569 | }\r |
| 570 | \r |
| 571 | \r |
| 572 | static void CheckForLauncher()\r |
| 573 | {\r |
| 574 | RProcess launcherProcess;\r |
| 575 | \r |
| 576 | // check for launcher, we are useless without it\r |
| 577 | if(launcherProcess.Open(launcherProcessId) != KErrNone || launcherProcess.ExitCategory().Length() != 0) {\r |
| 578 | #ifdef __DEBUG_PRINT\r |
| 579 | RProcess thisProcess;\r |
| 580 | DEBUGPRINT(_L("%i: launcher process is gone, terminating.."), thisProcess.Id());\r |
| 581 | if(launcherProcess.Handle()) {\r |
| 582 | TExitCategoryName ecn = launcherProcess.ExitCategory();\r |
| 583 | DEBUGPRINT(_L("%i: launcher exit category: %S"), thisProcess.Id(), &ecn);\r |
| 584 | launcherProcess.Close();\r |
| 585 | }\r |
| 586 | #endif\r |
| 587 | MainExit();\r |
| 588 | }\r |
| 589 | launcherProcess.Close();\r |
| 590 | }\r |
| 591 | \r |
| 592 | \r |
| 593 | void DumpMemInfo()\r |
| 594 | {\r |
| 595 | TInt ramSize, ramSizeFree, romSize;\r |
| 596 | \r |
| 597 | HAL::Get(HALData::EMemoryRAM, ramSize);\r |
| 598 | HAL::Get(HALData::EMemoryRAMFree, ramSizeFree);\r |
| 599 | HAL::Get(HALData::EMemoryROM, romSize);\r |
| 600 | \r |
| 601 | DEBUGPRINT(_L("ram=%dKB, ram_free=%dKB, rom=%dKB"), ramSize/1024, ramSizeFree/1024, romSize/1024);\r |
| 602 | }\r |
| 603 | \r |
| 604 | \r |
| 605 | \r |
| 606 | TInt E32Main()\r |
| 607 | {\r |
| 608 | // first thing's first\r |
| 609 | RThread().SetExceptionHandler(&ExceptionHandler, -1);\r |
| 610 | \r |
| 611 | //TInt pc, sp;\r |
| 612 | //asm volatile ("str pc, %0" : "=m" (pc) );\r |
| 613 | //asm volatile ("str sp, %0" : "=m" (sp) );\r |
| 614 | //RDebug::Print(_L("executing @ 0x%08x, sp=0x%08x"), pc, sp);\r |
| 615 | \r |
| 616 | /*\r |
| 617 | RDebug::Print(_L("Base Bottom Top Size RW Name"));\r |
| 618 | TBuf<4> l_r(_L("R")), l_w(_L("W")), l_d(_L("-"));\r |
| 619 | RChunk chunk;\r |
| 620 | TFullName chunkname;\r |
| 621 | TFindChunk findChunk(_L("*"));\r |
| 622 | while( findChunk.Next(chunkname) != KErrNotFound ) {\r |
| 623 | chunk.Open(findChunk);\r |
| 624 | 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 |
| 625 | chunk.Close();\r |
| 626 | }\r |
| 627 | */\r |
| 628 | \r |
| 629 | CTrapCleanup* cleanup=CTrapCleanup::New();\r |
| 630 | \r |
| 631 | TRAPD(error, TargetEpocGameL());\r |
| 632 | \r |
| 633 | __ASSERT_ALWAYS(!error, User::Panic(_L("Picosmall"), error));\r |
| 634 | delete cleanup;\r |
| 635 | \r |
| 636 | return 0;\r |
| 637 | }\r |
| 638 | \r |
| 639 | \r |
| 640 | void CGameWindow::ConstructResourcesL()\r |
| 641 | {\r |
| 642 | \r |
| 643 | // connect to window server\r |
| 644 | // tried to create it globally and not re-connect everytime,\r |
| 645 | // but my window started to lose focus strangely\r |
| 646 | User::LeaveIfError(iWsSession.Connect());\r |
| 647 | \r |
| 648 | // * Tell the Window Server not to mess about with our process priority\r |
| 649 | // * Also, because of the way legacy games are written, they never sleep\r |
| 650 | // * and thus never voluntarily yield the CPU. We set our process priority\r |
| 651 | // * to EPriorityForeground and hope that a Telephony application on\r |
| 652 | // * this device runs at EPriorityForeground as well. If not, tough! ;-)\r |
| 653 | \r |
| 654 | CGameWindow::iWsSession.ComputeMode(RWsSession::EPriorityControlDisabled);\r |
| 655 | RProcess me;\r |
| 656 | me.SetPriority(EPriorityForeground);\r |
| 657 | \r |
| 658 | iWsScreen=new(ELeave) CWsScreenDevice(iWsSession);\r |
| 659 | User::LeaveIfError(iWsScreen->Construct());\r |
| 660 | // User::LeaveIfError(iWsScreen->CreateContext(iWindowGc));\r |
| 661 | \r |
| 662 | iWsWindowGroup=RWindowGroup(iWsSession);\r |
| 663 | User::LeaveIfError(iWsWindowGroup.Construct((TUint32)&iWsWindowGroup));\r |
| 664 | //iWsWindowGroup.SetOrdinalPosition(0);\r |
| 665 | //iWsWindowGroup.SetName(KServerWGName);\r |
| 666 | iWsWindowGroup.EnableScreenChangeEvents(); // flip events (EEventScreenDeviceChanged)\r |
| 667 | iWsWindowGroup.EnableFocusChangeEvents(); // EEventFocusGroupChanged\r |
| 668 | iWsWindowGroup.SetOrdinalPosition(0, 1); // TInt aPos, TInt aOrdinalPriority\r |
| 669 | \r |
| 670 | iWsWindow=RWindow(iWsSession);\r |
| 671 | User::LeaveIfError(iWsWindow.Construct(iWsWindowGroup, (TUint32)&iWsWindow));\r |
| 672 | iWsWindow.SetSize(iWsScreen->SizeInPixels());\r |
| 673 | iWsWindow.PointerFilter(EPointerFilterDrag, 0);\r |
| 674 | iWsWindow.SetPointerGrab(ETrue);\r |
| 675 | iWsWindow.SetVisible(ETrue);\r |
| 676 | iWsWindow.Activate();\r |
| 677 | \r |
| 678 | // request access through RDirectScreenAccess api, but don't care about the result\r |
| 679 | RRegion *dsa_region = 0;\r |
| 680 | iDSA = new(ELeave) RDirectScreenAccess(iWsSession);\r |
| 681 | if(iDSA->Construct() == KErrNone)\r |
| 682 | iDSA->Request(dsa_region, iDSAstatus, iWsWindow);\r |
| 683 | DEBUGPRINT(_L("DSA: %i"), dsa_region ? dsa_region->Count() : -1);\r |
| 684 | \r |
| 685 | // now get the screenbuffer\r |
| 686 | TScreenInfoV01 screenInfo;\r |
| 687 | TPckg<TScreenInfoV01> sI(screenInfo);\r |
| 688 | UserSvr::ScreenInfo(sI);\r |
| 689 | \r |
| 690 | if(!screenInfo.iScreenAddressValid)\r |
| 691 | User::Leave(KErrNotSupported);\r |
| 692 | \r |
| 693 | #ifndef TEST_FRAMEBUFF\r |
| 694 | TUint8 *iFrameBuffer = (TUint8*) screenInfo.iScreenAddress;\r |
| 695 | #endif\r |
| 696 | TInt p800 = 0;\r |
| 697 | \r |
| 698 | switch(machineUid)\r |
| 699 | {\r |
| 700 | case 0x101f6b26: // A9xx & A10xx\r |
| 701 | case 0x101f6b27: // Chinese A10xx\r |
| 702 | case 0x101fd279: // P3x\r |
| 703 | iFrameBuffer += 32;\r |
| 704 | keyConfig = keyConfigMotA;\r |
| 705 | break;\r |
| 706 | case 0x101f408b: // P800\r |
| 707 | p800 = 1;\r |
| 708 | //case 0x101fb2ae: // P900\r |
| 709 | //case 0x10200ac6: // P910\r |
| 710 | default: \r |
| 711 | keyConfig = keyConfigPXXX;\r |
| 712 | break;\r |
| 713 | }\r |
| 714 | DEBUGPRINT(_L("framebuffer=0x%08x (%dx%d)"), iFrameBuffer,\r |
| 715 | screenInfo.iScreenSize.iWidth, screenInfo.iScreenSize.iHeight);\r |
| 716 | \r |
| 717 | // vidInit\r |
| 718 | User::LeaveIfError(vidInit(iWsScreen->DisplayMode(), iFrameBuffer, p800));\r |
| 719 | \r |
| 720 | // register for keyevents\r |
| 721 | for(TPicoKeyConfigEntry *e = keyConfig; e->name; e++) {\r |
| 722 | // release all keys\r |
| 723 | e->flags &= ~1;\r |
| 724 | if(e->flags & 0x80) {\r |
| 725 | // key disabled\r |
| 726 | e->handle1 = e->handle2 = -1;\r |
| 727 | continue;\r |
| 728 | }\r |
| 729 | e->handle1 = iWsWindowGroup.CaptureKeyUpAndDowns(e->scanCode, 0, 0, 2);\r |
| 730 | // although we only use UpAndDown events, register simple events too,\r |
| 731 | // just to prevent fireing their default actions.\r |
| 732 | if(e->keyCode) e->handle2 = iWsWindowGroup.CaptureKey(e->keyCode, 0, 0, 2); // priority of 1 is not enough on my phone\r |
| 733 | }\r |
| 734 | \r |
| 735 | // try to start the audio engine\r |
| 736 | static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0;\r |
| 737 | \r |
| 738 | if(gamestate == PGS_Running && (currentConfig.iFlags & 4)) {\r |
| 739 | TInt err = 0;\r |
| 740 | if(PsndRate != PsndRate_old || (PicoOpt&11) != (PicoOpt_old&11) || Pico.m.pal != pal_old) {\r |
| 741 | // if rate changed, reset all enabled chips, else reset only those chips, which were recently enabled\r |
| 742 | //sound_reset(PsndRate != PsndRate_old ? PicoOpt : (PicoOpt&(PicoOpt^PicoOpt_old)));\r |
| 743 | sound_rerate();\r |
| 744 | }\r |
| 745 | if(!gameAudio || PsndRate != PsndRate_old || ((PicoOpt&8) ^ (PicoOpt_old&8)) || Pico.m.pal != pal_old) { // rate or stereo or pal/ntsc changed\r |
| 746 | if(gameAudio) delete gameAudio; gameAudio = 0;\r |
| 747 | DEBUGPRINT(_L("starting audio: %i len: %i stereo: %i, pal: %i"), PsndRate, PsndLen, PicoOpt&8, Pico.m.pal);\r |
| 748 | TRAP(err, gameAudio = gameAudioNew(PsndRate, (PicoOpt&8) ? 1 : 0, PsndLen, Pico.m.pal ? 10 : 12));\r |
| 749 | }\r |
| 750 | if( gameAudio)\r |
| 751 | TRAP(err, PsndOut = gameAudio->ResumeL());\r |
| 752 | if(err) {\r |
| 753 | if(gameAudio) delete gameAudio;\r |
| 754 | gameAudio = 0;\r |
| 755 | PsndOut = 0;\r |
| 756 | strcpy(noticeMsg, "SOUND@STARTUP@FAILED");\r |
| 757 | gettimeofday(¬iceMsgTime, 0);\r |
| 758 | }\r |
| 759 | PsndRate_old = PsndRate;\r |
| 760 | PicoOpt_old = PicoOpt;\r |
| 761 | pal_old = Pico.m.pal;\r |
| 762 | } else {\r |
| 763 | if(gameAudio) delete gameAudio;\r |
| 764 | gameAudio = 0;\r |
| 765 | PsndOut = 0;\r |
| 766 | }\r |
| 767 | \r |
| 768 | // start key WS event polling\r |
| 769 | iWsSession.EventReady(&iWsEventStatus);\r |
| 770 | DEBUGPRINT(_L("CGameWindow::ConstructResourcesL() finished."));\r |
| 771 | }\r |
| 772 | \r |
| 773 | \r |
| 774 | void CGameWindow::FreeResources()\r |
| 775 | {\r |
| 776 | if(gameAudio) gameAudio->Pause();\r |
| 777 | \r |
| 778 | // free RDirectScreenAccess stuff\r |
| 779 | iDSA->Cancel();\r |
| 780 | iDSA->Close();\r |
| 781 | delete iDSA;\r |
| 782 | iDSA = NULL;\r |
| 783 | \r |
| 784 | iWsSession.EventReadyCancel();\r |
| 785 | \r |
| 786 | for(TPicoKeyConfigEntry *e = keyConfig; e->name; e++) {\r |
| 787 | if(e->handle2 >= 0) iWsWindowGroup.CancelCaptureKey(e->handle2);\r |
| 788 | if(e->handle1 >= 0) iWsWindowGroup.CancelCaptureKeyUpAndDowns(e->handle1);\r |
| 789 | }\r |
| 790 | \r |
| 791 | if(iWsWindow.WsHandle())\r |
| 792 | iWsWindow.Close();\r |
| 793 | \r |
| 794 | if(iWsWindowGroup.WsHandle())\r |
| 795 | iWsWindowGroup.Close();\r |
| 796 | \r |
| 797 | // these must be deleted before calling iWsSession.Close()\r |
| 798 | // delete iWindowGc;\r |
| 799 | // iWindowGc = NULL;\r |
| 800 | \r |
| 801 | delete iWsScreen;\r |
| 802 | iWsScreen = NULL;\r |
| 803 | \r |
| 804 | // emu might change renderer by itself, so we may need to sync config\r |
| 805 | if(PicoOpt != currentConfig.iPicoOpt) {\r |
| 806 | currentConfig.iFlags |= 0x80;\r |
| 807 | CGameWindow::SendClientWsEvent(EEventKeyCfgDone);\r |
| 808 | }\r |
| 809 | \r |
| 810 | if(iWsSession.WsHandle())\r |
| 811 | iWsSession.Close();\r |
| 812 | \r |
| 813 | vidFree();\r |
| 814 | \r |
| 815 | #ifdef TEST_FRAMEBUFF\r |
| 816 | FILE *tmp = fopen("d:\\temp\\screen.raw", "wb");\r |
| 817 | fwrite(iFrameBuffer, 1, 208*320*4, tmp);\r |
| 818 | fclose(tmp);\r |
| 819 | #endif\r |
| 820 | }\r |
| 821 | \r |
| 822 | \r |
| 823 | void CGameWindow::DoKeys(timeval &time)\r |
| 824 | {\r |
| 825 | TWsEvent iWsEvent;\r |
| 826 | TInt iWsEventType;\r |
| 827 | unsigned long allActions = 0;\r |
| 828 | static unsigned long areaActions = 0, forceUpdate = 0;\r |
| 829 | int i, nEvents;\r |
| 830 | \r |
| 831 | // detect if user is holding power button\r |
| 832 | static timeval powerPushed = { 0, 0 };\r |
| 833 | if(powerPushed.tv_sec) {\r |
| 834 | if((time.tv_sec*1000000+time.tv_usec) - (powerPushed.tv_sec*1000000+powerPushed.tv_usec) > 1000000) { // > 1 sec\r |
| 835 | gamestate = PGS_Paused;\r |
| 836 | powerPushed.tv_sec = powerPushed.tv_usec = 0;\r |
| 837 | }\r |
| 838 | }\r |
| 839 | \r |
| 840 | for(nEvents = 0; iWsEventStatus != KRequestPending; nEvents++)\r |
| 841 | {\r |
| 842 | iWsSession.GetEvent(iWsEvent);\r |
| 843 | iWsEventType = iWsEvent.Type();\r |
| 844 | \r |
| 845 | // pointer events?\r |
| 846 | if(iWsEventType == EEventPointer) {\r |
| 847 | if(iWsEvent.Pointer()->iType == TPointerEvent::EButton1Up) {\r |
| 848 | areaActions = 0; // remove all directionals\r |
| 849 | } else { // if(iWsEvent.Pointer()->iType == TPointerEvent::EButton1Down) {\r |
| 850 | TPoint p = iWsEvent.Pointer()->iPosition;\r |
| 851 | const TPicoAreaConfigEntry *e = areaConfig + 1;\r |
| 852 | for(i = 0; !e->rect.IsEmpty(); e++, i++)\r |
| 853 | if(e->rect.Contains(p)) {\r |
| 854 | areaActions = currentConfig.iAreaBinds[i];\r |
| 855 | break;\r |
| 856 | }\r |
| 857 | }\r |
| 858 | }\r |
| 859 | else if(iWsEventType == EEventKeyDown || iWsEventType == EEventKeyUp) {\r |
| 860 | TInt iScanCode = iWsEvent.Key()->iScanCode;\r |
| 861 | \r |
| 862 | for(TPicoKeyConfigEntry *e = keyConfig; e->name; e++)\r |
| 863 | if(iScanCode == e->scanCode) {\r |
| 864 | if(iWsEventType == EEventKeyDown) e->flags |= 1;\r |
| 865 | else if((e->flags & 2) == 0) e->flags &= ~1;\r |
| 866 | break;\r |
| 867 | }\r |
| 868 | \r |
| 869 | // power?\r |
| 870 | if(iScanCode == EScanPxxxPower || iScanCode == EScanMotAEnd) {\r |
| 871 | if(iWsEventType == EEventKeyDown)\r |
| 872 | powerPushed = time;\r |
| 873 | else powerPushed.tv_sec = powerPushed.tv_usec = 0;\r |
| 874 | }\r |
| 875 | }\r |
| 876 | else if(iWsEventType == EEventScreenDeviceChanged) {\r |
| 877 | // we have the priority, so the launcher will not be able to process this, but it has to\r |
| 878 | User::After(500000);\r |
| 879 | reset_timing = 1;\r |
| 880 | }\r |
| 881 | else if(iWsEventType == EEventFocusGroupChanged) {\r |
| 882 | TInt launcherGrpId = iWsSession.FindWindowGroupIdentifier(0, iLauncherThreadId);\r |
| 883 | TInt focusGrpId = iWsSession.GetFocusWindowGroup();\r |
| 884 | DEBUGPRINT(_L("EEventFocusGroupChanged: %i, our: %i, launcher: %i"),\r |
| 885 | focusGrpId, iWsWindowGroup.Identifier(), launcherGrpId);\r |
| 886 | // if it is not us and not launcher that got focus, pause emu\r |
| 887 | if(focusGrpId != iWsWindowGroup.Identifier() && focusGrpId != launcherGrpId)\r |
| 888 | gamestate = PGS_Paused;\r |
| 889 | }\r |
| 890 | \r |
| 891 | iWsEventStatus = KRequestPending;\r |
| 892 | iWsSession.EventReady(&iWsEventStatus);\r |
| 893 | }\r |
| 894 | \r |
| 895 | if(nEvents || forceUpdate) {\r |
| 896 | allActions = areaActions;\r |
| 897 | forceUpdate = 0;\r |
| 898 | \r |
| 899 | // add all pushed button actions\r |
| 900 | i = 0;\r |
| 901 | for(TPicoKeyConfigEntry *e = keyConfig; e->name; e++, i++) {\r |
| 902 | if(e->flags & 1) allActions |= currentConfig.iKeyBinds[i];\r |
| 903 | if((e->flags& 3) == 3) forceUpdate = 1;\r |
| 904 | if(e->flags & 2) e->flags &= ~1;\r |
| 905 | }\r |
| 906 | \r |
| 907 | PicoPad[0] = (unsigned short) allActions;\r |
| 908 | if(allActions & 0xFFFF0000) {\r |
| 909 | RunEvents(allActions >> 16);\r |
| 910 | areaActions = 0;\r |
| 911 | }\r |
| 912 | }\r |
| 913 | }\r |
| 914 | \r |
| 915 | \r |
| 916 | void CGameWindow::DoKeysConfig(TUint &which)\r |
| 917 | {\r |
| 918 | TWsEvent iWsEvent;\r |
| 919 | int i;\r |
| 920 | \r |
| 921 | // to detect if user is holding power button\r |
| 922 | static int powerIters = 0;\r |
| 923 | \r |
| 924 | while(iWsEventStatus != KRequestPending)\r |
| 925 | {\r |
| 926 | TUint currentActCode = 1 << which;\r |
| 927 | \r |
| 928 | iWsSession.GetEvent(iWsEvent);\r |
| 929 | \r |
| 930 | // pointer events?\r |
| 931 | if(iWsEvent.Type() == EEventPointer) {\r |
| 932 | TPoint p = iWsEvent.Pointer()->iPosition;\r |
| 933 | TRect prev(190, 112, 208, 126);\r |
| 934 | TRect next(190, 194, 208, 208);\r |
| 935 | \r |
| 936 | if(iWsEvent.Pointer()->iType == TPointerEvent::EButton1Down) {\r |
| 937 | if(prev.Contains(p)) do { which = (which-1) & 0x1F; } while(!actionNames[which]);\r |
| 938 | else if(next.Contains(p)) do { which = (which+1) & 0x1F; } while(!actionNames[which]);\r |
| 939 | else if(which == 31) gamestate = PGS_Paused; // done\r |
| 940 | else {\r |
| 941 | const TPicoAreaConfigEntry *e = areaConfig + 1;\r |
| 942 | for(i = 0; e->rect != TRect(0,0,0,0); e++, i++)\r |
| 943 | if(e->rect.Contains(p)) {\r |
| 944 | currentConfig.iAreaBinds[i] ^= currentActCode;\r |
| 945 | break;\r |
| 946 | }\r |
| 947 | }\r |
| 948 | }\r |
| 949 | }\r |
| 950 | else if(iWsEvent.Type() == EEventKeyDown || iWsEvent.Type() == EEventKeyUp)\r |
| 951 | {\r |
| 952 | //if(iWsEvent.Type() == EEventKey)\r |
| 953 | // DEBUGPRINT(_L("iWsEvent.Key()->iCode=0x%08x"), iWsEvent.Key()->iCode);\r |
| 954 | \r |
| 955 | //if(iWsEvent.Type() == EEventKeyDown)\r |
| 956 | // DEBUGPRINT(_L("EEventKeyDown iScanCode=0x%08x"), iWsEvent.Key()->iScanCode);\r |
| 957 | \r |
| 958 | //if(iWsEvent.Type() == EEventKeyUp)\r |
| 959 | // DEBUGPRINT(_L("EEventKeyUp iScanCode=0x%08x"), iWsEvent.Key()->iScanCode);\r |
| 960 | \r |
| 961 | // key events?\r |
| 962 | if(iWsEvent.Type() == EEventKeyDown) {\r |
| 963 | if(iWsScreen->CurrentScreenMode() == 1 && iWsEvent.Key()->iScanCode == EScanPxxxJogUp) {\r |
| 964 | do { which = (which-1) & 0x1F; } while(!actionNames[which]);\r |
| 965 | } else if(iWsScreen->CurrentScreenMode() == 1 && iWsEvent.Key()->iScanCode == EScanPxxxJogDown) {\r |
| 966 | do { which = (which+1) & 0x1F; } while(!actionNames[which]);\r |
| 967 | } else if(which == 31) {\r |
| 968 | gamestate = PGS_Paused;\r |
| 969 | if(iWsScreen->CurrentScreenMode()) // flip closed\r |
| 970 | vidDrawFCconfigDone();\r |
| 971 | } else {\r |
| 972 | i = 0;\r |
| 973 | for(TPicoKeyConfigEntry *e = keyConfig; e->name; e++, i++)\r |
| 974 | if(iWsEvent.Key()->iScanCode == e->scanCode)\r |
| 975 | if(!(e->flags&0x40)) currentConfig.iKeyBinds[i] ^= currentActCode;\r |
| 976 | }\r |
| 977 | }\r |
| 978 | \r |
| 979 | // power?\r |
| 980 | if(iWsEvent.Key()->iScanCode == EScanPxxxPower || iWsEvent.Key()->iScanCode == EScanMotAEnd)\r |
| 981 | {\r |
| 982 | if(iWsEvent.Type() == EEventKeyDown) powerIters = 1;\r |
| 983 | else if(iWsEvent.Type() == EEventKeyUp) powerIters = 0;\r |
| 984 | }\r |
| 985 | }\r |
| 986 | else if(iWsEvent.Type() == EEventScreenDeviceChanged) {\r |
| 987 | // trying to fix the P910 problem when something steals focus (and returns it after a while?)\r |
| 988 | User::After(300000);\r |
| 989 | }\r |
| 990 | else if(iWsEvent.Type() == EEventFocusGroupChanged) {\r |
| 991 | TInt launcherGrpId = iWsSession.FindWindowGroupIdentifier(0, iLauncherThreadId);\r |
| 992 | TInt focusGrpId = iWsSession.GetFocusWindowGroup();\r |
| 993 | DEBUGPRINT(_L("EEventFocusGroupChanged: %i, our: %i, launcher: %i"),\r |
| 994 | focusGrpId, iWsWindowGroup.Identifier(), launcherGrpId);\r |
| 995 | // if it is not us and not launcher that got focus, exit config mode\r |
| 996 | if(focusGrpId != iWsWindowGroup.Identifier() && focusGrpId != launcherGrpId) {\r |
| 997 | // don't give up that easily. May be the focus will be given back.\r |
| 998 | for (int i = 0; i < 4; i++) {\r |
| 999 | User::After(200000);\r |
| 1000 | focusGrpId = iWsSession.GetFocusWindowGroup();\r |
| 1001 | if(focusGrpId == iWsWindowGroup.Identifier() || focusGrpId == launcherGrpId) break;\r |
| 1002 | }\r |
| 1003 | if(focusGrpId != iWsWindowGroup.Identifier() && focusGrpId != launcherGrpId) \r |
| 1004 | // we probably should give up now\r |
| 1005 | gamestate = PGS_Paused;\r |
| 1006 | }\r |
| 1007 | }\r |
| 1008 | \r |
| 1009 | iWsEventStatus = KRequestPending;\r |
| 1010 | iWsSession.EventReady(&iWsEventStatus);\r |
| 1011 | }\r |
| 1012 | \r |
| 1013 | if(powerIters) { // iterations when power was down\r |
| 1014 | powerIters++;\r |
| 1015 | if(powerIters > 5) {\r |
| 1016 | gamestate = PGS_Paused;\r |
| 1017 | powerIters = 0;\r |
| 1018 | }\r |
| 1019 | }\r |
| 1020 | }\r |
| 1021 | \r |
| 1022 | \r |
| 1023 | void CGameWindow::RunEvents(TUint32 which)\r |
| 1024 | {\r |
| 1025 | if(which & 0x4000) currentConfig.iFrameskip = -1;\r |
| 1026 | if(which & 0x2000) currentConfig.iFrameskip = 8;\r |
| 1027 | if(which & 0x1800) { // save or load (but not both)\r |
| 1028 | if(PsndOut) gameAudio->Pause(); // this may take a while, so we pause sound output\r |
| 1029 | \r |
| 1030 | vidDrawNotice((which & 0x1000) ? "LOADING@GAME" : "SAVING@GAME");\r |
| 1031 | saveLoadGame(which & 0x1000);\r |
| 1032 | \r |
| 1033 | if(PsndOut) PsndOut = gameAudio->ResumeL();\r |
| 1034 | reset_timing = 1;\r |
| 1035 | }\r |
| 1036 | if(which & 0x0400) gamestate = PGS_Paused;\r |
| 1037 | if(which & 0x0200) { // switch renderer\r |
| 1038 | if(currentConfig.iScreenMode == TPicoConfig::PMCenter && !noticeMsgTime.tv_sec &&\r |
| 1039 | (currentConfig.iScreenRotation == TPicoConfig::PRot90 || currentConfig.iScreenRotation == TPicoConfig::PRot270)) {\r |
| 1040 | \r |
| 1041 | PicoOpt^=0x10;\r |
| 1042 | vidInit(iWsScreen->DisplayMode(), 0, 0, 1);\r |
| 1043 | \r |
| 1044 | strcpy(noticeMsg, (PicoOpt&0x10) ? "ALT@RENDERER" : "DEFAULT@RENDERER");\r |
| 1045 | gettimeofday(¬iceMsgTime, 0);\r |
| 1046 | }\r |
| 1047 | }\r |
| 1048 | if(which & 0x00c0) {\r |
| 1049 | if(which&0x0080) {\r |
| 1050 | state_slot -= 1;\r |
| 1051 | if(state_slot < 0) state_slot = 9;\r |
| 1052 | } else {\r |
| 1053 | state_slot += 1;\r |
| 1054 | if(state_slot > 9) state_slot = 0;\r |
| 1055 | }\r |
| 1056 | sprintf(noticeMsg, "SAVE@SLOT@%i@SELECTED", state_slot);\r |
| 1057 | gettimeofday(¬iceMsgTime, 0);\r |
| 1058 | }\r |
| 1059 | if(which & 0x0020) if(gameAudio) gameAudio->ChangeVolume(0); // for Motorolas (broken?)\r |
| 1060 | if(which & 0x0010) if(gameAudio) gameAudio->ChangeVolume(1);\r |
| 1061 | }\r |
| 1062 | \r |
| 1063 | \r |
| 1064 | // send event to launcher windowgroup (WS session MUST be alive)\r |
| 1065 | void CGameWindow::SendClientWsEvent(TInt type)\r |
| 1066 | {\r |
| 1067 | if(!iWsSession.Handle()) {\r |
| 1068 | DEBUGPRINT(_L("SendClientWsEvent(%i) called on dead iWsSession."), type);\r |
| 1069 | return;\r |
| 1070 | }\r |
| 1071 | \r |
| 1072 | TWsEvent event;\r |
| 1073 | event.SetType(type);\r |
| 1074 | event.SetTimeNow();\r |
| 1075 | \r |
| 1076 | TInt launcherGrpId = iWsSession.FindWindowGroupIdentifier(0, iLauncherThreadId);\r |
| 1077 | if(launcherGrpId != KErrNotFound)\r |
| 1078 | iWsSession.SendEventToWindowGroup(launcherGrpId, event);\r |
| 1079 | else DEBUGPRINT(_L("failed to send event %i to launcher."), event.Type());\r |
| 1080 | }\r |
| 1081 | \r |
| 1082 | \r |
| 1083 | size_t gzRead2(void *p, size_t _size, size_t _n, void *file)\r |
| 1084 | {\r |
| 1085 | return gzread(file, p, _n);\r |
| 1086 | }\r |
| 1087 | \r |
| 1088 | \r |
| 1089 | size_t gzWrite2(void *p, size_t _size, size_t _n, void *file)\r |
| 1090 | {\r |
| 1091 | return gzwrite(file, p, _n);\r |
| 1092 | }\r |
| 1093 | \r |
| 1094 | \r |
| 1095 | // this function is shared between both threads\r |
| 1096 | int saveLoadGame(int load, int sram)\r |
| 1097 | {\r |
| 1098 | int res = 0;\r |
| 1099 | \r |
| 1100 | if(!RomFileName) return -1;\r |
| 1101 | \r |
| 1102 | // make save filename\r |
| 1103 | char saveFname[KMaxFileName];\r |
| 1104 | strcpy(saveFname, RomFileName);\r |
| 1105 | saveFname[KMaxFileName-8] = 0;\r |
| 1106 | if(saveFname[strlen(saveFname)-4] == '.') saveFname[strlen(saveFname)-4] = 0;\r |
| 1107 | if(sram) strcat(saveFname, ".srm");\r |
| 1108 | else {\r |
| 1109 | if(state_slot > 0 && state_slot < 10) sprintf(saveFname, "%s.%i", saveFname, state_slot);\r |
| 1110 | strcat(saveFname, ".mds");\r |
| 1111 | }\r |
| 1112 | \r |
| 1113 | DEBUGPRINT(_L("saveLoad (%i, %i): %S"), load, sram, DO_CONV(saveFname));\r |
| 1114 | \r |
| 1115 | if(sram) {\r |
| 1116 | FILE *sramFile;\r |
| 1117 | int sram_size = SRam.end-SRam.start+1;\r |
| 1118 | if(SRam.reg_back & 4) sram_size=0x2000;\r |
| 1119 | if(!SRam.data) return 0; // SRam forcefully disabled for this game\r |
| 1120 | if(load) {\r |
| 1121 | sramFile = fopen(saveFname, "rb");\r |
| 1122 | if(!sramFile) return -1;\r |
| 1123 | fread(SRam.data, 1, sram_size, sramFile);\r |
| 1124 | fclose(sramFile);\r |
| 1125 | } else {\r |
| 1126 | // sram save needs some special processing\r |
| 1127 | // see if we have anything to save\r |
| 1128 | for(; sram_size > 0; sram_size--)\r |
| 1129 | if(SRam.data[sram_size-1]) break;\r |
| 1130 | \r |
| 1131 | if(sram_size) {\r |
| 1132 | sramFile = fopen(saveFname, "wb");\r |
| 1133 | res = fwrite(SRam.data, 1, sram_size, sramFile);\r |
| 1134 | res = (res != sram_size) ? -1 : 0;\r |
| 1135 | fclose((FILE *) sramFile);\r |
| 1136 | }\r |
| 1137 | }\r |
| 1138 | return res;\r |
| 1139 | } else {\r |
| 1140 | void *PmovFile = NULL;\r |
| 1141 | // try gzip first\r |
| 1142 | if(currentConfig.iFlags & 0x80) {\r |
| 1143 | strcat(saveFname, ".gz");\r |
| 1144 | if( (PmovFile = gzopen(saveFname, load ? "rb" : "wb")) ) {\r |
| 1145 | areaRead = gzRead2;\r |
| 1146 | areaWrite = gzWrite2;\r |
| 1147 | if(!load) gzsetparams(PmovFile, 9, Z_DEFAULT_STRATEGY);\r |
| 1148 | } else\r |
| 1149 | saveFname[strlen(saveFname)-3] = 0;\r |
| 1150 | }\r |
| 1151 | if(!PmovFile) { // gzip failed or was disabled\r |
| 1152 | if( (PmovFile = fopen(saveFname, load ? "rb" : "wb")) ) {\r |
| 1153 | areaRead = fread;\r |
| 1154 | areaWrite = fwrite;\r |
| 1155 | }\r |
| 1156 | }\r |
| 1157 | if(PmovFile) {\r |
| 1158 | PmovState(load ? 6 : 5, PmovFile);\r |
| 1159 | strcpy(noticeMsg, load ? "GAME@LOADED" : "GAME@SAVED");\r |
| 1160 | if(areaRead == gzRead2)\r |
| 1161 | gzclose(PmovFile);\r |
| 1162 | else fclose ((FILE *) PmovFile);\r |
| 1163 | PmovFile = 0;\r |
| 1164 | } else {\r |
| 1165 | strcpy(noticeMsg, load ? "LOAD@FAILED" : "SAVE@FAILED");\r |
| 1166 | res = -1;\r |
| 1167 | }\r |
| 1168 | \r |
| 1169 | gettimeofday(¬iceMsgTime, 0);\r |
| 1170 | return res;\r |
| 1171 | }\r |
| 1172 | }\r |
| 1173 | \r |
| 1174 | // static class members\r |
| 1175 | RWsSession CGameWindow::iWsSession;\r |
| 1176 | RWindowGroup CGameWindow::iWsWindowGroup;\r |
| 1177 | RWindow CGameWindow::iWsWindow;\r |
| 1178 | CWsScreenDevice* CGameWindow::iWsScreen = NULL;\r |
| 1179 | CWindowGc* CGameWindow::iWindowGc = NULL;\r |
| 1180 | TRequestStatus CGameWindow::iWsEventStatus = KRequestPending;\r |
| 1181 | TThreadId CGameWindow::iLauncherThreadId = 0;\r |
| 1182 | RDirectScreenAccess* CGameWindow::iDSA;\r |
| 1183 | TRequestStatus CGameWindow::iDSAstatus = KRequestPending;\r |