1 // Run module
2 #include "burner.h"
3
4 int bRunPause = 0;
5 int bAltPause = 0;
6
7 int bAlwaysDrawFrames = 0;
8
9 static bool bShowFPS = false;
10 static unsigned int nDoFPS = 0;
11
12 static bool bMute = false;
13 static int nOldAudVolume;
14
15 int kNetGame = 0; // Non-zero if Kaillera is being used
16
17 #ifdef FBA_DEBUG
18 int counter; // General purpose variable used when debugging
19 #endif
20
21 static unsigned int nNormalLast = 0; // Last value of timeGetTime()
22 static int nNormalFrac = 0; // Extra fraction we did
23
24 static bool bAppDoStep = 0;
25 static bool bAppDoFast = 0;
26 static bool bAppDoFasttoggled = 0;
27 static int nFastSpeed = 6;
28
29 // For System Macros (below)
30 static int prevPause = 0, prevFFWD = 0, prevSState = 0, prevLState = 0, prevUState = 0;
31 UINT32 prevPause_debounce = 0;
32
CheckSystemMacros()33 static void CheckSystemMacros() // These are the Pause / FFWD macros added to the input dialog
34 {
35 // Pause
36 if (macroSystemPause && macroSystemPause != prevPause && timeGetTime() > prevPause_debounce+90) {
37 if (bHasFocus) {
38 PostMessage(hScrnWnd, WM_KEYDOWN, VK_PAUSE, 0);
39 prevPause_debounce = timeGetTime();
40 }
41 }
42 prevPause = macroSystemPause;
43 // FFWD
44 if (!kNetGame) {
45 if (macroSystemFFWD) {
46 bAppDoFast = 1; prevFFWD = 1;
47 } else if (prevFFWD) {
48 bAppDoFast = 0; prevFFWD = 0;
49 }
50 }
51 // Load State
52 if (macroSystemLoadState && macroSystemLoadState != prevLState) {
53 PostMessage(hScrnWnd, WM_KEYDOWN, VK_F9, 0);
54 }
55 prevLState = macroSystemLoadState;
56 // Save State
57 if (macroSystemSaveState && macroSystemSaveState != prevSState) {
58 PostMessage(hScrnWnd, WM_KEYDOWN, VK_F10, 0);
59 }
60 prevSState = macroSystemSaveState;
61 // UNDO State
62 if (macroSystemUNDOState && macroSystemUNDOState != prevUState) {
63 scrnSSUndo();
64 }
65 prevUState = macroSystemUNDOState;
66 }
67
GetInput(bool bCopy)68 static int GetInput(bool bCopy)
69 {
70 static int i = 0;
71 InputMake(bCopy); // get input
72
73 CheckSystemMacros();
74
75 // Update Input dialog ever 3 frames
76 if (i == 0) {
77 InpdUpdate();
78 }
79
80 i++;
81
82 if (i >= 3) {
83 i = 0;
84 }
85
86 // Update Input Set dialog
87 InpsUpdate();
88 return 0;
89 }
90
91 static time_t fpstimer;
92 static unsigned int nPreviousFrames;
93
DisplayFPSInit()94 static void DisplayFPSInit()
95 {
96 nDoFPS = 0;
97 fpstimer = 0;
98 nPreviousFrames = nFramesRendered;
99 }
100
DisplayFPS()101 static void DisplayFPS()
102 {
103 TCHAR fpsstring[8];
104 time_t temptime = clock();
105 double fps = (double)(nFramesRendered - nPreviousFrames) * CLOCKS_PER_SEC / (temptime - fpstimer);
106 if (bAppDoFast) {
107 fps *= nFastSpeed+1;
108 }
109 _sntprintf(fpsstring, 7, _T("%2.2lf"), fps);
110 if (fpstimer && temptime - fpstimer>0) { // avoid strange fps values
111 VidSNewShortMsg(fpsstring, 0xDFDFFF, 480, 0);
112 }
113
114 fpstimer = temptime;
115 nPreviousFrames = nFramesRendered;
116 }
117
118 // define this function somewhere above RunMessageLoop()
ToggleLayer(unsigned char thisLayer)119 void ToggleLayer(unsigned char thisLayer)
120 {
121 nBurnLayer ^= thisLayer; // xor with thisLayer
122 VidRedraw();
123 VidPaint(0);
124 }
125
126 // With or without sound, run one frame.
127 // If bDraw is true, it's the last frame before we are up to date, and so we should draw the screen
RunFrame(int bDraw,int bPause)128 static int RunFrame(int bDraw, int bPause)
129 {
130 static int bPrevPause = 0;
131 static int bPrevDraw = 0;
132
133 if (bPrevDraw && !bPause) {
134 VidPaint(0); // paint the screen (no need to validate)
135
136 if (!nVidFullscreen && bVidDWMSync) // Sync to DWM (Win7+, windowed)
137 SuperWaitVBlank();
138 }
139
140 if (!bDrvOkay) {
141 return 1;
142 }
143
144 if (bPause) {
145 GetInput(false); // Update burner inputs, but not game inputs
146 if (bPause != bPrevPause) {
147 VidPaint(2); // Redraw the screen (to ensure mode indicators are updated)
148 }
149 } else {
150
151 nFramesEmulated++;
152 nCurrentFrame++;
153
154 if (kNetGame) {
155 GetInput(true); // Update inputs
156 if (KailleraGetInput()) { // Synchronize input with Kaillera
157 return 0;
158 }
159 } else {
160 if (nReplayStatus == 2) {
161 GetInput(false); // Update burner inputs, but not game inputs
162 if (ReplayInput()) { // Read input from file
163 SetPauseMode(1); // Replay has finished
164 bAppDoFast = 0;
165 bAppDoFasttoggled = 0; // Disable FFWD
166 MenuEnableItems();
167 InputSetCooperativeLevel(false, false);
168 return 0;
169 }
170 } else {
171 GetInput(true); // Update inputs
172 }
173 }
174
175 if (nReplayStatus == 1) {
176 RecordInput(); // Write input to file
177 }
178
179 if (bDraw) { // Draw Frame
180 nFramesRendered++;
181
182 if (VidFrame()) { // Do one frame
183 AudBlankSound();
184 }
185 } else { // frame skipping
186 pBurnDraw = NULL; // Make sure no image is drawn
187 BurnDrvFrame();
188 }
189
190 if (bShowFPS) {
191 if (nDoFPS < nFramesRendered) {
192 DisplayFPS();
193 nDoFPS = nFramesRendered + 30;
194 }
195 }
196
197 #ifdef INCLUDE_AVI_RECORDING
198 if (nAviStatus) {
199 if (AviRecordFrame(bDraw)) {
200 AviStop();
201 }
202 }
203 #endif
204 }
205
206 bPrevPause = bPause;
207 bPrevDraw = bDraw;
208
209 return 0;
210 }
211
212 // Callback used when DSound needs more sound
RunGetNextSound(int bDraw)213 static int RunGetNextSound(int bDraw)
214 {
215 if (nAudNextSound == NULL) {
216 return 1;
217 }
218
219 if (bRunPause) {
220 if (bAppDoStep) {
221 RunFrame(bDraw, 0);
222 memset(nAudNextSound, 0, nAudSegLen << 2); // Write silence into the buffer
223 } else {
224 RunFrame(bDraw, 1);
225 }
226
227 bAppDoStep = 0; // done one step
228 return 0;
229 }
230
231 if (bAppDoFast) { // do more frames
232 for (int i = 0; i < nFastSpeed; i++) {
233 if (!bAppDoFast) break; // break out if no longer in ffwd
234 #ifdef INCLUDE_AVI_RECORDING
235 if (nAviStatus) {
236 // Render frame with sound
237 pBurnSoundOut = nAudNextSound;
238 RunFrame(bDraw, 0);
239 } else {
240 RunFrame(0, 0);
241 }
242 #else
243 RunFrame(0, 0);
244 #endif
245 }
246 }
247
248 // Render frame with sound
249 pBurnSoundOut = nAudNextSound;
250 RunFrame(bDraw, 0);
251
252 if (WaveLog != NULL && pBurnSoundOut != NULL) { // log to the file
253 fwrite(pBurnSoundOut, 1, nBurnSoundLen << 2, WaveLog);
254 pBurnSoundOut = NULL;
255 }
256
257 if (bAppDoStep) {
258 memset(nAudNextSound, 0, nAudSegLen << 2); // Write silence into the buffer
259 }
260 bAppDoStep = 0; // done one step
261
262 return 0;
263 }
264
RunIdle()265 int RunIdle()
266 {
267 int nTime, nCount;
268
269 if (bAudPlaying) {
270 // Run with sound
271 AudSoundCheck();
272 return 0;
273 }
274
275 // Run without sound
276 nTime = timeGetTime() - nNormalLast;
277 nCount = (nTime * nAppVirtualFps - nNormalFrac) / 100000;
278 if (nCount <= 0) { // No need to do anything for a bit
279 //Sleep(2);
280 return 0;
281 }
282
283 nNormalFrac += nCount * 100000;
284 nNormalLast += nNormalFrac / nAppVirtualFps;
285 nNormalFrac %= nAppVirtualFps;
286
287 //if (bAppDoFast){ // Temporarily increase virtual fps
288 // nCount *= nFastSpeed;
289 //}
290 if (nCount > 100) { // Limit frame skipping
291 nCount = 100;
292 }
293 if (bRunPause) {
294 if (bAppDoStep) { // Step one frame
295 nCount = 10;
296 } else {
297 RunFrame(1, 1); // Paused
298 return 0;
299 }
300 }
301 bAppDoStep = 0;
302
303 if (bAppDoFast) { // do more frames
304 for (int i = 0; i < nFastSpeed; i++) {
305 #ifdef INCLUDE_AVI_RECORDING
306 if (nAviStatus) {
307 RunFrame(1, 0);
308 } else {
309 RunFrame(0, 0);
310 }
311 #else
312 RunFrame(0, 0);
313 #endif
314 }
315 }
316
317 if(!bAlwaysDrawFrames) {
318 for (int i = nCount / 10; i > 0; i--) { // Mid-frames
319 RunFrame(0, 0);
320 }
321 }
322 RunFrame(1, 0); // End-frame
323
324 return 0;
325 }
326
RunReset()327 int RunReset()
328 {
329 // Reset the speed throttling code
330 nNormalLast = 0; nNormalFrac = 0;
331
332 // Reset FPS display
333 DisplayFPSInit();
334
335 if (!bAudPlaying) {
336 // run without sound
337 nNormalLast = timeGetTime();
338 }
339 return 0;
340 }
341
RunInit()342 static int RunInit()
343 {
344 // Try to run with sound
345 AudSetCallback(RunGetNextSound);
346 AudSoundPlay();
347
348 RunReset();
349
350 return 0;
351 }
352
RunExit()353 static int RunExit()
354 {
355 nNormalLast = 0;
356 // Stop sound if it was playing
357 AudSoundStop();
358
359 bAppDoFast = 0;
360 bAppDoFasttoggled = 0;
361
362 return 0;
363 }
364
365 // Keyboard callback function, a way to provide a driver with shifted/unshifted ASCII data from the keyboard. see drv/msx/d_msx.cpp for usage
366 void (*cBurnerKeyCallback)(UINT8 code, UINT8 KeyType, UINT8 down) = NULL;
367
BurnerHandlerKeyCallback(MSG * Msg,INT32 KeyDown,INT32 KeyType)368 static void BurnerHandlerKeyCallback(MSG *Msg, INT32 KeyDown, INT32 KeyType)
369 {
370 INT32 shifted = (GetAsyncKeyState(VK_SHIFT) & 0x80000000) ? 0xf0 : 0;
371 INT32 scancode = (Msg->lParam >> 16) & 0xFF;
372 UINT8 keyboardState[256];
373 GetKeyboardState(keyboardState);
374 char charvalue[2];
375 if (ToAsciiEx(Msg->wParam, scancode, keyboardState, (LPWORD)&charvalue[0], 0, GetKeyboardLayout(0)) == 1) {
376 cBurnerKeyCallback(charvalue[0], shifted|KeyType, KeyDown);
377 }
378 }
379
380 // The main message loop
RunMessageLoop()381 int RunMessageLoop()
382 {
383 int bRestartVideo;
384 MSG Msg;
385
386 do {
387 bRestartVideo = 0;
388
389 // Remove pending initialisation messages from the queue
390 while (PeekMessage(&Msg, NULL, WM_APP + 0, WM_APP + 0, PM_NOREMOVE)) {
391 if (Msg.message != WM_QUIT) {
392 PeekMessage(&Msg, NULL, WM_APP + 0, WM_APP + 0, PM_REMOVE);
393 }
394 }
395
396 RunInit();
397
398 ShowWindow(hScrnWnd, nAppShowCmd); // Show the screen window
399 nAppShowCmd = SW_NORMAL;
400
401 SetForegroundWindow(hScrnWnd);
402
403 GameInpCheckLeftAlt();
404 GameInpCheckMouse(); // Hide the cursor
405
406 if (bVidDWMSync) {
407 bprintf(0, _T("[Win7+] Sync to DWM is enabled (if available).\n"));
408 SuperWaitVBlankInit();
409 }
410
411 if (bAutoLoadGameList && !bCmdOptUsed) {
412 static INT32 bLoaded = 0; // only do this once (at startup!)
413
414 if (bLoaded == 0)
415 PostMessage(hScrnWnd, WM_KEYDOWN, VK_F6, 0);
416 bLoaded = 1;
417 }
418
419 while (1) {
420 if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
421 // A message is waiting to be processed
422 if (Msg.message == WM_QUIT) { // Quit program
423 break;
424 }
425 if (Msg.message == (WM_APP + 0)) { // Restart video
426 bRestartVideo = 1;
427 break;
428 }
429
430 if (bMenuEnabled && nVidFullscreen == 0) { // Handle keyboard messages for the menu
431 if (MenuHandleKeyboard(&Msg)) {
432 continue;
433 }
434 }
435
436 if (Msg.message == WM_SYSKEYDOWN || Msg.message == WM_KEYDOWN) {
437 if (Msg.lParam & 0x20000000) {
438 // An Alt/AltGr-key was pressed
439 switch (Msg.wParam) {
440
441 #if defined (FBA_DEBUG)
442 case 'C': {
443 static int count = 0;
444 if (count == 0) {
445 count++;
446 { char* p = NULL; if (*p) { printf("crash...\n"); } }
447 }
448 break;
449 }
450 #endif
451
452 // 'Silence' & 'Sound Restored' Code (added by CaptainCPS-X)
453 case 'S': {
454 TCHAR buffer[60];
455 bMute = !bMute;
456
457 if (bMute) {
458 nOldAudVolume = nAudVolume;
459 nAudVolume = 0;// mute sound
460 _stprintf(buffer, FBALoadStringEx(hAppInst, IDS_SOUND_MUTE, true), nAudVolume / 100);
461 } else {
462 nAudVolume = nOldAudVolume;// restore volume
463 _stprintf(buffer, FBALoadStringEx(hAppInst, IDS_SOUND_MUTE_OFF, true), nAudVolume / 100);
464 }
465 if (AudSoundSetVolume() == 0) {
466 VidSNewShortMsg(FBALoadStringEx(hAppInst, IDS_SOUND_NOVOLUME, true));
467 } else {
468 VidSNewShortMsg(buffer);
469 }
470 break;
471 }
472
473 case VK_OEM_PLUS: {
474 if (bMute) break; // if mute, not do this
475 nOldAudVolume = nAudVolume;
476 TCHAR buffer[60];
477
478 if (GetAsyncKeyState(VK_CONTROL) & 0x80000000) {
479 nAudVolume += 100;
480 } else {
481 nAudVolume += 1000;
482 }
483
484 if (nAudVolume > 10000) {
485 nAudVolume = 10000;
486 }
487 if (AudSoundSetVolume() == 0) {
488 VidSNewShortMsg(FBALoadStringEx(hAppInst, IDS_SOUND_NOVOLUME, true));
489 } else {
490 _stprintf(buffer, FBALoadStringEx(hAppInst, IDS_SOUND_VOLUMESET, true), nAudVolume / 100);
491 VidSNewShortMsg(buffer);
492 }
493 break;
494 }
495 case VK_OEM_MINUS: {
496 if (bMute) break; // if mute, not do this
497 nOldAudVolume = nAudVolume;
498 TCHAR buffer[60];
499
500 if (GetAsyncKeyState(VK_CONTROL) & 0x80000000) {
501 nAudVolume -= 100;
502 } else {
503 nAudVolume -= 1000;
504 }
505
506 if (nAudVolume < 0) {
507 nAudVolume = 0;
508 }
509 if (AudSoundSetVolume() == 0) {
510 VidSNewShortMsg(FBALoadStringEx(hAppInst, IDS_SOUND_NOVOLUME, true));
511 } else {
512 _stprintf(buffer, FBALoadStringEx(hAppInst, IDS_SOUND_VOLUMESET, true), nAudVolume / 100);
513 VidSNewShortMsg(buffer);
514 }
515 break;
516 }
517 case VK_MENU: {
518 continue;
519 }
520 }
521 } else {
522
523 if (cBurnerKeyCallback)
524 BurnerHandlerKeyCallback(&Msg, (Msg.message == WM_KEYDOWN) ? 1 : 0, 0);
525
526 switch (Msg.wParam) {
527
528 #if defined (FBA_DEBUG)
529 case 'N':
530 counter--;
531 if (counter < 0) {
532 bprintf(PRINT_IMPORTANT, _T("*** New counter value: %04X (%d).\n"), counter, counter);
533 } else {
534 bprintf(PRINT_IMPORTANT, _T("*** New counter value: %04X.\n"), counter);
535 }
536 break;
537 case 'M':
538 counter++;
539 if (counter < 0) {
540 bprintf(PRINT_IMPORTANT, _T("*** New counter value: %04X (%d).\n"), counter, counter);
541 } else {
542 bprintf(PRINT_IMPORTANT, _T("*** New counter value: %04X.\n"), counter);
543 }
544 break;
545 #endif
546 case VK_ESCAPE: {
547 if (hwndChat) {
548 DeActivateChat();
549 } else {
550 if (bCmdOptUsed & 1) {
551 PostQuitMessage(0);
552 } else {
553 if (nVidFullscreen) {
554 nVidFullscreen = 0;
555 POST_INITIALISE_MESSAGE;
556 }
557 }
558 }
559 break;
560 }
561 case VK_RETURN: {
562 if (hwndChat) {
563 int i = 0;
564 while (EditText[i]) {
565 if (EditText[i++] != 0x20) {
566 break;
567 }
568 }
569 if (i) {
570 Kaillera_Chat_Send(TCHARToANSI(EditText, NULL, 0));
571 //kailleraChatSend(TCHARToANSI(EditText, NULL, 0));
572 }
573 DeActivateChat();
574
575 break;
576 }
577 if (GetAsyncKeyState(VK_CONTROL) & 0x80000000) {
578 bMenuEnabled = !bMenuEnabled;
579 POST_INITIALISE_MESSAGE;
580
581 break;
582 }
583
584 break;
585 }
586 case VK_F1: {
587 bool bOldAppDoFast = bAppDoFast;
588
589 if (kNetGame) {
590 break;
591 }
592
593 if (((GetAsyncKeyState(VK_CONTROL) | GetAsyncKeyState(VK_SHIFT)) & 0x80000000) == 0) {
594 if (bRunPause) {
595 bAppDoStep = 1;
596 } else {
597 bAppDoFast = 1;
598 }
599 }
600
601 if ((GetAsyncKeyState(VK_SHIFT) & 0x80000000) && !GetAsyncKeyState(VK_CONTROL)) { // Shift-F1: toggles FFWD state
602 bAppDoFast = !bAppDoFast;
603 bAppDoFasttoggled = bAppDoFast;
604 }
605
606 if (bOldAppDoFast != bAppDoFast) {
607 DisplayFPSInit(); // resync fps display
608 }
609 break;
610 }
611 case VK_BACK: {
612 if ((GetAsyncKeyState(VK_SHIFT) & 0x80000000) && !GetAsyncKeyState(VK_CONTROL))
613 { // Shift-Backspace: toggles recording/replay frame counter
614 bReplayFrameCounterDisplay = !bReplayFrameCounterDisplay;
615 if (!bReplayFrameCounterDisplay) {
616 VidSKillTinyMsg();
617 }
618 } else
619 { // Backspace: toggles FPS counter
620 bShowFPS = !bShowFPS;
621 if (bShowFPS) {
622 DisplayFPSInit();
623 } else {
624 VidSKillShortMsg();
625 VidSKillOSDMsg();
626 }
627 }
628 break;
629 }
630 case 'T': {
631 if (kNetGame && hwndChat == NULL) {
632 if (AppMessage(&Msg)) {
633 ActivateChat();
634 }
635 }
636 break;
637 }
638 }
639 }
640 } else {
641 if (Msg.message == WM_SYSKEYUP || Msg.message == WM_KEYUP) {
642
643 if (cBurnerKeyCallback)
644 BurnerHandlerKeyCallback(&Msg, (Msg.message == WM_KEYDOWN) ? 1 : 0, 0);
645
646 switch (Msg.wParam) {
647 case VK_MENU:
648 continue;
649 case VK_F1: {
650 bool bOldAppDoFast = bAppDoFast;
651
652 if (!bAppDoFasttoggled)
653 bAppDoFast = 0;
654 bAppDoFasttoggled = 0;
655 if (bOldAppDoFast != bAppDoFast) {
656 DisplayFPSInit(); // resync fps display
657 }
658 break;
659 }
660 }
661 }
662 }
663
664 // Check for messages for dialogs etc.
665 if (AppMessage(&Msg)) {
666 if (TranslateAccelerator(hScrnWnd, hAccel, &Msg) == 0) {
667 if (hwndChat) {
668 TranslateMessage(&Msg);
669 }
670 DispatchMessage(&Msg);
671 }
672 }
673 } else {
674 // No messages are waiting
675 SplashDestroy(0);
676 RunIdle();
677 }
678 }
679
680 RunExit();
681 MediaExit();
682 if (bRestartVideo) {
683 MediaInit();
684 PausedRedraw();
685 }
686 } while (bRestartVideo);
687
688 return 0;
689 }
690
691