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