1 // DirectDraw blitter
2 
3 #include "burner.h"
4 
5 #if !defined BUILD_X64_EXE
6  #include "vid_directx_support.h"
7 #endif
8 
9 #include <InitGuid.h>
10 #define DIRECT3D_VERSION 0x0700							// Use this Direct3D version
11 
12 #if defined BUILD_X64_EXE
13  #include "vid_directx_support.h"
14 #endif
15 
16 #include "ddraw_core.h"
17 
18 static IDirectDraw7* DtoDD = NULL;				// DirectDraw interface
19 static IDirectDrawSurface7* DtoPrim = NULL;		// Primary surface
20 static IDirectDrawSurface7* DtoBack = NULL;		// Back buffer surface
21 
22 static int nRotateGame;
23 static bool bRotateScanlines;
24 static DDBLTFX* DtoBltFx = NULL;				// We use mirrored blits for flipped games
25 
26 static IDirectDrawSurface7* pddsDtos = NULL;	// The screen surface
27 static int nGameWidth = 0, nGameHeight = 0;		// screen size
28 
29 static bool bDtosScan;
30 
31 static RECT Src = { 0, 0, 0, 0 };
32 static RECT Dest = { 0, 0, 0, 0 };
33 
34 static int nHalfMask = 0;
35 
36 static int nUseSys;								// Use System or Video memory
37 
DtoPrimClear()38 static int DtoPrimClear()
39 {
40 	if (DtoPrim == NULL) {
41 		return 1;
42 	}
43 
44 	VidSClearSurface(DtoPrim, 0, NULL);			// Clear 1st page
45 
46 	if (DtoBack) {								// We're using a triple buffer
47 		VidSClearSurface(DtoBack, 0, NULL);		// Clear 2nd page
48 		DtoPrim->Flip(NULL, DDFLIP_WAIT);
49 		VidSClearSurface(DtoBack, 0, NULL);		// Clear 3rd page
50 	}
51 
52 	return 0;
53 }
54 
DtoPrimInit(int bTriple)55 static int DtoPrimInit(int bTriple)
56 {
57 	DDSURFACEDESC2 ddsd;
58 
59 	// Create the primary surface
60 	memset(&ddsd, 0, sizeof(ddsd));
61 	ddsd.dwSize = sizeof(ddsd);
62 	if (bTriple) {															// Make a primary surface capable of triple bufferring
63 		ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
64 		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY;
65 		ddsd.dwBackBufferCount = 2;
66 	} else {
67 		ddsd.dwFlags = DDSD_CAPS;
68 		ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
69 	}
70 
71 	if (FAILED(DtoDD->CreateSurface(&ddsd, &DtoPrim, NULL))) {
72 		return 1;
73 	}
74 
75 	if (bTriple) {
76 		// Get the back buffer
77 		memset(&ddsd.ddsCaps, 0, sizeof(ddsd.ddsCaps));
78 		ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
79 
80 		if (FAILED(DtoPrim->GetAttachedSurface(&ddsd.ddsCaps, &DtoBack))) {	// Failed to make triple buffer
81 			RELEASE(DtoPrim)
82 			return 1;
83 		}
84 
85 		DtoPrimClear();														// Clear surfaces
86 	}
87 
88 	return 0;
89 }
90 
91 // Try to autodetect the best secondary buffer type to use, based on the hardware capabilities
AutodetectUseSys()92 static int AutodetectUseSys()
93 {
94 	DDCAPS ddc;
95 
96 	memset(&ddc, 0, sizeof(ddc));
97 	ddc.dwSize = sizeof(ddc);
98 	DtoDD->GetCaps(&ddc, NULL);
99 
100 	if (ddc.dwCaps & DDCAPS_BLTSTRETCH) {					// If it can do a hardware stretch use video memory
101 		return 0;
102 	} else {												// Otherwise use system memory:
103 		return 1;
104 	}
105 }
106 
107 // Create a secondary DirectDraw surface for the game image
DtosMakeSurf()108 static int DtosMakeSurf()
109 {
110 	int nRet;
111 
112 	nUseSys = 0;
113 	DDSURFACEDESC2 ddsd;
114 
115 	if (DtoDD == NULL) {
116 		return 1;
117 	}
118 
119 	nUseSys = nVidTransferMethod;
120 	if (nUseSys < 0) {
121 		nUseSys = AutodetectUseSys();
122 	}
123 
124 	// Try to allocate buffer in Video memory first, if that fails use System memory
125 	do {
126 		memset(&ddsd, 0, sizeof(ddsd));
127 		ddsd.dwSize = sizeof(ddsd);
128 
129 		ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
130 		ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
131 		if (nUseSys == 0) {
132 			ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
133 		} else {
134 			if (nUseSys == 1) {
135 				ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
136 			}
137 		}
138 
139 		ddsd.dwWidth = nGameWidth << 1;						// Make the surface large enough to add scanlines
140 		ddsd.dwHeight = nGameHeight << 1;					//
141 
142 		nRet = DtoDD->CreateSurface(&ddsd, &pddsDtos, NULL);
143 
144 		if (SUCCEEDED(nRet)) {								// Break early, so nUseSys will keep its value
145 			break;
146 		}
147 
148 	} while (++nUseSys <= 1);
149 
150 	if (FAILED(nRet)) {
151 		return 1;
152 	}
153 
154 	nVidScrnDepth = VidSGetSurfaceDepth(pddsDtos);			// Get colourdepth of primary surface
155 
156 	VidSClearSurface(pddsDtos, 0, NULL);
157 
158 	return 0;
159 }
160 
DtosExit()161 static int DtosExit()
162 {
163 	RELEASE(pddsDtos);
164 
165 	VidSFreeVidImage();
166 
167 	VidSExitOSD();
168 
169 	return 0;
170 }
171 
DtosInit()172 static int DtosInit()
173 {
174 	if (DtoDD == NULL) {
175 		return 1;
176 	}
177 
178 	if (nRotateGame & 1) {
179 		nVidImageWidth = nGameHeight;
180 		nVidImageHeight = nGameWidth;
181 	} else {
182 		nVidImageWidth = nGameWidth;
183 		nVidImageHeight = nGameHeight;
184 	}
185 
186 	if (bVidScanRotate && nGameWidth < nGameHeight) {
187 		bRotateScanlines = true;
188 	} else {
189 		bRotateScanlines = false;
190 	}
191 
192 	nVidImageDepth = VidSGetSurfaceDepth(DtoPrim);	// Get color depth of primary surface
193 	nVidImageBPP = (nVidImageDepth + 7) >> 3;
194 
195 	// Make the mask to mask out all but the lowest intensity bit
196 	if (nVidImageDepth == 15) {
197 		nHalfMask = 0xFBDEFBDE;
198 	} else {
199 		if (nVidImageDepth == 16) {
200 			nHalfMask = 0xF7DEF7DE;
201 		} else {
202 			nHalfMask = 0xFEFEFEFE;
203 		}
204 	}
205 
206 	// Make the normal memory buffer
207 	if (VidSAllocVidImage()) {
208 		DtosExit();
209 		return 1;
210 	}
211 
212 	// Make the DirectDraw secondary surface
213 	if (DtosMakeSurf()) {
214 		DtosExit();
215 		return 1;
216 	}
217 
218 	// Use our callback to get colors:
219 	SetBurnHighCol(nVidImageDepth);
220 
221 	Dest.left = 0; Dest.right = -1;
222 
223 	RECT rect = { 0, 0, 0, 0 };
224 	GetClientScreenRect(hVidWnd, &rect);
225 	rect.top += nMenuHeight;
226 
227 	VidSScaleImage(&rect, nGameWidth, nGameHeight, bVidScanRotate);
228 
229 	VidSInitOSD(4);
230 
231 	return 0;
232 }
233 
vidExit()234 static int vidExit()
235 {
236 	VidSRestoreGamma();
237 
238 	VidSRestoreScreenMode();
239 
240 	DtosExit();
241 
242 	RELEASE(DtoPrim)					// a single call releases all surfaces
243 	DtoBack = NULL;
244 
245 	VidSExit();
246 
247 	if (DtoBltFx) {
248 		free(DtoBltFx);
249 		DtoBltFx = NULL;
250 	}
251 
252 	RELEASE(DtoDD)
253 
254 	return 0;
255 }
256 
257 static GUID MyGuid;
258 static int nWantDriver;
259 
260 #ifdef PRINT_DEBUG_INFO
261 static int nCurrentDriver;
262 #ifdef UNICODE
MyEnumDisplayDrivers(GUID FAR * pGuid,LPWSTR pszDesc,LPWSTR,LPVOID,HMONITOR)263 static BOOL PASCAL MyEnumDisplayDrivers(GUID FAR* pGuid, LPWSTR pszDesc, LPWSTR /*pszName*/, LPVOID /*pContext*/, HMONITOR /*hMonitor*/)
264 #else
265 static BOOL PASCAL MyEnumDisplayDrivers(GUID FAR* pGuid, LPSTR pszDesc, LPSTR /*pszName*/, LPVOID /*pContext*/, HMONITOR /*hMonitor*/)
266 #endif
267 {
268 	if (nCurrentDriver == nWantDriver) {
269 		memcpy(&MyGuid, pGuid, sizeof(GUID));
270 	}
271 
272 	if (nCurrentDriver == 0) {
273 		dprintf(_T("    %s\n"), pszDesc);
274 	} else {
275 		dprintf(_T("    Display %d (on %s)\n"), nCurrentDriver, pszDesc);
276 	}
277 
278 	nCurrentDriver++;
279 
280 	return DDENUMRET_OK;
281 }
282 #endif
283 
vidInit()284 static int vidInit()
285 {
286 	hVidWnd = hScrnWnd;								// Use Screen window for video
287 
288 #ifdef PRINT_DEBUG_INFO
289 	dprintf(_T("  * Enumerating available drivers:\n"));
290 	nWantDriver = 0;
291 	nCurrentDriver = 0;
292 	_DirectDrawEnumerateEx(MyEnumDisplayDrivers, NULL, DDENUM_ATTACHEDSECONDARYDEVICES | DDENUM_DETACHEDSECONDARYDEVICES | DDENUM_NONDISPLAYDEVICES);
293 #endif
294 
295 	// Get pointer to DirectDraw device
296 	_DirectDrawCreateEx(nWantDriver ? &MyGuid : NULL, (void**)&DtoDD, IID_IDirectDraw7, NULL);
297 
298 	VidSInit(DtoDD);
299 
300 	nGameWidth = nVidImageWidth; nGameHeight = nVidImageHeight;
301 
302 	nRotateGame = 0;
303 	if (bDrvOkay) {
304 		DtoBltFx = NULL;
305 
306 		// Get the game screen size
307 		BurnDrvGetVisibleSize(&nGameWidth, &nGameHeight);
308 
309 	    if (BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) {
310 			if (nVidRotationAdjust & 1) {
311 				int n = nGameWidth;
312 				nGameWidth = nGameHeight;
313 				nGameHeight = n;
314 				nRotateGame |= (nVidRotationAdjust & 2);
315 			} else {
316 				nRotateGame |= 1;
317 			}
318 		}
319 		if (BurnDrvGetFlags() & BDF_ORIENTATION_FLIPPED) {
320 			nRotateGame ^= 2;
321 		}
322 
323 		if (nRotateGame & 2) {
324 			DDCAPS ddcaps;
325 
326 			// Disable flipping until we've checked the hardware supports it
327 			nRotateGame &= ~2;
328 
329 			memset(&ddcaps, 0, sizeof(ddcaps));
330 			ddcaps.dwSize = sizeof(ddcaps);
331 
332 			DtoDD->GetCaps(&ddcaps, NULL);
333 			if (((ddcaps.dwFXCaps & DDFXCAPS_BLTMIRRORLEFTRIGHT) && (ddcaps.dwFXCaps & DDFXCAPS_BLTMIRRORUPDOWN)) || bVidForceFlip) {
334 
335 				DtoBltFx = (DDBLTFX*)malloc(sizeof(DDBLTFX));
336 				if (DtoBltFx == NULL) {
337 					vidExit();
338 					return 1;
339 				}
340 
341 				memset(DtoBltFx, 0, sizeof(DDBLTFX));
342 
343 				DtoBltFx->dwSize = sizeof(DDBLTFX);
344 				DtoBltFx->dwDDFX = DDBLTFX_MIRRORLEFTRIGHT | DDBLTFX_MIRRORUPDOWN;
345 
346 				// Enable flipping now
347 				nRotateGame |= 2;
348 			}
349 		}
350 	}
351 
352 	DtoPrim = NULL;							// No primary surface yet
353 	DtoBack = NULL;
354 
355 	// Remember the changes to the display
356 	if (nVidFullscreen) {
357 		if (VidSEnterFullscreenMode(nScreenSize, 0)) {
358 			vidExit();
359 			return 1;
360 		}
361 	} else {
362 		DtoDD->SetCooperativeLevel(hVidWnd, DDSCL_NORMAL);
363 	}
364 
365 #ifdef PRINT_DEBUG_INFO
366 	{
367 		// Display amount of free video memory
368 		DDSCAPS2 ddsCaps2;
369 		DWORD dwTotal;
370 		DWORD dwFree;
371 
372 		memset(&ddsCaps2, 0, sizeof(ddsCaps2));
373 		ddsCaps2.dwCaps = DDSCAPS_PRIMARYSURFACE;
374 
375 		dprintf(_T(" ** Starting DirectDraw7 blitter.\n"));
376 
377 		if (SUCCEEDED(DtoDD->GetAvailableVidMem(&ddsCaps2, &dwTotal, &dwFree))) {
378 			dprintf(_T("  * Initialising video: Total video memory minus display surface: %.2fMB.\n"), (double)dwTotal / (1024 * 1024));
379 		}
380 
381 		if (bDrvOkay) {
382 			if (BurnDrvGetFlags() & BDF_ORIENTATION_FLIPPED) {
383 				if (nRotateGame & 2) {
384 					dprintf(_T("  * Using graphics hardware to rotate the image 180 degrees.\n"));
385 				} else {
386 					dprintf(_T("  * Warning: Graphics hardware does not support mirroring blits.\n    Image orientation will be incorrect.\n"));
387 				}
388 			}
389 		}
390 	}
391 #endif
392 
393 	if (bVidTripleBuffer && nVidFullscreen) {
394 		if (DtoPrimInit(1)) {			// Try to make triple buffer
395 
396 #ifdef PRINT_DEBUG_INFO
397 			dprintf(_T("  * Warning: Couldn't allocate a triple-buffering surface.\n"));
398 #endif
399 
400 			// If we fail, fail entirely and make a normal buffer
401 			RELEASE(DtoPrim)
402 		}
403 	}
404 
405 	if (DtoPrim == NULL) {
406 		// No primary surface yet, so try normal
407 		if (DtoPrimInit(0)) {
408 #ifdef PRINT_DEBUG_INFO
409 	   	dprintf(_T("  * Error: Couldn't create primary surface.\n"));
410 #endif
411 
412 			vidExit();
413 			return 1;
414 		}
415 	}
416 
417 	if (nVidFullscreen) {
418 		DtoDD->Compact();
419 	}
420 
421 	VidSClipperInit(DtoPrim);
422 
423 	VidSSetupGamma(DtoPrim);
424 
425 	// Init the buffer surfaces
426 	if (DtosInit()) {
427 		vidExit();
428 		return 1;
429 	}
430 
431 #ifdef PRINT_DEBUG_INFO
432 	{
433 		DDSCAPS2 ddsCaps2;
434 		DWORD dwTotal;
435 		DWORD dwFree;
436 
437 		memset(&ddsCaps2, 0, sizeof(ddsCaps2));
438 		ddsCaps2.dwCaps = DDSCAPS_PRIMARYSURFACE;
439 
440 		if (SUCCEEDED(DtoDD->GetAvailableVidMem(&ddsCaps2, &dwTotal, &dwFree))) {
441 			dprintf(_T("  * Initialisation complete: %.2fMB video memory free.\n"), (double)dwFree / (1024 * 1024));
442 			dprintf(_T("    Displaying and rendering in %i-bit mode, emulation running in %i-bit mode.\n"), nVidScrnDepth, nVidImageDepth);
443 			if (nUseSys) {
444 				dprintf(_T("    Blitting directly from system memory.\n"));
445 			} else {
446 				dprintf(_T("    Transferring the image to video memory before blitting.\n"));
447 			}
448 			if (nVidFullscreen) {
449 				dprintf(_T("    Running in fullscreen mode (%i x %i)"), nVidScrnWidth, nVidScrnHeight);
450 				if (DtoBack != NULL) {
451 					dprintf(_T(", using a triple buffer"));
452 				}
453 				dprintf(_T(".\n"));
454 			} else {
455 				dprintf(_T("    Running in windowed mode.\n"));
456 			}
457 		}
458 	}
459 #endif
460 
461 	return 0;
462 }
463 
464 // Copy pVidImage to pddsDtos, rotate
vidRenderRotate(DDSURFACEDESC2 * ddsd)465 static int vidRenderRotate(DDSURFACEDESC2* ddsd)
466 {
467 	unsigned char *pd, *ps, *pdd;
468 	unsigned char *Surf;
469 	int nPitch;
470 
471 	Surf = (unsigned char*)ddsd->lpSurface;
472 	nPitch = ddsd->lPitch;
473 
474 	pd = Surf;
475 
476 	if (bDtosScan) {
477 		if (bVidScanHalf) {
478 			if (bVidScanRotate) {
479 				switch (nVidImageBPP) {
480 					case 4: {
481 						int t;
482 						for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
483 							ps = pVidImage + (nGameHeight - 1 - y) * 4;
484 							pdd = pd;
485 							for (int x = 0; x < nGameWidth; x++) {
486 								t = *(int*)ps;
487 								ps += nVidImagePitch;
488 								*(int*)pdd = t;
489 								pdd += 4;
490 								*(int*)pdd = (t & 0xFEFEFE) >> 1;
491 								pdd += 4;
492 							}
493 						}
494 						break;
495 					}
496 					case 3: {
497 						for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
498 							ps = pVidImage + (nGameHeight - 1 - y) * 3;
499 							pdd = pd;
500 							for (int x = 0; x < nGameWidth; x++) {
501 								pdd[0] = ps[0];
502 								pdd[1] = ps[1];
503 								pdd[2] = ps[2];
504 								pdd[3] = ps[0] >> 1;
505 								pdd[4] = ps[1] >> 1;
506 								pdd[5] = ps[2] >> 1;
507 								ps += nVidImagePitch;
508 								pdd +=6;
509 							}
510 						}
511 						break;
512 					}
513 					case 2: {
514 						short t;
515 						for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
516 							ps = pVidImage + (nGameHeight - 1 - y) * 2;
517 							pdd = pd;
518 							for (int x = 0; x < nGameWidth; x++) {
519 								t = *(short*)ps;
520 								ps += nVidImagePitch;
521 								*(short*)pdd = t;
522 								pdd += 2;
523 								*(short*)pdd = (t & nHalfMask) >> 1;
524 								pdd += 2;
525 							}
526 						}
527 						break;
528 					}
529 				}
530 
531 			} else {
532 				unsigned char* pBuffer = (unsigned char*)malloc(nPitch);
533 				unsigned char* pd2;
534 
535 				switch (nVidImageBPP) {
536 					case 4: {
537 						int t;
538 						for (int y = 0; y < nGameHeight; y++, pd += nPitch * 2) {
539 							ps = pVidImage + (nGameHeight - 1 - y) * 4;
540 							pdd = pd;
541 							pd2 = pBuffer;
542 							for (int x = 0; x < nGameWidth; x++) {
543 								t = *(int*)ps;
544 								ps += nVidImagePitch;
545 								*(int*)pdd = t;
546 								*(int*)pd2 = t;
547 								pdd += 4;
548 								pd2 += 4;
549 							}
550 							pdd = pd + nPitch;
551 							for (ps = pBuffer; ps < (pBuffer + nGameWidth * 4); ps += 4) {
552 								*(int*)pdd = (*(int*)ps & 0xFEFEFE) >> 1;
553 								pdd += 4;
554 							}
555 						}
556 						break;
557 					}
558 					case 3: {
559 						for (int y = 0; y < nGameHeight; y++, pd += nPitch * 2) {
560 							ps = pVidImage + (nGameHeight - 1 - y) * 3;
561 							pdd = pd;
562 							pd2 = pBuffer;
563 							for (int x = 0; x < nGameWidth; x++) {
564 								pdd[0] = ps[0];
565 								pd2[0] = ps[0];
566 								pdd[1] = ps[1];
567 								pd2[1] = ps[1];
568 								pdd[2] = ps[2];
569 								pd2[2] = ps[2];
570 								ps += nVidImagePitch;
571 								pdd += 3;
572 								pd2 += 3;
573 							}
574 							pdd = pd + nPitch;
575 							for (ps = pBuffer; ps < (pBuffer + nGameWidth * 3); ps += 3) {
576 								pdd[0] = ps[0] >> 1;
577 								pdd[1] = ps[1] >> 1;
578 								pdd[2] = ps[2] >> 1;
579 								pdd += 3;
580 							}
581 						}
582 						break;
583 					}
584 					case 2: {
585 						short t;
586 						for (int y = 0; y < nGameHeight; y++, pd += nPitch * 2) {
587 							ps = pVidImage + (nGameHeight - 1 - y) * 2;
588 							pdd = pd;
589 							pd2 = pBuffer;
590 							for (int x = 0; x < nGameWidth; x++) {
591 								t = *(short*)ps;
592 								ps += nVidImagePitch;
593 								*(short*)pdd = t;
594 								*(short*)pd2 = t;
595 								pdd += 2;
596 								pd2 += 2;
597 							}
598 							pdd = pd + nPitch;
599 							for (ps = pBuffer; ps < (pBuffer + nGameWidth * 2); ps += 4) {
600 								*(unsigned int*)pdd = (*(unsigned int*)ps & nHalfMask) >> 1;
601 								pdd += 4;
602 							}
603 						}
604 						break;
605 					}
606 				}
607 				if (pBuffer) {
608 					free (pBuffer);
609 					pBuffer = NULL;
610 				}
611 			}
612 		} else {
613 			int nPixelSize = nVidImageBPP;
614 			if (bVidScanRotate) {
615 				nPixelSize <<= 1;
616 			} else {
617 				nPitch <<= 1;
618 			}
619 
620 			switch (nVidImageBPP) {
621 				case 4: {
622 					for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
623 						ps = pVidImage + (nGameHeight - 1 - y) * 4;
624 						pdd = pd;
625 						for (int x = 0; x < nGameWidth; x++) {
626 							*(int*)pdd = *(int*)ps;
627 							ps += nVidImagePitch;
628 							pdd += nPixelSize;
629 						}
630 					}
631 					break;
632 				}
633 				case 3: {
634 					for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
635 						ps = pVidImage + (nGameHeight - 1 - y) * 3;
636 						pdd = pd;
637 						for (int x = 0; x < nGameWidth; x++) {
638 							pdd[0] = ps[0];
639 							pdd[1] = ps[1];
640 							pdd[2] = ps[2];
641 							ps += nVidImagePitch;
642 							pdd += nPixelSize;
643 						}
644 					}
645 					break;
646 				}
647 				case 2: {
648 					for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
649 						ps = pVidImage + (nGameHeight - 1 - y) * 2;
650 						pdd = pd;
651 						for (int x = 0; x < nGameWidth; x++) {
652 							*(short*)pdd = *(short*)ps;
653 							ps += nVidImagePitch;
654 							pdd += nPixelSize;
655 						}
656 					}
657 					break;
658 				}
659 			}
660 		}
661 	} else {
662 		switch (nVidImageBPP) {
663 			case 4: {
664 				for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
665 					ps = pVidImage + (nGameHeight - 1 - y) * 4;
666 					pdd = pd;
667 					for (int x = 0; x < nGameWidth; x++) {
668 						*(int*)pdd = *(int*)ps;
669 						ps += nVidImagePitch;
670 						pdd += 4;
671 					}
672 				}
673 				break;
674 			}
675 			case 3: {
676 				for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
677 					ps = pVidImage + (nGameHeight - 1 - y) * 3;
678 					pdd = pd;
679 					for (int x = 0; x < nGameWidth; x++) {
680 						pdd[0] = ps[0];
681 						pdd[1] = ps[1];
682 						pdd[2] = ps[2];
683 						ps += nVidImagePitch;
684 						pdd +=3;
685 					}
686 				}
687 				break;
688 			}
689 			case 2:	{
690 				for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
691 					ps = pVidImage + (nGameHeight - 1 - y) * 2;
692 					pdd = pd;
693 					for (int x = 0; x < nGameWidth; x++) {
694 						*(short*)pdd = *(short*)ps;
695 						ps += nVidImagePitch;
696 						pdd += 2;
697 					}
698 				}
699 				break;
700 			}
701 		}
702 	}
703 
704 	return 0;
705 }
706 
707 // Copy pVidImage to pddsDtos, don't rotate, add scanlines in odd lines if needed
vidRenderNoRotateHorScanlines(DDSURFACEDESC2 * ddsd,int nField,int nHalf)708 static int vidRenderNoRotateHorScanlines(DDSURFACEDESC2* ddsd, int nField, int nHalf)
709 {
710 	unsigned char *pd, *ps;
711 
712 	unsigned char* Surf = (unsigned char*)ddsd->lpSurface;
713 	int nPitch = ddsd->lPitch;
714 	if (bDtosScan) {
715 		if (nField) {			// copy to odd fields
716 			Surf += nPitch;
717 		}
718 		nPitch <<= 1;
719 	}
720 
721 	pd = Surf; ps = pVidImage;
722 	for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
723 		if (nHalf == 0) {
724 			memcpy(pd, ps, nVidImagePitch);
725 		} else {
726 			unsigned char* psEnd = ps + nVidImagePitch;
727 			unsigned char* pdp = pd;
728 			unsigned char* psp = ps;
729 
730 			do {
731 				unsigned int t;
732 				t = *((unsigned int *)(psp));
733 				t = (t & nHalfMask) >> 1;
734 				*((unsigned int *)(pdp)) = t;
735 
736 				psp += 4;
737 				pdp += 4;
738 			} while (psp < psEnd);
739 		}
740 	}
741 
742 	return 0;
743 }
744 
745 // Copy pVidImage to pddsDtos, don't rotate, add rotated scanlines
vidRenderNoRotateVertScanlines(DDSURFACEDESC2 * ddsd)746 static int vidRenderNoRotateVertScanlines(DDSURFACEDESC2* ddsd)
747 {
748 	unsigned char *pd, *ps;
749 	unsigned char* Surf = (unsigned char*)ddsd->lpSurface;
750 	int nPitch = ddsd->lPitch;
751 
752 	if (bVidScanHalf) {
753 		pd = Surf; ps = pVidImage;
754 		switch (nVidImageBPP) {
755 			case 4: {
756 				unsigned char* pdp;
757 				unsigned char* psp;
758 				unsigned char* psEnd;
759 				for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
760 					pdp = pd;
761 					psp = ps;
762 					psEnd = ps + nVidImagePitch;
763 					do {
764 						unsigned int t = *(unsigned int*)psp;
765 						*(unsigned int*)pdp = t;
766 						psp += 4;
767 						pdp += 4;
768 						*(unsigned int*)pdp = (t & 0xFEFEFE) >> 1;
769 						pdp += 4;
770 					} while (psp < psEnd);
771 				}
772 				break;
773 			}
774 			case 3: {
775 				unsigned char* pdp;
776 				unsigned char* psp;
777 				unsigned char* psEnd;
778 				for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
779 					pdp = pd;
780 					psp = ps;
781 					psEnd = ps + nVidImagePitch;
782 					do {
783 						pdp[0] = psp[0];
784 						pdp[1] = psp[1];
785 						pdp[2] = psp[2];
786 						pdp[3] = psp[0] >> 1;
787 						pdp[4] = psp[1] >> 1;
788 						pdp[5] = psp[2] >> 1;
789 						psp += 3;
790 						pdp += 6;
791 					} while (psp < psEnd);
792 				}
793 				break;
794 			}
795 			case 2: {
796 				unsigned char* pdp;
797 				unsigned char* psp;
798 				unsigned char* psEnd;
799 				unsigned short t;
800 				for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
801 					pdp = pd;
802 					psp = ps;
803 					psEnd = ps + nVidImagePitch;
804 					do {
805 						t = *(unsigned short*)psp;
806 						*(unsigned short*)pdp = t;
807 						psp += 2;
808 						pdp += 2;
809 						*(unsigned short*)pdp = (t & nHalfMask) >> 1;
810 						pdp += 2;
811 					} while (psp < psEnd);
812 				}
813 				break;
814 			}
815 		}
816 
817 	} else {
818 		pd = Surf; ps = pVidImage;
819 		switch (nVidImageBPP) {
820 			case 4: {
821 				unsigned char* pdp;
822 				unsigned char* psp;
823 				unsigned char* psEnd;
824 				for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
825 					pdp = pd;
826 					psp = ps;
827 					psEnd = ps + nVidImagePitch;
828 					do {
829 						*(unsigned int*)pdp = *(unsigned int*)psp;
830 
831 						psp += 4;
832 						pdp += 8;
833 					} while (psp < psEnd);
834 				}
835 				break;
836 			}
837 			case 3: {
838 				unsigned char* pdp;
839 				unsigned char* psp;
840 				unsigned char* psEnd;
841 				for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
842 					pdp = pd;
843 					psp = ps;
844 					psEnd = ps + nVidImagePitch;
845 					do {
846 						pdp[0] = psp[0];
847 						pdp[1] = psp[1];
848 						pdp[2] = psp[2];
849 
850 						psp += 3;
851 						pdp += 6;
852 					} while (psp < psEnd);
853 				}
854 				break;
855 			}
856 			case 2: {
857 				unsigned char* pdp;
858 				unsigned char* psp;
859 				unsigned char* psEnd;
860 				for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
861 					pdp = pd;
862 					psp = ps;
863 					psEnd = ps + nVidImagePitch;
864 					do {
865 						*(unsigned short*)pdp = *(unsigned short*)psp;
866 
867 						psp += 2;
868 						pdp += 4;
869 					} while (psp < psEnd);
870 				}
871 				break;
872 			}
873 		}
874 	}
875 
876 	return 0;
877 }
878 
vidBurnToSurf()879 static int vidBurnToSurf()
880 {
881 	DDSURFACEDESC2 ddsd;
882 
883 	if (pddsDtos == NULL) {
884 		return 1;
885 	}
886 
887 	if (DtoPrim->IsLost()) {														// We've lost control of the screen
888 		return 1;
889 	}
890 
891 	GetClientScreenRect(hVidWnd, &Dest);
892 
893 	if (!nVidFullscreen) {
894 		Dest.top += nMenuHeight;
895 	}
896 
897 	if (bVidArcaderes && nVidFullscreen) {
898 		Dest.left = (Dest.right + Dest.left) / 2;
899 		Dest.left -= nGameWidth / 2;
900 		Dest.right = Dest.left + nGameWidth;
901 
902 		Dest.top = (Dest.top + Dest.bottom) / 2;
903 		Dest.top -= nGameHeight / 2;
904 		Dest.bottom = Dest.top + nGameHeight;
905 	} else {
906 		VidSScaleImage(&Dest, nGameWidth, nGameHeight, bVidScanRotate);
907 	}
908 
909 	Src.right = nGameWidth;
910 	Src.bottom = nGameHeight;
911 	bool bScan = false;
912 
913 	if (bVidScanlines) {															// See if the display window is large enough to add scanlines if needed
914 		if (bRotateScanlines) {
915 			if (Dest.right - Dest.left >= (nGameWidth << 1)) {						// We need to add vertical scanlines
916 				Src.right <<= 1;
917 				bScan = true;
918 			}
919 		} else {
920 			if (Dest.bottom - Dest.top >= (nGameHeight << 1)) {						// We need to add horizontal scanlines
921 				Src.bottom <<= 1;
922 				bScan = true;
923 			}
924 		}
925 	}
926 
927 	// Lock the surface so we can write to it
928 	memset(&ddsd, 0, sizeof(ddsd));
929 	ddsd.dwSize = sizeof(ddsd);
930 
931 	if (FAILED(pddsDtos->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL))) {
932 		return 1;
933 	}
934 
935 	if (bScan && !bDtosScan) {														// Scanlines were just enabled. We need to clear the screen
936 		for (int y = 0; y < (nVidImageHeight << 1); y++) {
937 			memset(((unsigned char*)ddsd.lpSurface) + y * ddsd.lPitch, 0, ddsd.lPitch);
938 		}
939 	}
940 
941 	bDtosScan = bScan;
942 
943 	if (nRotateGame & 1) {
944 		vidRenderRotate(&ddsd);
945 	} else {
946 		if (bDtosScan && bRotateScanlines) {
947 			vidRenderNoRotateVertScanlines(&ddsd);
948 		} else {
949 			vidRenderNoRotateHorScanlines(&ddsd, 0, 0);
950 			if (bDtosScan && bVidScanHalf) {
951 				vidRenderNoRotateHorScanlines(&ddsd, 1, 1);
952 			}
953 		}
954 	}
955 
956 	pddsDtos->Unlock(NULL);
957 
958 	return 0;
959 }
960 
961 // Run one frame and render the screen
vidFrame(bool bRedraw)962 int vidFrame(bool bRedraw)			// bRedraw = 0
963 {
964 	if (pVidImage == NULL) {
965 		return 1;
966 	}
967 
968 	if (DtoPrim->IsLost()) {		// We've lost control of the screen
969 		if (VidSRestoreOSD()) {
970 			return 1;
971 		}
972 
973 		if (FAILED(DtoDD->RestoreAllSurfaces())) {
974 			return 1;
975 		}
976 
977 		DtoPrimClear();
978 	}
979 
980 	if (bDrvOkay) {
981 		if (bRedraw) {				// Redraw current frame
982 			if (BurnDrvRedraw()) {
983 				BurnDrvFrame();		// No redraw function provided, advance one frame
984 			}
985 		} else {
986 			BurnDrvFrame();			// Run one frame and draw the screen
987 		}
988 
989 		if ((BurnDrvGetFlags() & BDF_16BIT_ONLY) && pVidTransCallback)
990 			pVidTransCallback();
991 	}
992 
993 	vidBurnToSurf();
994 
995 	return 0;
996 }
997 
998 // Paint the Dtos surface onto the primary surface
vidPaint(int bValidate)999 static int vidPaint(int bValidate)
1000 {
1001 	if (DtoPrim == NULL || pddsDtos == NULL) {
1002 		return 1;
1003 	}
1004 
1005 	if (DtoPrim->IsLost()) {																	// We've lost control of the screen
1006 		return 1;
1007 	}
1008 
1009 	if (!nVidFullscreen) {																		// Check if the window has changed since we prepared the image
1010 		RECT rect = { 0, 0, 0, 0 };
1011 
1012 		GetClientScreenRect(hVidWnd, &rect);
1013 		rect.top += nMenuHeight;
1014 
1015 		VidSScaleImage(&rect, nGameWidth, nGameHeight, bVidScanRotate);
1016 
1017 		if (Dest.left != rect.left || Dest.right != rect.right || Dest.top != rect.top || Dest.bottom != rect.bottom) {
1018 			bValidate |= 2;
1019 		}
1020 	}
1021 
1022 	if (bValidate & 2) {
1023 		vidBurnToSurf();
1024 	}
1025 
1026 	DWORD dwBltFlags = 0;																		// See if we need to use blit effects
1027 	if (DtoBltFx) {
1028 		dwBltFlags |= DDBLT_DDFX;
1029 	}
1030 
1031 	if (bVidVSync && !nVidFullscreen) { DtoDD->WaitForVerticalBlank(DDWAITVB_BLOCKEND, NULL); }
1032 
1033 	if (DtoBack != NULL) {																		// Triple bufferring
1034 		if (FAILED(DtoBack->Blt(&Dest, pddsDtos, &Src, DDBLT_ASYNC | dwBltFlags, DtoBltFx))) {
1035 			if (FAILED(DtoBack->Blt(&Dest, pddsDtos, &Src, DDBLT_WAIT | dwBltFlags, DtoBltFx))) {
1036 				return 1;
1037 			}
1038 		}
1039 		VidSDisplayOSD(DtoBack, &Dest, 0);
1040 
1041 		DtoPrim->Flip(NULL, DDFLIP_WAIT);
1042 	} else {																					// Normal
1043 		RECT rect = { 0, 0, nGameWidth, nGameHeight };
1044 		int nFlags = 0;
1045 
1046 		if (bDtosScan) {
1047 			if (bRotateScanlines) {
1048 				nFlags |= 0x01;
1049 			} else {
1050 				nFlags |= 0x02;
1051 			}
1052 		}
1053 
1054 		// Display OSD text message
1055 		VidSDisplayOSD(pddsDtos, &rect, nFlags);
1056 
1057 		if (FAILED(DtoPrim->Blt(&Dest, pddsDtos, &Src, DDBLT_ASYNC | dwBltFlags, DtoBltFx))) {
1058 			if (FAILED(DtoPrim->Blt(&Dest, pddsDtos, &Src, DDBLT_WAIT | dwBltFlags, DtoBltFx))) {
1059 				return 1;
1060 			}
1061 		}
1062 /*
1063 		DWORD lpdwScanLine;
1064 		RECT window;
1065 		GetWindowRect(hVidWnd, &window);
1066 
1067 		while (1)
1068 		{
1069 			DtoDD->GetScanLine(&lpdwScanLine);
1070 			if (lpdwScanLine >= (unsigned int)window.bottom) {
1071 				break;
1072 			}
1073 			//Sleep(1);
1074 		}
1075 */
1076 	}
1077 
1078 	if (bValidate & 1) {
1079 		// Validate the rectangle we just drew
1080 		POINT c = {0, 0};
1081 		ClientToScreen(hVidWnd, &c);
1082 		Dest.left -= c.x; Dest.right -= c.x;
1083 		Dest.top -= c.y; Dest.bottom -= c.y;
1084 		ValidateRect(hVidWnd, &Dest);
1085 	}
1086 
1087 	return 0;
1088 }
1089 
vidScale(RECT * pRect,int nWidth,int nHeight)1090 static int vidScale(RECT* pRect, int nWidth, int nHeight)
1091 {
1092 	return VidSScaleImage(pRect, nWidth, nHeight, bVidScanRotate);
1093 }
1094 
vidGetSettings(InterfaceInfo * pInfo)1095 static int vidGetSettings(InterfaceInfo* pInfo)
1096 {
1097 	if (nVidFullscreen && DtoBack) {
1098 		IntInfoAddStringModule(pInfo, _T("Using a triple buffer"));
1099 	} else {
1100 		IntInfoAddStringModule(pInfo, _T("Using Blt() to transfer the image"));
1101 	}
1102 
1103 	if (nUseSys) {
1104 		IntInfoAddStringModule(pInfo, _T("Using system memory"));
1105 	} else {
1106 		IntInfoAddStringModule(pInfo, _T("Using video memory for the final blit"));
1107 	}
1108 
1109 	if (nRotateGame) {
1110 		TCHAR* pszEffect[8] = { _T(""), _T(""), _T(""), _T(""), _T(""), _T(""), _T(""), _T("") };
1111 		TCHAR szString[MAX_PATH] = _T("");
1112 
1113 		pszEffect[0] = _T("Using ");
1114 		if (nRotateGame & 1) {
1115 			pszEffect[1] = _T("software rotation");
1116 		}
1117 		if (nRotateGame & 2) {
1118 			if (nRotateGame & 1) {
1119 				pszEffect[2] = _T(" and");
1120 			}
1121 			pszEffect[3] = _T(" hardware mirroring");
1122 		}
1123 		pszEffect[4] = _T(", ");
1124 
1125 		_sntprintf(szString, MAX_PATH, _T("%s%s%s%s%s%s%s%s"), pszEffect[0], pszEffect[1], pszEffect[2], pszEffect[3], pszEffect[4], pszEffect[5], pszEffect[6], pszEffect[7]);
1126 		IntInfoAddStringModule(pInfo, szString);
1127 	}
1128 
1129 	if (bDtosScan) {
1130 		TCHAR* pszEffect[8] = { _T(""), _T(""), _T(""), _T(""), _T(""), _T(""), _T(""), _T("") };
1131 		TCHAR szString[MAX_PATH] = _T("");
1132 
1133 		pszEffect[0] = _T("Applying ");
1134 		if (bVidScanHalf) {
1135 			pszEffect[1] = _T("50% ");
1136 		}
1137 		if (bVidScanRotate) {
1138 			pszEffect[2] = _T("rotated ");
1139 		}
1140 		pszEffect[3] = _T("scanlines");
1141 
1142 		_sntprintf(szString, MAX_PATH, _T("%s%s%s%s%s%s%s%s"), pszEffect[0], pszEffect[1], pszEffect[2], pszEffect[3], pszEffect[4], pszEffect[5], pszEffect[6], pszEffect[7]);
1143 		IntInfoAddStringModule(pInfo, szString);
1144 	}
1145 
1146 	return 0;
1147 }
1148 
1149 // The video output plugin:
1150 struct VidOut VidOutDDraw = { vidInit, vidExit, vidFrame, vidPaint, vidScale, vidGetSettings, _T("DirectDraw7 video output") };
1151