1 // Support functions for blitters that use DirectX
2 #include "burner.h"
3 
4 #if !defined BUILD_X64_EXE
5  #include "vid_directx_support.h"
6 #endif
7 
8 #include <initguid.h>
9 #define DIRECT3D_VERSION 0x0700							// Use this Direct3D version
10 
11 #if defined BUILD_X64_EXE
12  #include "vid_directx_support.h"
13 #endif
14 
15 #include "ddraw_core.h"
16 
17 // ---------------------------------------------------------------------------
18 // General
19 
20 static IDirectDraw7* pDD = NULL;
21 
VidSExit()22 void VidSExit()
23 {
24 	pDD = NULL;
25 }
26 
VidSInit(IDirectDraw7 * pDD7)27 int VidSInit(IDirectDraw7* pDD7)
28 {
29 	pDD = pDD7;
30 
31 	if (pDD == NULL) {
32 		return 1;
33 	}
34 
35 	return 0;
36 }
37 
38 // ---------------------------------------------------------------------------
39 
40 // Get bit-depth of a surface (15/16/24/32)
41 
VidSGetSurfaceDepth(IDirectDrawSurface7 * pSurf)42 int VidSGetSurfaceDepth(IDirectDrawSurface7* pSurf)
43 {
44 	DDPIXELFORMAT ddpf;
45 	if (pSurf == NULL) {
46 		return 0;
47 	}
48 
49 	// Find out the pixelformat of the screen surface
50 	memset(&ddpf, 0, sizeof(ddpf));
51 	ddpf.dwSize = sizeof(ddpf);
52 
53 	if (SUCCEEDED(pSurf->GetPixelFormat(&ddpf))) {
54 		int nDepth = ddpf.dwRGBBitCount;
55 		if (nDepth == 16 && ddpf.dwGBitMask == 0x03E0) {
56 			nDepth = 15;
57 		}
58 
59 		return nDepth;
60 	}
61 
62 	return 0;
63 }
64 
65 // Clear a surface to a specified colour
66 
VidSClearSurface(IDirectDrawSurface7 * pSurf,unsigned int nColour,RECT * pRect)67 int VidSClearSurface(IDirectDrawSurface7* pSurf, unsigned int nColour, RECT* pRect)
68 {
69 	DDBLTFX BltFx;
70 
71 	if (pSurf == NULL) {
72 		return 1;
73 	}
74 
75 	memset(&BltFx, 0, sizeof(BltFx));
76 	BltFx.dwSize = sizeof(BltFx);
77 	BltFx.dwFillColor = nColour;
78 
79 	if (FAILED(pSurf->Blt(NULL, NULL, pRect, DDBLT_COLORFILL, &BltFx))) {
80 		return 1;
81 	} else {
82 		return 0;
83 	}
84 }
85 
86 // Clipper
87 
VidSClipperInit(IDirectDrawSurface7 * pSurf)88 int VidSClipperInit(IDirectDrawSurface7* pSurf)
89 {
90 	if (nVidFullscreen && bDrvOkay) {
91 		return 0;
92 	}
93 
94 	IDirectDrawClipper *pClipper = NULL;
95 
96 	if (SUCCEEDED(_DirectDrawCreateClipper(0, &pClipper, NULL))) {
97 		if (SUCCEEDED(pClipper->SetHWnd(0, hVidWnd))) {
98 			pSurf->SetClipper(pClipper);
99 			RELEASE(pClipper);
100 
101 			return 0;
102 		}
103 	}
104 
105 	return 1;
106 }
107 
108 // ---------------------------------------------------------------------------
109 // Gamma controls
110 
111 static IDirectDrawGammaControl* pGammaControl;
112 static DDGAMMARAMP* pFBAGamma = NULL;
113 static DDGAMMARAMP* pSysGamma = NULL;
114 
VidSRestoreGamma()115 void VidSRestoreGamma()
116 {
117 	if (pGammaControl) {
118 		if (pSysGamma) {
119 			pGammaControl->SetGammaRamp(0, pSysGamma);
120 		}
121 
122 		if (pSysGamma) {
123 			free(pSysGamma);
124 			pSysGamma = NULL;
125 		}
126 		if (pFBAGamma) {
127 			free(pFBAGamma);
128 			pFBAGamma = NULL;
129 		}
130 
131 		RELEASE(pGammaControl);
132 	}
133 }
134 
VidSUpdateGamma()135 int VidSUpdateGamma()
136 {
137 	if (pGammaControl) {
138 		if (bDoGamma) {
139 			for (int i = 0; i < 256; i++) {
140 				int nValue = (int)(65535.0 * pow((i / 255.0), nGamma));
141 				pFBAGamma->red[i] = nValue;
142 				pFBAGamma->green[i] = nValue;
143 				pFBAGamma->blue[i] = nValue;
144 			}
145 			pGammaControl->SetGammaRamp(0, pFBAGamma);
146 		} else {
147 			pGammaControl->SetGammaRamp(0, pSysGamma);
148 		}
149 	}
150 
151 	return 0;
152 }
153 
VidSSetupGamma(IDirectDrawSurface7 * pSurf)154 int VidSSetupGamma(IDirectDrawSurface7* pSurf)
155 {
156 	pGammaControl = NULL;
157 
158 	if (!bVidUseHardwareGamma || !nVidFullscreen) {
159 		return 0;
160 	}
161 
162 	if (FAILED(pSurf->QueryInterface(IID_IDirectDrawGammaControl, (void**)&pGammaControl))) {
163 		pGammaControl = NULL;
164 #ifdef PRINT_DEBUG_INFO
165 		dprintf(_T("  * Warning: Couldn't use hardware gamma controls.\n"));
166 #endif
167 
168 		return 1;
169 	}
170 
171 	pSysGamma = (DDGAMMARAMP*)malloc(sizeof(DDGAMMARAMP));
172 	if (pSysGamma == NULL) {
173 		VidSRestoreGamma();
174 		return 1;
175 	}
176 	pGammaControl->GetGammaRamp(0, pSysGamma);
177 
178 	pFBAGamma = (DDGAMMARAMP*)malloc(sizeof(DDGAMMARAMP));
179 	if (pFBAGamma == NULL) {
180 		VidSRestoreGamma();
181 		return 1;
182 	}
183 
184 	VidSUpdateGamma();
185 
186 	return 0;
187 }
188 
189 // ---------------------------------------------------------------------------
190 // Fullscreen mode support routines
191 
VidSScoreDisplayMode(VidSDisplayScoreInfo * pScoreInfo)192 int VidSScoreDisplayMode(VidSDisplayScoreInfo* pScoreInfo)
193 {
194 	// Continue if the resolution is too low
195 	if (pScoreInfo->nModeWidth < pScoreInfo->nRequestedWidth || pScoreInfo->nModeHeight < pScoreInfo->nRequestedHeight) {
196 		return 0;
197 	}
198 
199 	// Only test standard resolutions if below 512 x 384
200 	if ((pScoreInfo->nModeWidth == 320 && pScoreInfo->nModeHeight == 240) || (pScoreInfo->nModeWidth == 400 && pScoreInfo->nModeHeight == 300) || (pScoreInfo->nModeWidth >= 512 && pScoreInfo->nModeHeight >= 384)) {
201 
202 		if (pScoreInfo->nRequestedWidth == 0 || pScoreInfo->nRequestedHeight == 0) {
203 			RECT rect = {0, 0, 0, 0};
204 			int nGameWidth = nVidImageWidth, nGameHeight = nVidImageHeight;
205 			unsigned int nScore;
206 
207 			if (bDrvOkay) {
208 				if ((BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) && (nVidRotationAdjust & 1)) {
209 					BurnDrvGetVisibleSize(&nGameHeight, &nGameWidth);
210 				} else {
211 					BurnDrvGetVisibleSize(&nGameWidth, &nGameHeight);
212 				}
213 			}
214 
215 			nVidScrnWidth = rect.right = pScoreInfo->nModeWidth;
216 			nVidScrnHeight = rect.bottom = pScoreInfo->nModeHeight;
217 			VidImageSize(&rect, nGameWidth, nGameHeight);
218 
219 			// Continue if the resolution is too low
220 			if (!bDrvOkay) {
221 				if ((unsigned int)(rect.bottom - rect.top) < pScoreInfo->nRequestedHeight) {
222 					return 0;
223 				}
224 			} else {
225 				if (BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) {
226 					if ((unsigned int)(rect.right - rect.left) < pScoreInfo->nRequestedWidth) {
227 						return 0;
228 					}
229 				} else {
230 					if ((unsigned int)(rect.bottom - rect.top) < pScoreInfo->nRequestedHeight) {
231 						return 0;
232 					}
233 				}
234 			}
235 
236 			// Score resolutions based on how many pixels are unused and the pixel aspect
237 			nScore = 65536 * (pScoreInfo->nModeWidth - (unsigned)(rect.right - rect.left)) + (pScoreInfo->nModeHeight - (unsigned)(rect.bottom - rect.top));
238 			nScore = (int)((double)nScore * ((double)pScoreInfo->nModeWidth * nVidScrnAspectY / pScoreInfo->nModeHeight / nVidScrnAspectX));
239 			if (nScore < pScoreInfo->nBestScore) {
240 				pScoreInfo->nBestScore = nScore;
241 				pScoreInfo->nBestWidth = pScoreInfo->nModeWidth;
242 				pScoreInfo->nBestHeight = pScoreInfo->nModeHeight;
243 			}
244 		} else {
245 			// Select the lowest resolution that will fit the image
246 			if (pScoreInfo->nModeWidth < pScoreInfo->nBestWidth && pScoreInfo->nModeHeight < pScoreInfo->nBestHeight) {
247 				pScoreInfo->nBestWidth = pScoreInfo->nModeWidth;
248 				pScoreInfo->nBestHeight = pScoreInfo->nModeHeight;
249 			}
250 		}
251 	}
252 
253 	return 0;
254 }
255 
VidSInitScoreInfo(VidSDisplayScoreInfo * pScoreInfo)256 int VidSInitScoreInfo(VidSDisplayScoreInfo* pScoreInfo)
257 {
258 	int nGameWidth = nVidImageWidth, nGameHeight = nVidImageHeight;
259 	int nGameAspectX = 4, nGameAspectY = 3;
260 
261 	pScoreInfo->nBestWidth = -1U;
262 	pScoreInfo->nBestHeight = -1U;
263 	pScoreInfo->nBestScore = -1U;
264 
265 	if (pScoreInfo->nRequestedZoom == 0) {
266 		return 0;
267 	}
268 
269 	pScoreInfo->nRequestedWidth = 0;
270 	pScoreInfo->nRequestedHeight = 0;
271 
272 	if (bDrvOkay) {
273 		if ((BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) && (nVidRotationAdjust & 1)) {
274 			BurnDrvGetVisibleSize(&nGameHeight, &nGameWidth);
275 			BurnDrvGetAspect(&nGameAspectY, &nGameAspectX);
276 		} else {
277 			BurnDrvGetVisibleSize(&nGameWidth, &nGameHeight);
278 			BurnDrvGetAspect(&nGameAspectX, &nGameAspectY);
279 		}
280 	}
281 
282 	if (!bVidCorrectAspect || bVidFullStretch || (nVidSelect == 1 && (nVidBlitterOpt[1] & 0x07000000) == 0x07000000) || (nVidSelect == 2 && nVidBlitterOpt[2] & 0x00000100)) {
283 		pScoreInfo->nRequestedWidth = nGameWidth * pScoreInfo->nRequestedZoom;
284 		pScoreInfo->nRequestedHeight = nGameHeight * pScoreInfo->nRequestedZoom;
285 	} else {
286 		if (bDrvOkay) {
287 			if (BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) {
288 				pScoreInfo->nRequestedWidth = nGameWidth * pScoreInfo->nRequestedZoom;
289 			} else {
290 				pScoreInfo->nRequestedHeight = nGameHeight * pScoreInfo->nRequestedZoom;
291 			}
292 		}
293 	}
294 
295 	return 0;
296 }
297 
myEnumModesCallback(LPDDSURFACEDESC2 pSurfaceDesc,void * lpContext)298 static HRESULT WINAPI myEnumModesCallback(LPDDSURFACEDESC2 pSurfaceDesc, void* lpContext)
299 {
300 	((VidSDisplayScoreInfo*)lpContext)->nModeWidth = pSurfaceDesc->dwWidth;
301 	((VidSDisplayScoreInfo*)lpContext)->nModeHeight = pSurfaceDesc->dwHeight;
302 
303 	VidSScoreDisplayMode((VidSDisplayScoreInfo*)lpContext);
304 
305 	return DDENUMRET_OK;
306 }
307 
VidSRestoreScreenMode()308 void VidSRestoreScreenMode()
309 {
310 	if (pDD == NULL) {
311 		return;
312 	}
313 
314 	// Undo the changes we made to the display
315 	pDD->SetCooperativeLevel(NULL, DDSCL_NORMAL);
316 
317 	nVidScrnWidth = 0;
318 	nVidScrnHeight = 0;
319 
320 	return;
321 }
322 
323 // Enter fullscreen mode, select optimal full-screen resolution
VidSEnterFullscreenMode(int nZoom,int nDepth)324 int VidSEnterFullscreenMode(int nZoom, int nDepth)
325 {
326 	int nWidth, nHeight;
327 
328 	if (pDD == NULL) {
329 		return 1;
330 	}
331 
332 	if (nDepth == 0) {
333 		nDepth = nVidDepth;
334 	}
335 	if (nDepth == 15) {
336 		nDepth = 16;
337 	}
338 
339 	if (FAILED(pDD->SetCooperativeLevel(hVidWnd, DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FPUPRESERVE | DDSCL_FULLSCREEN))) {
340 		return 1;
341 	}
342 
343 	if (bVidArcaderes) {
344 		if (!VidSGetArcaderes(&nWidth, &nHeight)) {
345 			return 1;
346 		}
347 	} else {
348 		if (nZoom) {
349 			DDSURFACEDESC2 ddsd;
350 			VidSDisplayScoreInfo ScoreInfo;
351 
352 			memset(&ScoreInfo, 0, sizeof(VidSDisplayScoreInfo));
353 			ScoreInfo.nRequestedZoom = nZoom;
354 			VidSInitScoreInfo(&ScoreInfo);
355 
356 			memset(&ddsd, 0, sizeof(ddsd));
357 			ddsd.dwSize = sizeof(ddsd);
358 			ddsd.dwFlags = DDSD_PIXELFORMAT;
359 
360 			ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
361 			ddsd.ddpfPixelFormat.dwRGBBitCount = nDepth;
362 
363 			pDD->EnumDisplayModes(0, &ddsd, (void*)&ScoreInfo, myEnumModesCallback);
364 
365 			if (ScoreInfo.nBestWidth == -1U) {
366 
367 				VidSRestoreScreenMode();
368 
369 				FBAPopupAddText(PUF_TEXT_DEFAULT, MAKEINTRESOURCE(IDS_ERR_UI_FULL_NOMODE));
370 				FBAPopupDisplay(PUF_TYPE_ERROR);
371 
372 				return 1;
373 			}
374 
375 			nWidth = ScoreInfo.nBestWidth;
376 			nHeight = ScoreInfo.nBestHeight;
377 		} else {
378 			nWidth = nVidWidth;
379 			nHeight = nVidHeight;
380 		}
381 	}
382 
383 	if (!bDrvOkay && (nWidth < 640 || nHeight < 480)) {
384 		return 1;
385 	}
386 
387 	if (FAILED(pDD->SetDisplayMode(nWidth, nHeight, nDepth, nVidRefresh, 0))) {
388 		VidSRestoreScreenMode();
389 
390 		FBAPopupAddText(PUF_TEXT_DEFAULT, MAKEINTRESOURCE(IDS_ERR_UI_FULL_PROBLEM));
391 		if (bVidArcaderes && (nWidth != 320 && nHeight != 240)) {
392 			FBAPopupAddText(PUF_TEXT_DEFAULT, MAKEINTRESOURCE(IDS_ERR_UI_FULL_CUSTRES));
393 		}
394 		FBAPopupDisplay(PUF_TYPE_ERROR);
395 
396 		nVidScrnWidth = 0;
397 		nVidScrnHeight = 0;
398 
399 		return 1;
400 	}
401 
402 	nVidScrnWidth = nWidth;
403 	nVidScrnHeight = nHeight;
404 
405 	return 0;
406 }
407 
408 // ---------------------------------------------------------------------------
409 
410 // Compute the resolution needed to run a game without scaling and filling the screen
411 // (taking into account the aspect ratios of the game and monitor)
VidSGetArcaderes(int * pWidth,int * pHeight)412 bool VidSGetArcaderes(int* pWidth, int* pHeight)
413 {
414 	int nGameWidth, nGameHeight;
415 	int nGameAspectX, nGameAspectY;
416 
417 	if (bDrvOkay) {
418 		if ((BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) && (nVidRotationAdjust & 1)) {
419 			BurnDrvGetVisibleSize(&nGameHeight, &nGameWidth);
420 			BurnDrvGetAspect(&nGameAspectY, &nGameAspectX);
421 		} else {
422 			BurnDrvGetVisibleSize(&nGameWidth, &nGameHeight);
423 			BurnDrvGetAspect(&nGameAspectX, &nGameAspectY);
424 		}
425 
426 		double dMonAspect = (double)nVidScrnAspectX / nVidScrnAspectY;
427 		double dGameAspect = (double)nGameAspectX / nGameAspectY;
428 
429 		if (dMonAspect > dGameAspect) {
430 			*pWidth = nGameHeight * nGameAspectY * nGameWidth * nVidScrnAspectX / (nGameHeight * nGameAspectX * nVidScrnAspectY);
431 			*pHeight = nGameHeight;
432 		} else {
433 			*pWidth = nGameWidth;
434 			*pHeight = nGameWidth * nGameAspectX * nGameHeight * nVidScrnAspectY / (nGameWidth * nGameAspectY * nVidScrnAspectX);
435 		}
436 
437 		// Horizontal resolution must be a multiple of 8
438 		if (*pWidth - nGameWidth < 8) {
439 			*pWidth = (*pWidth + 7) & ~7;
440 		} else {
441 			*pWidth = (*pWidth + 4) & ~7;
442 		}
443 	} else {
444 		return false;
445 	}
446 
447 	return true;
448 }
449 
450 // This function takes a rectangle and scales it to either:
451 // - The largest possible multiple of both X and Y;
452 // - The largest possible multiple of Y, modifying X to ensure the correct aspect ratio;
453 // - The window size
VidSScaleImage(RECT * pRect,int nGameWidth,int nGameHeight,bool bVertScanlines)454 int VidSScaleImage(RECT* pRect, int nGameWidth, int nGameHeight, bool bVertScanlines)
455 {
456 	float xm, ym;											// The multiple of nScrnWidth and nScrnHeight we can fit in
457 	int nScrnWidth, nScrnHeight;
458 	int nScrnAspectX, nScrnAspectY;
459 
460 	int nGameAspectX = 4, nGameAspectY = 3;
461 	int nWidth = pRect->right - pRect->left;
462 	int nHeight = pRect->bottom - pRect->top;
463 
464 	if (bVidFullStretch) {								// Arbitrary stretch
465 		return 0;
466 	}
467 
468 	nScrnAspectX = nVidScrnAspectX;
469 	nScrnAspectY = nVidScrnAspectY;
470 
471 	if (bDrvOkay) {
472 		if ((BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) && (nVidRotationAdjust & 1)) {
473 			BurnDrvGetAspect(&nGameAspectY, &nGameAspectX);
474 		} else {
475 			BurnDrvGetAspect(&nGameAspectX, &nGameAspectY);
476 		}
477 
478 
479 		if ((BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) && nVidFullscreen && !(nVidRotationAdjust & 1)) {
480 			// Using vertically orientated monitor
481 			nScrnAspectX = nVidVerScrnAspectX;
482 			nScrnAspectY = nVidVerScrnAspectY;
483 		}
484 	}
485 
486 	//xm = (nWidth / nGameWidth) + (nWidth / nGameWidth) * 0.166666667;
487 	//ym = (nHeight / nGameHeight) + (nWidth / nGameWidth) * 0.166666667;
488 	xm = (float)nWidth / nGameWidth;
489 	ym = (float)nHeight / nGameHeight;
490 
491 	if (nVidFullscreen) {
492 		nScrnWidth = nVidScrnWidth;
493 		nScrnHeight = nVidScrnHeight;
494 	} else {
495 		nScrnWidth = SystemWorkArea.right - SystemWorkArea.left;
496 		nScrnHeight = SystemWorkArea.bottom - SystemWorkArea.top;
497 	}
498 
499 	if (bVidCorrectAspect && bVidScanlines && ((ym >= 2 && xm) || (ym && xm >= 2 && bVertScanlines))) {	// Correct aspect ratio with scanlines
500 		float nWidthScratch, nHeightScratch;
501 		if (nGameWidth < nGameHeight && bVertScanlines) {
502 			float xmScratch = xm;
503 			do {
504 				nWidthScratch = nGameWidth * xmScratch;
505 				nHeightScratch = (INT64)nWidthScratch * nScrnAspectX * nGameAspectY * nScrnHeight / (nScrnWidth * nScrnAspectY * nGameAspectX);
506 				xmScratch--;
507 			} while (nHeightScratch > nHeight && xmScratch >= 2);
508 			if (nHeightScratch > nHeight) {				// The image is too high
509 				if (nGameWidth < nGameHeight) {			// Vertical games
510 					nWidth = (INT64)nHeight * nScrnAspectX * nGameAspectX * nScrnHeight / (nScrnWidth * nScrnAspectY * nGameAspectY);
511 				} else {								// Horizontal games
512 					nWidth = (INT64)nHeight * nScrnAspectY * nGameAspectX * nScrnWidth / (nScrnHeight * nScrnAspectX * nGameAspectY);
513 				}
514 			} else {
515 				nWidth = nWidthScratch;
516 				nHeight = nHeightScratch;
517 			}
518 		} else {
519 			float ymScratch = ym;
520 			do {
521 				nHeightScratch = nGameHeight * ymScratch;
522 				nWidthScratch = (INT64)nHeightScratch * nScrnAspectY * nGameAspectX * nScrnWidth / (nScrnHeight * nScrnAspectX * nGameAspectY);
523 				ymScratch--;
524 			} while (nWidthScratch > nWidth && ymScratch >= 2);
525 			if (nWidthScratch > nWidth) {				// The image is too wide
526 				if (nGameWidth < nGameHeight) {			// Vertical games
527 					nHeight = (INT64)nWidth * nScrnAspectY * nGameAspectY * nScrnWidth / (nScrnHeight * nScrnAspectX * nGameAspectX);
528 				} else {								// Horizontal games
529 					nHeight = (INT64)nWidth * nScrnAspectX * nGameAspectY * nScrnHeight / (nScrnWidth * nScrnAspectY * nGameAspectX);
530 				}
531 			} else {
532 				nWidth = nWidthScratch;
533 				nHeight = nHeightScratch;
534 			}
535 		}
536 	} else {
537 		if (bVidCorrectAspect) {					// Correct aspect ratio
538 			float nWidthScratch;
539 			nWidthScratch = (INT64)nHeight * nScrnAspectY * nGameAspectX * nScrnWidth / (nScrnHeight * nScrnAspectX * nGameAspectY);
540 			if (nWidthScratch > nWidth) {			// The image is too wide
541 				if (nGameWidth < nGameHeight) {		// Vertical games
542 					nHeight = (INT64)nWidth * nScrnAspectY * nGameAspectY * nScrnWidth / (nScrnHeight * nScrnAspectX * nGameAspectX);
543 				} else {							// Horizontal games
544 					nHeight = (INT64)nWidth * nScrnAspectX * nGameAspectY * nScrnHeight / (nScrnWidth * nScrnAspectY * nGameAspectX);
545 				}
546 			} else {
547 				nWidth = nWidthScratch;
548 			}
549 		} else {
550 			if (xm && ym) {							// Don't correct aspect ratio
551 				if (xm > ym) {
552 					xm = ym;
553 				} else {
554 					ym = xm;
555 				}
556 				nWidth = nGameWidth * xm;
557 				nHeight = nGameHeight * ym;
558 			} else {
559 				if (xm) {
560 					nWidth = nGameWidth * xm * nHeight / nGameHeight;
561 				} else {
562 					if (ym) {
563 						nHeight = nGameHeight * ym * nWidth / nGameWidth;
564 					}
565 				}
566 			}
567 		}
568 	}
569 
570 	pRect->left = (pRect->right + pRect->left) / 2;
571 	pRect->left -= nWidth / 2;
572 	pRect->right = pRect->left + nWidth;
573 
574 	pRect->top = (pRect->top + pRect->bottom) / 2;
575 	pRect->top -= nHeight / 2;
576 	pRect->bottom = pRect->top + nHeight;
577 
578 	return 0;
579 }
580 
581 // ---------------------------------------------------------------------------
582 // Text display routines
583 
584 static struct { TCHAR pMsgText[32]; COLORREF nColour; int nPriority; unsigned int nTimer; } VidSShortMsg = { _T(""), 0, 0, 0,};
585 static HFONT ShortMsgFont = NULL;
586 
587 static unsigned char nStatusSymbols[4] = {0x3B, 0xC2, 0x3D, 0x34}; //"pause", "record", "kaillera" and "play" in font Webdings, respectivelly
588 static HFONT StatusFont = NULL;
589 static HFONT StatusFontTiny = NULL;
590 
591 #define CHAT_SIZE 11
592 
593 static struct { TCHAR* pIDText; COLORREF nIDColour; TCHAR* pMainText; COLORREF nMainColour; } VidSChatMessage[CHAT_SIZE];
594 static bool bChatInitialised = false;
595 
596 static HFONT ChatIDFont = NULL;
597 static HFONT ChatMainFont = NULL;
598 static unsigned int nChatTimer;
599 static int nChatFontSize;
600 static int nChatShadowOffset;
601 static int nChatOverFlow;
602 static bool	bDrawChat;
603 
604 static HFONT EditTextFont = NULL;
605 static HFONT EditCursorFont = NULL;
606 static DWORD nPrevStart, nPrevEnd;
607 static int nCursorTime;
608 static int nCursorState = 0;
609 static int nEditSize;
610 static int nEditShadowOffset;
611 
612 static struct { TCHAR pMsgText[64]; COLORREF nColour; int nPriority; unsigned int nTimer; } VidSTinyMsg = {_T(""), 0, 0, 0};
613 static HFONT TinyMsgFont = NULL;
614 
615 static struct { TCHAR pMsgText[64]; COLORREF nColour; int nLineNo; unsigned int nTimer; } VidSJoystickMsg = {_T(""), 0, 0, 0};
616 static HFONT JoystickMsgFont = NULL;
617 
618 static IDirectDrawSurface7* pShortMsgSurf = NULL;
619 static IDirectDrawSurface7* pStatusSurf = NULL;
620 static IDirectDrawSurface7* pChatSurf = NULL;
621 static IDirectDrawSurface7* pEditSurf = NULL;
622 static IDirectDrawSurface7* pTinyMsgSurf = NULL;
623 static IDirectDrawSurface7* pJoystickMsgSurf = NULL;
624 
625 static unsigned int nKeyColour = 0x000001;
626 
627 static int nStatus = 0;
628 static int nPrevStatus = -1;
629 
630 int nVidSDisplayStatus = 1;								// 1 = display pause/record/replay/kaillera icons in the upper right corner of the display
631 
632 int nMaxChatFontSize = 36;								// Maximum size of the font used for the Kaillera chat function (1280x960 or higher)
633 int nMinChatFontSize = 12;								// Minimum size of the font used for the Kaillera chat function (arcade resolution)
634 
635 static int nZoom;										// & 1: zoom X, & 2: zoom Y
636 
637 static int nShortMsgFlags;
638 static int nStatusFlags;
639 
640 bool bEditActive = false;
641 bool bEditTextChanged = false;
642 TCHAR EditText[MAX_CHAT_SIZE + 1] = _T("");
643 
644 TCHAR OSDMsg[MAX_PATH] = _T("");
645 unsigned int nOSDTimer = 0;
646 
MyTextOut(HDC hdc,int nXStart,int nYStart,LPCTSTR lpString,int cbString,int nShadowOffset,int nColour)647 static BOOL MyTextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cbString, int nShadowOffset, int nColour)
648 {
649 	// Print a black shadow
650 	SetTextColor(hdc, 0);
651 	if (nShadowOffset >= 2) {
652 		TextOut(hdc, nXStart + nShadowOffset, nYStart + nShadowOffset, lpString, cbString);
653 	}
654 	// Print a black outline
655 	TextOut(hdc, nXStart - 1, nYStart - 1, lpString, cbString);
656 	TextOut(hdc, nXStart + 0, nYStart - 1, lpString, cbString);
657 	TextOut(hdc, nXStart + 1, nYStart - 1, lpString, cbString);
658 	TextOut(hdc, nXStart + 1, nYStart + 0, lpString, cbString);
659 	TextOut(hdc, nXStart + 1, nYStart + 1, lpString, cbString);
660 	TextOut(hdc, nXStart + 0, nYStart + 1, lpString, cbString);
661 	TextOut(hdc, nXStart - 1, nYStart + 1, lpString, cbString);
662 	TextOut(hdc, nXStart - 1, nYStart + 0, lpString, cbString);
663 	// Print the text on top
664 	SetTextColor(hdc, nColour);
665 
666 	return TextOut(hdc, nXStart, nYStart, lpString, cbString);
667 }
668 
MyExtTextOut(HDC hdc,int X,int Y,UINT fuOptions,CONST RECT * lprc,LPCTSTR lpString,UINT cbCount,CONST INT * lpDx,int nShadowOffset,int nColour)669 static BOOL MyExtTextOut(HDC hdc, int X, int Y, UINT fuOptions, CONST RECT* lprc, LPCTSTR lpString, UINT cbCount, CONST INT* lpDx, int nShadowOffset, int nColour)
670 {
671 	// Print a black shadow
672 	SetTextColor(hdc, 0);
673 	if (nShadowOffset >= 2) {
674 		ExtTextOut(hdc, X + nShadowOffset, Y + nShadowOffset, fuOptions, lprc, lpString, cbCount, lpDx);
675 	}
676 	// Print a black outline
677 	ExtTextOut(hdc, X - 1, Y - 1, fuOptions, lprc, lpString, cbCount, lpDx);
678 	ExtTextOut(hdc, X + 0, Y - 1, fuOptions, lprc, lpString, cbCount, lpDx);
679 	ExtTextOut(hdc, X + 1, Y - 1, fuOptions, lprc, lpString, cbCount, lpDx);
680 	ExtTextOut(hdc, X + 1, Y + 0, fuOptions, lprc, lpString, cbCount, lpDx);
681 	ExtTextOut(hdc, X + 1, Y + 1, fuOptions, lprc, lpString, cbCount, lpDx);
682 	ExtTextOut(hdc, X + 0, Y + 1, fuOptions, lprc, lpString, cbCount, lpDx);
683 	ExtTextOut(hdc, X - 1, Y + 1, fuOptions, lprc, lpString, cbCount, lpDx);
684 	ExtTextOut(hdc, X - 1, Y + 0, fuOptions, lprc, lpString, cbCount, lpDx);
685 	// Print the text on top
686 	SetTextColor(hdc, nColour);
687 
688 	return ExtTextOut(hdc, X, Y, fuOptions, lprc, lpString, cbCount, lpDx);
689 }
690 
VidSExitTinyMsg()691 static void VidSExitTinyMsg()
692 {
693 	VidSTinyMsg.nTimer = 0;
694 
695 	if (TinyMsgFont) {
696 		DeleteObject(TinyMsgFont);
697 		TinyMsgFont = NULL;
698 	}
699 
700 	RELEASE(pTinyMsgSurf);
701 }
702 
VidSExitJoystickMsg()703 static void VidSExitJoystickMsg()
704 {
705 	VidSJoystickMsg.nTimer = 0;
706 
707 	if (JoystickMsgFont) {
708 		DeleteObject(JoystickMsgFont);
709 		JoystickMsgFont = NULL;
710 	}
711 
712 	RELEASE(pJoystickMsgSurf);
713 }
714 
VidSExitShortMsg()715 static void VidSExitShortMsg()
716 {
717 	VidSShortMsg.nTimer = 0;
718 
719 	if (ShortMsgFont) {
720 		DeleteObject(ShortMsgFont);
721 		ShortMsgFont = NULL;
722 	}
723 
724 	RELEASE(pShortMsgSurf);
725 }
726 
VidSExitStatus()727 static void VidSExitStatus()
728 {
729 	if (StatusFont) {
730 		DeleteObject(StatusFont);
731 		StatusFont = NULL;
732 	}
733 
734 	if (StatusFontTiny) {
735 		DeleteObject(StatusFontTiny);
736 		StatusFontTiny = NULL;
737 	}
738 
739 	RELEASE(pStatusSurf);
740 }
741 
VidSExitChat()742 void VidSExitChat()
743 {
744 	nChatTimer = 0;
745 
746 	if (ChatIDFont) {
747 		DeleteObject(ChatIDFont);
748 		ChatIDFont = NULL;
749 	}
750 	if (ChatMainFont) {
751 		DeleteObject(ChatMainFont);
752 		ChatMainFont = NULL;
753 	}
754 
755 	for (int i = 0; i < CHAT_SIZE; i++) {
756 		if (VidSChatMessage[i].pIDText) {
757 			free(VidSChatMessage[i].pIDText);
758 			VidSChatMessage[i].pIDText = NULL;
759 		}
760 		if (VidSChatMessage[i].pMainText) {
761 			free(VidSChatMessage[i].pMainText);
762 			VidSChatMessage[i].pMainText = NULL;
763 		}
764 	}
765 
766 	RELEASE(pChatSurf);
767 }
768 
VidSExitEdit()769 static void VidSExitEdit()
770 {
771 	bEditActive = false;
772 
773 	if (EditTextFont) {
774 		DeleteObject(EditTextFont);
775 		EditTextFont = NULL;
776 	}
777 	if (EditCursorFont) {
778 		DeleteObject(EditCursorFont);
779 		EditCursorFont = NULL;
780 	}
781 
782 	RELEASE(pEditSurf);
783 }
784 
VidSExitOSD()785 void VidSExitOSD()
786 {
787 	VidSExitTinyMsg();
788 	VidSExitJoystickMsg();
789 	if (kNetGame) {
790 		VidSExitEdit();
791 	}
792 	VidSExitChat();
793 	VidSExitShortMsg();
794 	VidSExitStatus();
795 }
796 
VidSInitTinyMsg(int)797 static int VidSInitTinyMsg(int /*nFlags*/)
798 {
799 	DDSURFACEDESC2 ddsd;
800 
801 	VidSExitTinyMsg();
802 
803 	TinyMsgFont = CreateFont(12, 0, 0, 0, FW_SEMIBOLD, 0, 0, 0, 0, 0, 0, ANTIALIASED_QUALITY, FF_SWISS, _T("MS Sans Serif"));
804 	VidSTinyMsg.nTimer = 0;
805 
806 	// create surface to display the text
807 	memset(&ddsd, 0, sizeof(ddsd));
808 	ddsd.dwSize = sizeof(ddsd);
809 	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_CKSRCBLT;
810 
811 	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
812 
813 	ddsd.dwWidth = 300;
814 	ddsd.dwHeight = 20;
815 
816 	ddsd.ddckCKSrcBlt.dwColorSpaceLowValue = nKeyColour;
817 	ddsd.ddckCKSrcBlt.dwColorSpaceHighValue = nKeyColour;
818 
819 	if (FAILED(pDD->CreateSurface(&ddsd, &pTinyMsgSurf, NULL))) {
820 #ifdef PRINT_DEBUG_INFO
821 		printf("  * Error: Couldn't create OSD texture.\n");
822 #endif
823 		return 1;
824 	}
825 
826 	VidSClearSurface(pTinyMsgSurf, nKeyColour, NULL);
827 
828 	return 0;
829 }
830 
VidSInitJoystickMsg(int)831 static int VidSInitJoystickMsg(int /*nFlags*/)
832 {
833 	DDSURFACEDESC2 ddsd;
834 
835 	VidSExitJoystickMsg();
836 
837 	//JoystickMsgFont = CreateFont(12, 0, 0, 0, FW_DEMIBOLD, 0, 0, 0, 0, 0, 0, ANTIALIASED_QUALITY, FF_SWISS, _T("Courier New"));
838 	JoystickMsgFont = CreateFont(8, 0, 0, 0, FW_THIN, 0, 0, 0, OUT_OUTLINE_PRECIS, 0, 0, NONANTIALIASED_QUALITY, FF_SWISS, _T("Courier New"));
839 	VidSJoystickMsg.nTimer = 0;
840 
841 	// create surface to display the text
842 	memset(&ddsd, 0, sizeof(ddsd));
843 	ddsd.dwSize = sizeof(ddsd);
844 	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_CKSRCBLT;
845 
846 	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
847 
848 	ddsd.dwWidth = 300;
849 	ddsd.dwHeight = 60;
850 
851 	ddsd.ddckCKSrcBlt.dwColorSpaceLowValue = nKeyColour;
852 	ddsd.ddckCKSrcBlt.dwColorSpaceHighValue = nKeyColour;
853 
854 	if (FAILED(pDD->CreateSurface(&ddsd, &pJoystickMsgSurf, NULL))) {
855 #ifdef PRINT_DEBUG_INFO
856 		printf("  * Error: Couldn't create OSD texture.\n");
857 #endif
858 		return 1;
859 	}
860 
861 	VidSClearSurface(pJoystickMsgSurf, nKeyColour, NULL);
862 
863 	return 0;
864 }
865 
VidSInitShortMsg(int nFlags)866 static int VidSInitShortMsg(int nFlags)
867 {
868 	DDSURFACEDESC2 ddsd;
869 
870 	VidSExitShortMsg();
871 
872 	nShortMsgFlags = nFlags;
873 
874 	ShortMsgFont = CreateFont(24, 0, 0, 0, FW_DEMIBOLD, 0, 0, 0, 0, 0, 0, ANTIALIASED_QUALITY, FF_SWISS, _T("Lucida"));
875 	VidSShortMsg.nTimer = 0;
876 
877 	// create surface to display the text
878 	memset(&ddsd, 0, sizeof(ddsd));
879 	ddsd.dwSize = sizeof(ddsd);
880 	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_CKSRCBLT;
881 
882 	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
883 
884 	ddsd.dwWidth = 256;
885 	ddsd.dwHeight = 32;
886 
887 	ddsd.ddckCKSrcBlt.dwColorSpaceLowValue = nKeyColour;
888 	ddsd.ddckCKSrcBlt.dwColorSpaceHighValue = nKeyColour;
889 
890 	if (FAILED(pDD->CreateSurface(&ddsd, &pShortMsgSurf, NULL))) {
891 #ifdef PRINT_DEBUG_INFO
892 		dprintf(_T("  * Error: Couldn't create OSD texture.\n"));
893 #endif
894 		return 1;
895 	}
896 
897 	VidSClearSurface(pShortMsgSurf, nKeyColour, NULL);
898 
899 	return 0;
900 }
901 
VidSInitStatus(int nFlags)902 static int VidSInitStatus(int nFlags)
903 {
904 	DDSURFACEDESC2 ddsd;
905 
906 	VidSExitStatus();
907 
908 	nStatusFlags = nFlags;
909 
910 	StatusFont = CreateFont(48, 0, 0, 0, FW_DEMIBOLD, 0, 0, 0, DEFAULT_CHARSET, 0, 0, ANTIALIASED_QUALITY, FF_SWISS, _T("Webdings"));
911 	StatusFontTiny = CreateFont(20, 0, 0, 0, FW_DEMIBOLD, 0, 0, 0, DEFAULT_CHARSET, 0, 0, ANTIALIASED_QUALITY, FF_SWISS, _T("Webdings"));
912 	nPrevStatus = -1;
913 
914 	// create surface to display the text
915 	memset(&ddsd, 0, sizeof(ddsd));
916 	ddsd.dwSize = sizeof(ddsd);
917 	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_CKSRCBLT;
918 
919 	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
920 
921 	ddsd.dwWidth = 192;
922 	ddsd.dwHeight = 50;
923 
924 	ddsd.ddckCKSrcBlt.dwColorSpaceLowValue = nKeyColour;
925 	ddsd.ddckCKSrcBlt.dwColorSpaceHighValue = nKeyColour;
926 
927 	if (FAILED(pDD->CreateSurface(&ddsd, &pStatusSurf, NULL))) {
928 #ifdef PRINT_DEBUG_INFO
929 		dprintf(_T("  * Error: Couldn't create OSD texture.\n"));
930 #endif
931 		return 1;
932 	}
933 
934 	VidSClearSurface(pStatusSurf, nKeyColour, NULL);
935 
936 	return 0;
937 }
938 
VidSInitChat(int)939 static int VidSInitChat(int /*nFlags*/)
940 {
941 	DDSURFACEDESC2 ddsd;
942 
943 	if (bChatInitialised) {
944 		VidSExitChat();
945 	} else {
946 //		for (int i = 0; i < CHAT_SIZE + (CHAT_SIZE >> 1); i++) {
947 		for (int i = 0; i < CHAT_SIZE; i++) {
948 			VidSChatMessage[i].pIDText = NULL;
949 			VidSChatMessage[i].pMainText = NULL;
950 		}
951 		bChatInitialised = true;
952 	}
953 
954 	//nChatFontSize = nMaxChatFontSize - ((nMaxChatFontSize - nMinChatFontSize) * nFlags) / 4;
955 	nChatFontSize = 20; // used for cheat search only at the minute so only used in a window
956 
957 	ChatIDFont = CreateFont(nChatFontSize, 0, 0, 0, FW_BOLD, 0, 0, 0, 0, 0, 0, ANTIALIASED_QUALITY, FF_SWISS, _T("Lucida"));
958 	if (nChatFontSize > 20) {
959 		ChatMainFont = CreateFont(nChatFontSize, 0, 0, 0, FW_NORMAL, 0, 0, 0, 0, 0, 0, ANTIALIASED_QUALITY, FF_SWISS, _T("Lucida"));
960 	} else {
961 		ChatMainFont = CreateFont(nChatFontSize, 0, 0, 0, FW_BOLD, 0, 0, 0, 0, 0, 0, ANTIALIASED_QUALITY, FF_SWISS, _T("Lucida"));
962 	}
963 
964 	nChatShadowOffset = (nChatFontSize / 16) + 1;
965 	nChatOverFlow = 0;
966 	bDrawChat = false;
967 
968 	// create surface to display the text
969 	memset(&ddsd, 0, sizeof(ddsd));
970 	ddsd.dwSize = sizeof(ddsd);
971 	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_CKSRCBLT;
972 
973 	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
974 
975 	if (nVidFullscreen) {
976 		RECT rect;
977 		GetClientScreenRect(hScrnWnd, &rect);
978 		ddsd.dwWidth = rect.right - rect.left;
979 	} else {
980 		extern RECT SystemWorkArea;
981 		ddsd.dwWidth = SystemWorkArea.right - SystemWorkArea.left;
982 	}
983 	ddsd.dwHeight = nChatFontSize * (CHAT_SIZE + (CHAT_SIZE >> 1));
984 
985 	ddsd.ddckCKSrcBlt.dwColorSpaceLowValue = nKeyColour;
986 	ddsd.ddckCKSrcBlt.dwColorSpaceHighValue = nKeyColour;
987 
988 	if (FAILED(pDD->CreateSurface(&ddsd, &pChatSurf, NULL))) {
989 #ifdef PRINT_DEBUG_INFO
990 		dprintf(_T("  * Error: Couldn't create Chat texture.\n"));
991 #endif
992 		return 1;
993 	}
994 
995 	VidSClearSurface(pChatSurf, nKeyColour, NULL);
996 
997 	bDrawChat = true;
998 
999 	return 0;
1000 }
1001 
VidSInitEdit(int nFlags)1002 static int VidSInitEdit(int nFlags)
1003 {
1004 	DDSURFACEDESC2 ddsd;
1005 
1006 	VidSExitEdit();
1007 
1008 	nEditSize = nMaxChatFontSize + 8 - ((nMaxChatFontSize - nMinChatFontSize) * nFlags) / 4;
1009 
1010 	EditTextFont = CreateFont(nEditSize - 8, 0, 0, 0, FW_BOLD, 0, 0, 0, 0, 0, 0, ANTIALIASED_QUALITY, FF_SWISS, _T("Lucida"));
1011 	EditCursorFont = CreateFont(nEditSize - 8, 0, 0, 0, FW_BOLD, 0, TRUE, 0, 0, 0, 0, ANTIALIASED_QUALITY, FF_SWISS, _T("Lucida"));
1012 
1013 	nEditShadowOffset = ((nEditSize - 8) / 16) + 1;
1014 
1015 	// create surface to display the text
1016 	memset(&ddsd, 0, sizeof(ddsd));
1017 	ddsd.dwSize = sizeof(ddsd);
1018 	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_CKSRCBLT;
1019 
1020 	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
1021 
1022 	if (nVidFullscreen) {
1023 		RECT rect;
1024 		GetClientScreenRect(hScrnWnd, &rect);
1025 		ddsd.dwWidth = rect.right - rect.left;
1026 	} else {
1027 		extern RECT SystemWorkArea;
1028 		ddsd.dwWidth = SystemWorkArea.right - SystemWorkArea.left;
1029 	}
1030 	ddsd.dwHeight = nEditSize;
1031 
1032 	ddsd.ddckCKSrcBlt.dwColorSpaceLowValue = nKeyColour;
1033 	ddsd.ddckCKSrcBlt.dwColorSpaceHighValue = nKeyColour;
1034 
1035 	if (FAILED(pDD->CreateSurface(&ddsd, &pEditSurf, NULL))) {
1036 #ifdef PRINT_DEBUG_INFO
1037 		dprintf(_T("  * Error: Couldn't create OSD texture.\n"));
1038 #endif
1039 		return 1;
1040 	}
1041 
1042 	VidSClearSurface(pEditSurf, nKeyColour, NULL);
1043 
1044 	return 0;
1045 }
1046 
VidSInitOSD(int nFlags)1047 int VidSInitOSD(int nFlags)
1048 {
1049 #ifdef PRINT_DEBUG_INFO
1050 //	dprintf(_T(" ** OSD initialised.\n"));
1051 #endif
1052 
1053 	if (pDD == NULL) {
1054 		return 1;
1055 	}
1056 
1057 	if (VidSInitStatus(nFlags)) {
1058 		return 1;
1059 	}
1060 	if (VidSInitShortMsg(nFlags)) {
1061 		return 1;
1062 	}
1063 	if (VidSInitChat(nFlags)) {
1064 		return 1;
1065 	}
1066 	if (kNetGame) {
1067 		if (VidSInitEdit(nFlags)) {
1068 			return 1;
1069 		}
1070 	}
1071 	if (VidSInitTinyMsg(nFlags)) {
1072 		return 1;
1073 	}
1074 	if (VidSInitJoystickMsg(nFlags)) {
1075 		return 1;
1076 	}
1077 
1078 	return 0;
1079 }
1080 
VidSRestoreOSD()1081 int VidSRestoreOSD()
1082 {
1083 	if (pShortMsgSurf) {
1084 		if (FAILED(pShortMsgSurf->IsLost())) {
1085 			if (FAILED(pShortMsgSurf->Restore())) {
1086 				return 1;
1087 			}
1088 			VidSClearSurface(pShortMsgSurf, nKeyColour, NULL);
1089 
1090 			VidSShortMsg.nTimer = 0;
1091 		}
1092 	}
1093 
1094 	if (pTinyMsgSurf) {
1095 		if (FAILED(pTinyMsgSurf->IsLost())) {
1096 		if (FAILED(pTinyMsgSurf->Restore())) {
1097 				return 1;
1098 			}
1099 			VidSClearSurface(pTinyMsgSurf, nKeyColour, NULL);
1100 
1101 			VidSTinyMsg.nTimer = 0;
1102 		}
1103 	}
1104 
1105 	if (pJoystickMsgSurf) {
1106 		if (FAILED(pJoystickMsgSurf->IsLost())) {
1107 		if (FAILED(pJoystickMsgSurf->Restore())) {
1108 				return 1;
1109 			}
1110 			VidSClearSurface(pJoystickMsgSurf, nKeyColour, NULL);
1111 
1112 			VidSJoystickMsg.nTimer = 0;
1113 		}
1114 	}
1115 
1116 	if (pStatusSurf) {
1117 		if (FAILED(pStatusSurf->IsLost())) {
1118 			if (FAILED(pStatusSurf->Restore())) {
1119 				return 1;
1120 			}
1121 			VidSClearSurface(pStatusSurf, nKeyColour, NULL);
1122 
1123 			nPrevStatus = -1;
1124 		}
1125 	}
1126 
1127 	if (pChatSurf) {
1128 		if (FAILED(pChatSurf->IsLost())) {
1129 			if (FAILED(pChatSurf->Restore())) {
1130 				return 1;
1131 			}
1132 			VidSClearSurface(pChatSurf, nKeyColour, NULL);
1133 		}
1134 	}
1135 
1136 	if (kNetGame) {
1137 		if (pEditSurf) {
1138 			if (FAILED(pEditSurf->IsLost())) {
1139 				if (FAILED(pEditSurf->Restore())) {
1140 					return 1;
1141 				}
1142 				VidSClearSurface(pEditSurf, nKeyColour, NULL);
1143 			}
1144 		}
1145 	}
1146 
1147 	return 0;
1148 }
1149 
VidSDisplayTinyMsg(IDirectDrawSurface7 * pSurf,RECT * pRect)1150 static void VidSDisplayTinyMsg(IDirectDrawSurface7* pSurf, RECT* pRect)
1151 {
1152 	if (VidSTinyMsg.nTimer) {
1153 		RECT src = { 0, 0, 300, 20 };
1154 		RECT dest = { pRect->right - 320, pRect->bottom - 24, pRect->right - 8, pRect->bottom - 4 };
1155 
1156 		// Switch off message display when the message has been displayed long enough
1157 		if (nFramesEmulated > VidSTinyMsg.nTimer) {
1158 			VidSTinyMsg.nTimer = 0;
1159 		}
1160 
1161 		if (dest.left < pRect->left) {
1162 			src.left = pRect->left - dest.left;
1163 			dest.left = pRect->left;
1164 		}
1165 
1166 		if (nZoom & 2) {
1167 			dest.top <<= 1;
1168 			dest.bottom <<= 1;
1169 		}
1170 
1171 		if (nZoom & 1) {
1172 			dest.left <<= 1;
1173 			dest.right <<= 1;
1174 		}
1175 
1176 		// Blit the message to the surface using a colourkey
1177 		pSurf->Blt(&dest, pTinyMsgSurf, &src, DDBLT_ASYNC | DDBLT_KEYSRC, NULL);
1178 	}
1179 }
1180 
VidSDisplayJoystickMsg(IDirectDrawSurface7 * pSurf,RECT * pRect)1181 static void VidSDisplayJoystickMsg(IDirectDrawSurface7* pSurf, RECT* pRect)
1182 {
1183 	if (VidSJoystickMsg.nTimer) {
1184 		RECT src = { 0, 0, 300, 60 }; // left top right bottom
1185 		RECT dest = { pRect->left, pRect->bottom - 60, pRect->left + 300, pRect->bottom };
1186 
1187 		// Switch off message display when the message has been displayed long enough
1188 		if (nFramesEmulated > VidSJoystickMsg.nTimer) {
1189 			VidSJoystickMsg.nTimer = 0;
1190 		}
1191 
1192 		if (dest.left < pRect->left) {
1193 			src.left = pRect->left - dest.left;
1194 			dest.left = pRect->left;
1195 		}
1196 
1197 		if (nZoom & 2) {
1198 			dest.top <<= 1;
1199 			dest.bottom <<= 1;
1200 		}
1201 
1202 		if (nZoom & 1) {
1203 			dest.left <<= 1;
1204 			dest.right <<= 1;
1205 		}
1206 
1207 		// Blit the message to the surface using a colourkey
1208 		pSurf->Blt(&dest, pJoystickMsgSurf, &src, DDBLT_ASYNC | DDBLT_KEYSRC, NULL);
1209 	}
1210 }
1211 
VidSDisplayShortMsg(IDirectDrawSurface7 * pSurf,RECT * pRect)1212 static void VidSDisplayShortMsg(IDirectDrawSurface7* pSurf, RECT* pRect)
1213 {
1214 	if (VidSShortMsg.nTimer) {
1215 		RECT src = { 0, 0, 256, 32 };
1216 		RECT dest = { 0, pRect->top + 4, pRect->right - 8, pRect->top + 36 };
1217 
1218 		// Switch off message display when the message has been displayed long enough
1219 		if (nFramesEmulated > VidSShortMsg.nTimer) {
1220 			VidSShortMsg.nTimer = 0;
1221 		}
1222 
1223 		if (nStatus) {
1224 			for (int x = 0; x < 4; x++) {
1225 				if (nStatus & (1 << x)) {
1226 					dest.right -= 48;
1227 				}
1228 			}
1229 
1230 			dest.top += 10;
1231 			dest.bottom += 10;
1232 		}
1233 
1234 		dest.left = dest.right - 256;
1235 		if (dest.left < pRect->left) {
1236 			src.left = pRect->left - dest.left;
1237 			dest.left = pRect->left;
1238 		}
1239 
1240 		if (nZoom & 2) {
1241 			dest.top <<= 1;
1242 			dest.bottom <<= 1;
1243 		}
1244 
1245 		if (nZoom & 1) {
1246 			dest.left <<= 1;
1247 			dest.right <<= 1;
1248 		}
1249 
1250 		// Blit the message to the surface using a colourkey
1251 		pSurf->Blt(&dest, pShortMsgSurf, &src, DDBLT_ASYNC | DDBLT_KEYSRC, NULL);
1252 	}
1253 }
1254 
VidSDisplayStatus(IDirectDrawSurface7 * pSurf,RECT * pRect)1255 static void VidSDisplayStatus(IDirectDrawSurface7* pSurf, RECT* pRect)
1256 {
1257 	nStatus = 0;
1258 
1259 	if (nVidSDisplayStatus == 0) {
1260 		return;
1261 	}
1262 
1263 	if (bRunPause) {
1264 		nStatus |= 1;
1265 	}
1266 	if (kNetGame) {
1267 		nStatus |= 2;
1268 	}
1269 	if (nReplayStatus == 1) {
1270 		nStatus |= 4;
1271 	}
1272 	if (nReplayStatus == 2) {
1273 		nStatus |= 8;
1274 	}
1275 
1276 	if (nStatus != nPrevStatus) {
1277 		nPrevStatus = nStatus;
1278 		if (nStatus) {
1279 			// Print the message
1280 			HDC hDC;
1281 			HFONT hFont;
1282 			TCHAR szStatus[8];
1283 
1284 			// Clear the surface first
1285 			VidSClearSurface(pStatusSurf, nKeyColour, NULL);
1286 
1287 			// Make the status string
1288 			memset(szStatus, 0, sizeof(szStatus));
1289 			for (int i = 0, j = 0; i < 4; i++) {
1290 				if (nStatus & (1 << i)) {
1291 					szStatus[j] = nStatusSymbols[i];
1292 					j++;
1293 				}
1294 			}
1295 
1296 			pStatusSurf->GetDC(&hDC);
1297 			SetBkMode(hDC, TRANSPARENT);
1298 			hFont = (HFONT)SelectObject(hDC, (nReplayStatus == 1) ? StatusFontTiny : StatusFont);
1299 			SetTextAlign(hDC, TA_TOP | TA_RIGHT);
1300 
1301 			MyTextOut(hDC, 192 - 2, 0, szStatus, _tcslen(szStatus), 2, RGB(0xFF, 0x3F, 0x3F));
1302 
1303 			// Clean up
1304 			SelectObject(hDC, hFont);
1305 			pStatusSurf->ReleaseDC(hDC);
1306 		}
1307 	}
1308 
1309 	if (nStatus) {
1310 		RECT src = { 192, 0, 192, 48 };
1311 		RECT dest = { 0, pRect->top + 4, pRect->right - 4, pRect->top + 52 };
1312 		for (int x = 0; x < 4; x++) {
1313 			if (nStatus & (1 << x)) {
1314 				src.left -= 48;
1315 			}
1316 		}
1317 		dest.left = dest.right - (src.right - src.left);
1318 
1319 		if (nZoom & 2) {
1320 			dest.top <<= 1;
1321 			dest.bottom <<= 1;
1322 		}
1323 
1324 		if (nZoom & 1) {
1325 			dest.left <<= 1;
1326 			dest.right <<= 1;
1327 		}
1328 
1329 		// Blit the message to the surface using a colourkey
1330 		pSurf->Blt(&dest, pStatusSurf, &src, DDBLT_ASYNC  | DDBLT_KEYSRC, NULL);
1331 	}
1332 }
1333 
VidSDrawChat(RECT * dest)1334 static int VidSDrawChat(RECT* dest)
1335 {
1336 	bool bContent = false;
1337 
1338 	if (pChatSurf == NULL) {
1339 		return 1;
1340 	}
1341 
1342 	VidSClearSurface(pChatSurf, nKeyColour, NULL);
1343 
1344 	for (int i = 0; i < CHAT_SIZE; i++) {
1345 		if (VidSChatMessage[i].pIDText || VidSChatMessage[i].pMainText) {
1346 			bContent = true;
1347 			break;
1348 		}
1349 	}
1350 
1351 	if (bContent) {
1352 
1353 		nChatTimer = nFramesEmulated + 300;
1354 
1355 		// Print the message
1356 		HDC hDC;
1357 		HFONT hFont;
1358 		int nStringLength;
1359 		int nFit = 0;
1360 		SIZE sizeID, sizeMain;
1361 
1362 		// Clear the surface first
1363 		VidSClearSurface(pChatSurf, nKeyColour, NULL);
1364 
1365 		pChatSurf->GetDC(&hDC);
1366 		SetBkMode(hDC, TRANSPARENT);
1367 
1368 		hFont = (HFONT)SelectObject(hDC, ChatIDFont);
1369 		SetTextAlign(hDC, TA_TOP | TA_LEFT);
1370 
1371 		nChatOverFlow = 0;
1372 
1373 		for (int i = 0; i < CHAT_SIZE; i++) {
1374 
1375 			// Exit when too many lines are displayed
1376 			if (nChatOverFlow > (CHAT_SIZE >> 1)) {
1377 				break;
1378 			}
1379 
1380 			if (VidSChatMessage[i].pIDText) {
1381 				SelectObject(hDC, ChatIDFont);
1382 				nStringLength =  _tcslen(VidSChatMessage[i].pIDText);
1383 				MyExtTextOut(hDC, 0, (i + nChatOverFlow) * nChatFontSize, ETO_CLIPPED, dest, VidSChatMessage[i].pIDText, nStringLength, NULL, nChatShadowOffset, VidSChatMessage[i].nIDColour);
1384 				GetTextExtentPoint32(hDC, VidSChatMessage[i].pIDText, nStringLength, &sizeID);
1385 			} else {
1386 				sizeID.cx = 0;
1387 			}
1388 
1389 			if (VidSChatMessage[i].pMainText) {
1390 				SelectObject(hDC, ChatMainFont);
1391 				nStringLength =  _tcslen(VidSChatMessage[i].pMainText);
1392 				GetTextExtentExPoint(hDC, VidSChatMessage[i].pMainText, nStringLength, dest->right - sizeID.cx, &nFit, NULL, &sizeMain);
1393 
1394 				if (nFit < nStringLength) {
1395 					// The string doesn't fit on the screen in one line, break the line on the last space that fits
1396 					while (nFit > 0 && (VidSChatMessage[i].pMainText[nFit] != ' ')) {
1397 						nFit--;
1398 					}
1399 
1400 					nStringLength = nFit;
1401 					MyExtTextOut(hDC, sizeID.cx, (i + nChatOverFlow) * nChatFontSize, ETO_CLIPPED, dest, VidSChatMessage[i].pMainText, nStringLength, NULL, nChatShadowOffset, VidSChatMessage[i].nMainColour);
1402 
1403 					nChatOverFlow++;
1404 
1405 					nStringLength =  _tcslen(&(VidSChatMessage[i].pMainText[nFit + 1]));
1406 					MyExtTextOut(hDC, sizeID.cx, (i + nChatOverFlow) * nChatFontSize, ETO_CLIPPED, dest, &(VidSChatMessage[i].pMainText[nFit + 1]), nStringLength, NULL, nChatShadowOffset, VidSChatMessage[i].nMainColour);
1407 				} else {
1408 					SelectObject(hDC, ChatMainFont);
1409 					MyTextOut(hDC, sizeID.cx, (i + nChatOverFlow) * nChatFontSize, VidSChatMessage[i].pMainText, nStringLength, nChatShadowOffset, VidSChatMessage[i].nMainColour);
1410 				}
1411 			}
1412 		}
1413 
1414 		// Clean up
1415 		SelectObject(hDC, hFont);
1416 		pChatSurf->ReleaseDC(hDC);
1417 	} else {
1418 		nChatTimer = 0;
1419 	}
1420 
1421 	return 0;
1422 }
1423 
VidSDisplayChat(IDirectDrawSurface7 * pSurf,RECT * pRect)1424 static void VidSDisplayChat(IDirectDrawSurface7* pSurf, RECT* pRect)
1425 {
1426 	if (nChatTimer || bDrawChat) {
1427 		// Blit the message to the surface using a colourkey
1428 		RECT src = { 0, 0, pRect->right - pRect->left, nChatFontSize * (CHAT_SIZE + (CHAT_SIZE >> 1)) };
1429 
1430 		// Update the message if needed
1431 		if (bDrawChat) {
1432 			VidSDrawChat(&src);
1433 			bDrawChat = false;
1434 		}
1435 
1436 		if (nChatTimer) {
1437 			RECT dest = { pRect->left, pRect->bottom - nChatFontSize * (CHAT_SIZE + nChatOverFlow), pRect->right, pRect->bottom };
1438 			src.bottom = nChatFontSize * (CHAT_SIZE + nChatOverFlow);
1439 
1440 			if (bEditActive) {
1441 				dest.top -= nEditSize;
1442 				dest.bottom -= nEditSize;
1443 			} else {
1444 				dest.top -= 4;
1445 				dest.bottom -= 4;
1446 			}
1447 
1448 			if (nZoom & 2) {
1449 				dest.top <<= 1;
1450 				dest.bottom <<= 1;
1451 			}
1452 
1453 			if (nZoom & 1) {
1454 				dest.left <<= 1;
1455 				dest.right <<= 1;
1456 			}
1457 
1458 			pSurf->Blt(&dest, pChatSurf, &src, DDBLT_ASYNC | DDBLT_KEYSRC, NULL);
1459 		}
1460 
1461 		// Scroll message if needed
1462 		if (nFramesEmulated > nChatTimer) {
1463 			nChatTimer = 0;
1464 			VidSAddChatMsg(NULL, 0, NULL, 0);
1465 			bDrawChat = true;
1466 		}
1467 	}
1468 }
1469 
VidSDisplayEdit(IDirectDrawSurface7 * pSurf,RECT * pRect)1470 static void VidSDisplayEdit(IDirectDrawSurface7* pSurf, RECT* pRect)
1471 {
1472 	if (bEditActive) {
1473 		RECT src = { 0, 0, pRect->right - pRect->left, nEditSize };
1474 		RECT dest = { pRect->left, pRect->bottom - nEditSize, pRect->right, pRect->bottom };
1475 
1476 		if (nZoom & 2) {
1477 			dest.top <<= 1;
1478 			dest.bottom <<= 1;
1479 		}
1480 
1481 		if (nZoom & 1) {
1482 			dest.left <<= 1;
1483 			dest.right <<= 1;
1484 		}
1485 
1486 		DWORD nStart, nEnd;
1487 		SendMessage(hwndChat, EM_GETSEL, (WPARAM)&nStart, (WPARAM)&nEnd);
1488 
1489 		if (nStart != nPrevStart || nEnd != nPrevEnd) {
1490 			nPrevStart = nStart;
1491 			nPrevEnd = nEnd;
1492 			bEditTextChanged = true;
1493 			nCursorTime = nFramesEmulated + 30;
1494 			nCursorState = 1;
1495 		} else {
1496 			if (GetCurrentFrame() > nCursorTime) {
1497 				nCursorTime = nFramesEmulated + 30;
1498 				nCursorState = !nCursorState;
1499 				bEditTextChanged = true;
1500 			}
1501 		}
1502 
1503 		if (bEditTextChanged) {
1504 			if (pEditSurf == NULL) {
1505 				return;
1506 			}
1507 
1508 			// Print the message
1509 			HDC hDC;
1510 			HFONT hFont;
1511 			SIZE size;
1512 			int pos;
1513 
1514 			TCHAR Space[] = _T(" ");
1515 			unsigned int nFit = 0;
1516 			TCHAR* pText = EditText;
1517 
1518 //			bool bDisplayCursor = false;
1519 //			if (GetFocus()) {
1520 //				bDisplayCursor = true;
1521 //			}
1522 
1523 			// Clear the surface first
1524 			VidSClearSurface(pEditSurf, nKeyColour, NULL);
1525 
1526 			pEditSurf->GetDC(&hDC);
1527 			SetBkMode(hDC, TRANSPARENT);
1528 			hFont = (HFONT)SelectObject(hDC, EditTextFont);
1529 			SetTextAlign(hDC, TA_TOP | TA_LEFT);
1530 
1531 			// If the string is wider than the screen, shift the display to the right.
1532 			pos = 0;
1533 			do {
1534 				GetTextExtentExPoint(hDC, pText, _tcslen(pText), src.right - pos - nEditSize, (int*)&nFit, NULL, &size);
1535 				if (nFit < nStart) {
1536 					pos -= src.right * 3 / 5;
1537 				}
1538 			} while (nFit < nStart);
1539 
1540 			if (nStart == _tcslen(pText)) {
1541 				if (nStart) {
1542 					MyExtTextOut(hDC, pos + 3, 3, ETO_CLIPPED, &src, pText, nStart, NULL, nEditShadowOffset, RGB(0xDF, 0xDF, 0xFF));
1543 				}
1544 
1545 				GetTextExtentPoint32(hDC, pText, nStart, &size);
1546 				pos += size.cx;
1547 
1548 				if (nCursorState) {
1549 					SelectObject(hDC, EditCursorFont);
1550 				}
1551 				MyExtTextOut(hDC, pos + 3, 3, ETO_CLIPPED, &src, Space, 1, NULL, nEditShadowOffset, RGB(0xFF, 0xFF, 0xFF));
1552 			} else {
1553 				if (nStart) {
1554 					MyExtTextOut(hDC, pos + 3, 3, ETO_CLIPPED, &src, pText, nStart, NULL, nEditShadowOffset, RGB(0xDF, 0xDF, 0xFF));
1555 				}
1556 
1557 				GetTextExtentPoint32(hDC, pText, nStart, &size);
1558 				pos += size.cx;
1559 
1560 				if (nEnd == nStart) {
1561 					if (nCursorState) {
1562 						SelectObject(hDC, EditCursorFont);
1563 					}
1564 					MyExtTextOut(hDC, pos + 3, 3, ETO_CLIPPED, &src, pText + nStart, 1, NULL, nEditShadowOffset, RGB(0xFF, 0xFF, 0xFF));
1565 
1566 					GetTextExtentPoint32(hDC, pText + nStart, 1, &size);
1567 					pos += size.cx;
1568 
1569 					// We really printed one character past the end of the selection
1570 					nEnd++;
1571 				} else {
1572 					// Highlight selected characters
1573 					SelectObject(hDC, EditTextFont);
1574 					// SetTextColor(hDC, RGB(0x7F, 0x1F, 0x1F));
1575 					MyExtTextOut(hDC, pos + 3, 3, ETO_CLIPPED, &src, pText + nStart, nEnd - nStart, NULL, nEditShadowOffset, RGB(0xFF, 0xFF, 0xDF));
1576 
1577 					GetTextExtentPoint32(hDC, pText + nStart, nEnd - nStart, &size);
1578 					pos += size.cx;
1579 				}
1580 
1581 				if (nEnd < _tcslen(EditText)) {
1582 					SelectObject(hDC, EditTextFont);
1583 					MyExtTextOut(hDC, pos + 3, 3, ETO_CLIPPED, &src, pText + nEnd, _tcslen(pText) - nEnd, NULL, nEditShadowOffset, RGB(0xDF, 0xDF, 0xFF));
1584 				}
1585 			}
1586 
1587 			// Clean up
1588 			SelectObject(hDC, hFont);
1589 			pEditSurf->ReleaseDC(hDC);
1590 
1591 			bEditTextChanged = false;
1592 		}
1593 
1594 		// Blit the message to the surface using a colourkey
1595 		pSurf->Blt(&dest, pEditSurf, &src, DDBLT_ASYNC | DDBLT_KEYSRC, NULL);
1596 	}
1597 }
1598 
VidSDisplayOSD(IDirectDrawSurface7 * pSurf,RECT * pRect,int nFlags)1599 void VidSDisplayOSD(IDirectDrawSurface7* pSurf, RECT* pRect, int nFlags)
1600 {
1601 	nZoom = nFlags & 3;
1602 
1603 	VidSDisplayStatus(pSurf, pRect);
1604 	VidSDisplayTinyMsg(pSurf, pRect);
1605 	VidSDisplayJoystickMsg(pSurf, pRect);
1606 	VidSDisplayShortMsg(pSurf, pRect);
1607 	VidSDisplayChat(pSurf, pRect);
1608 	if (kNetGame) {
1609 		VidSDisplayEdit(pSurf, pRect);
1610 	}
1611 }
1612 
VidSNewTinyMsg(const TCHAR * pText,int nRGB,int nDuration,int nPriority)1613 int VidSNewTinyMsg(const TCHAR* pText, int nRGB, int nDuration, int nPriority)	// int nRGB = 0, int nDuration = 0, int nPriority = 5
1614 {
1615 	// If a message with a higher priority is being displayed, exit.
1616 	if (VidSTinyMsg.nTimer && VidSTinyMsg.nPriority > nPriority) {
1617 		return 1;
1618 	}
1619 
1620 	int nSize = _tcslen(pText);
1621 	if (nSize > 63) {
1622 		nSize = 63;
1623 	}
1624 	_tcsncpy(VidSTinyMsg.pMsgText, pText, nSize);
1625 	VidSTinyMsg.pMsgText[nSize] = 0;
1626 
1627 	if (nRGB) {
1628 		// Convert RGB value to COLORREF
1629 		VidSTinyMsg.nColour = RGB((nRGB >> 16), ((nRGB >> 8) & 0xFF), (nRGB & 0xFF));
1630 	} else {
1631 		// Default message colour (yellow)
1632 		VidSTinyMsg.nColour = RGB(0xFF, 0xFF, 0x7F);
1633 	}
1634 	if (nDuration) {
1635 		VidSTinyMsg.nTimer = nFramesEmulated + nDuration;
1636 	} else {
1637 		VidSTinyMsg.nTimer = nFramesEmulated + 120;
1638 	}
1639 	VidSTinyMsg.nPriority = nPriority;
1640 
1641 	{
1642 		if (pTinyMsgSurf == NULL) {
1643 			return 1;
1644 		}
1645 
1646 		// Print the message
1647 		HDC hDC;
1648 		HFONT hFont;
1649 
1650 		// Clear the surface first
1651 		VidSClearSurface(pTinyMsgSurf, nKeyColour, NULL);
1652 
1653 		pTinyMsgSurf->GetDC(&hDC);
1654 		SetBkMode(hDC, TRANSPARENT);
1655 		hFont = (HFONT)SelectObject(hDC, TinyMsgFont);
1656 		SetTextAlign(hDC, TA_BOTTOM | TA_RIGHT);
1657 
1658 		// Print a black shadow
1659 		SetTextColor(hDC, 0);
1660 		TextOut(hDC, 300, 20, VidSTinyMsg.pMsgText, _tcslen(VidSTinyMsg.pMsgText));
1661 		// Print the text on top
1662 		SetTextColor(hDC, VidSTinyMsg.nColour);
1663 		TextOut(hDC, 300 - 1, 20 - 1, VidSTinyMsg.pMsgText, _tcslen(VidSTinyMsg.pMsgText));
1664 
1665 		// Clean up
1666 		SelectObject(hDC, hFont);
1667 		pTinyMsgSurf->ReleaseDC(hDC);
1668 	}
1669 
1670 	return 0;
1671 }
1672 
VidSNewJoystickMsg(const TCHAR * pText,int nRGB,int nDuration,int nLineNo)1673 int VidSNewJoystickMsg(const TCHAR* pText, int nRGB, int nDuration, int nLineNo)	// int nRGB = 0, int nDuration = 0, int nLineNo = 0
1674 {
1675 	if (!pText) { // NULL passed, clear surface
1676 		VidSClearSurface(pJoystickMsgSurf, nKeyColour, NULL);
1677 		return 0;
1678 	}
1679 
1680 	int nSize = _tcslen(pText);
1681 	if (nSize > 63) {
1682 		nSize = 63;
1683 	}
1684 	_tcsncpy(VidSJoystickMsg.pMsgText, pText, nSize);
1685 	VidSJoystickMsg.pMsgText[nSize] = 0;
1686 
1687 	if (nRGB) {
1688 		// Convert RGB value to COLORREF
1689 		VidSJoystickMsg.nColour = RGB((nRGB >> 16), ((nRGB >> 8) & 0xFF), (nRGB & 0xFF));
1690 	} else {
1691 		// Default message colour (yellow)
1692 		VidSJoystickMsg.nColour = RGB(0xFF, 0xFF, 0x7F);
1693 	}
1694 	if (nDuration) {
1695 		VidSJoystickMsg.nTimer = nFramesEmulated + nDuration;
1696 	} else {
1697 		VidSJoystickMsg.nTimer = nFramesEmulated + 120;
1698 	}
1699 	VidSJoystickMsg.nLineNo = nLineNo;
1700 
1701 	{
1702 		if (pJoystickMsgSurf == NULL) {
1703 			return 1;
1704 		}
1705 
1706 		// Print the message
1707 		HDC hDC;
1708 		HFONT hFont;
1709 
1710 		pJoystickMsgSurf->GetDC(&hDC);
1711 		SetBkMode(hDC, TRANSPARENT);
1712 		hFont = (HFONT)SelectObject(hDC, JoystickMsgFont);
1713 		SetTextAlign(hDC, TA_TOP | TA_LEFT);
1714 
1715 		MyTextOut(hDC, 40, 7+(nLineNo*8), VidSJoystickMsg.pMsgText, _tcslen(VidSJoystickMsg.pMsgText), 0, VidSJoystickMsg.nColour);
1716 
1717 		// Clean up
1718 		SelectObject(hDC, hFont);
1719 		pJoystickMsgSurf->ReleaseDC(hDC);
1720 	}
1721 
1722 	return 0;
1723 }
1724 
VidSNewShortMsg(const TCHAR * pText,int nRGB,int nDuration,int nPriority)1725 int VidSNewShortMsg(const TCHAR* pText, int nRGB, int nDuration, int nPriority)	// int nRGB = 0, int nDuration = 0, int nPriority = 5
1726 {
1727 	// If a message with a higher priority is being displayed, exit.
1728 	if (VidSShortMsg.nTimer && VidSShortMsg.nPriority > nPriority) {
1729 		return 1;
1730 	}
1731 
1732 	int nSize = _tcslen(pText);
1733 	if (nSize > 31) {
1734 		nSize = 31;
1735 	}
1736 	_tcsncpy(VidSShortMsg.pMsgText, pText, nSize);
1737 	VidSShortMsg.pMsgText[nSize] = 0;
1738 
1739 	// copy osd message
1740 	memset(OSDMsg, '\0', MAX_PATH * sizeof(TCHAR));
1741 	_tcsncpy(OSDMsg, pText, nSize);
1742 
1743 	if (nRGB) {
1744 		// Convert RGB value to COLORREF
1745 		VidSShortMsg.nColour = RGB((nRGB >> 16), ((nRGB >> 8) & 0xFF), (nRGB & 0xFF));
1746 	} else {
1747 		// Default message colour (yellow)
1748 		VidSShortMsg.nColour = RGB(0xFF, 0xFF, 0x7F);
1749 	}
1750 	if (nDuration) {
1751 		VidSShortMsg.nTimer = nFramesEmulated + nDuration;
1752 	} else {
1753 		VidSShortMsg.nTimer = nFramesEmulated + 120;
1754 	}
1755 	nOSDTimer = VidSShortMsg.nTimer;
1756 	VidSShortMsg.nPriority = nPriority;
1757 
1758 	{
1759 		if (pShortMsgSurf == NULL) {
1760 			return 1;
1761 		}
1762 
1763 		// Print the message
1764 		HDC hDC;
1765 		HFONT hFont;
1766 
1767 		// Clear the surface first
1768 		VidSClearSurface(pShortMsgSurf, nKeyColour, NULL);
1769 
1770 		pShortMsgSurf->GetDC(&hDC);
1771 		SetBkMode(hDC, TRANSPARENT);
1772 		hFont = (HFONT)SelectObject(hDC, ShortMsgFont);
1773 		SetTextAlign(hDC, TA_TOP | TA_RIGHT);
1774 
1775 		MyTextOut(hDC, 256 - 2, 0, VidSShortMsg.pMsgText, _tcslen(VidSShortMsg.pMsgText), 2, VidSShortMsg.nColour);
1776 
1777 		// Clean up
1778 		SelectObject(hDC, hFont);
1779 		pShortMsgSurf->ReleaseDC(hDC);
1780 	}
1781 
1782 	return 0;
1783 }
1784 
VidSKillShortMsg()1785 void VidSKillShortMsg()
1786 {
1787 	VidSShortMsg.nTimer = 0;
1788 }
1789 
VidSKillTinyMsg()1790 void VidSKillTinyMsg()
1791 {
1792 	VidSTinyMsg.nTimer = 0;
1793 }
1794 
VidSKillJoystickMsg()1795 void VidSKillJoystickMsg()
1796 {
1797 	VidSTinyMsg.nTimer = 0;
1798 }
1799 
VidSKillOSDMsg()1800 void VidSKillOSDMsg()
1801 {
1802 	nOSDTimer = 0;
1803 }
1804 
VidSAddChatMsg(const TCHAR * pID,int nIDRGB,const TCHAR * pMain,int nMainRGB)1805 int VidSAddChatMsg(const TCHAR* pID, int nIDRGB, const TCHAR* pMain, int nMainRGB)
1806 {
1807 	if (pID || pMain) {
1808 		// Scroll the text buffers up one entry
1809 		if (VidSChatMessage[0].pIDText) {
1810 			free(VidSChatMessage[0].pIDText);
1811 			VidSChatMessage[0].pIDText = NULL;
1812 		}
1813 		if (VidSChatMessage[0].pMainText) {
1814 			free(VidSChatMessage[0].pMainText);
1815 			VidSChatMessage[0].pMainText = NULL;
1816 		}
1817 
1818 		for (int i = 1; i < CHAT_SIZE; i++) {
1819 			VidSChatMessage[i - 1].pIDText = VidSChatMessage[i].pIDText;
1820 			VidSChatMessage[i - 1].nIDColour = VidSChatMessage[i].nIDColour;
1821 			VidSChatMessage[i - 1].pMainText = VidSChatMessage[i].pMainText;
1822 			VidSChatMessage[i - 1].nMainColour = VidSChatMessage[i].nMainColour;
1823 		}
1824 
1825 		// Put the new entry in the last position
1826 		if (pID) {
1827 			int nSize = _tcslen(pID);
1828 			VidSChatMessage[CHAT_SIZE - 1].pIDText = (TCHAR*)malloc((nSize + 1) * sizeof(TCHAR));
1829 			 _tcscpy(VidSChatMessage[CHAT_SIZE - 1].pIDText, pID);
1830 			VidSChatMessage[CHAT_SIZE - 1].pIDText[nSize] = 0;
1831 			VidSChatMessage[CHAT_SIZE - 1].nIDColour = RGB((nIDRGB >> 16), ((nIDRGB >> 8) & 0xFF), (nIDRGB & 0xFF));
1832 		} else {
1833 			VidSChatMessage[CHAT_SIZE - 1].pIDText = NULL;
1834 		}
1835 		if (pMain) {
1836 			int nSize = _tcslen(pMain);
1837 			VidSChatMessage[CHAT_SIZE - 1].pMainText = (TCHAR*)malloc((nSize + 1) * sizeof(TCHAR));
1838 			 _tcscpy(VidSChatMessage[CHAT_SIZE - 1].pMainText, pMain);
1839 			VidSChatMessage[CHAT_SIZE - 1].pMainText[nSize] = 0;
1840 			VidSChatMessage[CHAT_SIZE - 1].nMainColour = RGB((nMainRGB >> 16), ((nMainRGB >> 8) & 0xFF), (nMainRGB & 0xFF));
1841 		} else {
1842 			VidSChatMessage[CHAT_SIZE - 1].pMainText = NULL;
1843 		}
1844 	} else {
1845 		for (int i = 0; i < CHAT_SIZE; i++) {
1846 			if (VidSChatMessage[i].pIDText || VidSChatMessage[i].pMainText) {
1847 				if (VidSChatMessage[i].pIDText) {
1848 					free(VidSChatMessage[i].pIDText);
1849 					VidSChatMessage[i].pIDText = NULL;
1850 				}
1851 				if (VidSChatMessage[i].pMainText) {
1852 					free(VidSChatMessage[i].pMainText);
1853 					VidSChatMessage[i].pMainText = NULL;
1854 				}
1855 
1856 				break;
1857 			}
1858 		}
1859 	}
1860 
1861 	bDrawChat = true;
1862 
1863 	return 0;
1864 }
1865