2 Skelton for retropc emulator
4 Author : Takeda.Toshiya
13 #define RESULT_SUCCESS 1
15 #define RESULT_ERROR 3
17 unsigned __stdcall rec_video_thread(void *lpx);
20 static uint8 r0[2048], g0[2048], b0[2048], t0[2048];
21 static uint8 r1[2048], g1[2048], b1[2048];
24 void EMU::initialize_screen()
26 screen_width = SCREEN_WIDTH;
27 screen_height = SCREEN_HEIGHT;
28 screen_width_aspect = SCREEN_WIDTH_ASPECT;
29 screen_height_aspect = SCREEN_HEIGHT_ASPECT;
30 window_width = WINDOW_WIDTH;
31 window_height = WINDOW_HEIGHT;
32 screen_size_changed = true;
34 source_width = source_height = -1;
35 source_width_aspect = source_height_aspect = -1;
36 stretch_pow_x = stretch_pow_y = -1;
37 stretch_screen = false;
39 // create dib sections
40 HDC hdc = GetDC(main_window_handle);
41 create_dib_section(hdc, screen_width, screen_height, &hdcDib, &hBmp, &hOldBmp, &lpBuf, &lpBmp, &lpDib);
42 #ifdef USE_SCREEN_ROTATE
43 create_dib_section(hdc, screen_height, screen_width, &hdcDibRotate, &hBmpRotate, &hOldBmpRotate, &lpBufRotate, &lpBmpRotate, &lpDibRotate);
45 ReleaseDC(main_window_handle, hdc);
47 hdcDibStretch1 = hdcDibStretch2 = NULL;
48 hBmpStretch1 = hOldBmpStretch1 = hBmpStretch2 = hOldBmpStretch2 = NULL;
49 lpBufStretch1 = lpBufStretch2 = NULL;
55 lpd3d9OffscreenSurface = NULL;
57 render_to_d3d9Buffer = false;
58 use_d3d9 = config.use_d3d9;
59 wait_vsync = config.wait_vsync;
61 // initialize video recording
62 now_rec_video = false;
65 pAVICompressed = NULL;
68 // initialize update flags
69 first_draw_screen = false;
70 first_invalidate = self_invalidate = false;
73 // initialize crtc filter
74 memset(r1, 0, sizeof(r1));
75 memset(g1, 0, sizeof(g1));
76 memset(b1, 0, sizeof(b1));
80 #define release_dib_section(hdcdib, hbmp, holdbmp, lpbuf) { \
81 if(hdcdib != NULL && holdbmp != NULL) { \
82 SelectObject(hdcdib, holdbmp); \
92 if(hdcdib != NULL) { \
98 #define release_d3d9() { \
99 if(lpd3d9OffscreenSurface != NULL) { \
100 lpd3d9OffscreenSurface->Release(); \
101 lpd3d9OffscreenSurface = NULL; \
103 if(lpd3d9Surface != NULL) { \
104 lpd3d9Surface->Release(); \
105 lpd3d9Surface = NULL; \
107 if(lpd3d9Device != NULL) { \
108 lpd3d9Device->Release(); \
109 lpd3d9Device = NULL; \
111 if(lpd3d9 != NULL) { \
117 #define release_d3d9_surface() { \
118 if(lpd3d9OffscreenSurface != NULL) { \
119 lpd3d9OffscreenSurface->Release(); \
120 lpd3d9OffscreenSurface = NULL; \
122 if(lpd3d9Surface != NULL) { \
123 lpd3d9Surface->Release(); \
124 lpd3d9Surface = NULL; \
128 void EMU::release_screen()
130 // stop video recording
133 // release dib sections
134 release_dib_section(hdcDib, hBmp, hOldBmp, lpBuf);
135 #ifdef USE_SCREEN_ROTATE
136 release_dib_section(hdcDibRotate, hBmpRotate, hOldBmpRotate, lpBufRotate);
138 release_dib_section(hdcDibStretch1, hBmpStretch1, hOldBmpStretch1, lpBufStretch1);
139 release_dib_section(hdcDibStretch2, hBmpStretch2, hOldBmpStretch2, lpBufStretch2);
145 void EMU::create_dib_section(HDC hdc, int width, int height, HDC *hdcDib, HBITMAP *hBmp, HBITMAP *hOldBmp, LPBYTE *lpBuf, scrntype **lpBmp, LPBITMAPINFO *lpDib)
147 *hdcDib = CreateCompatibleDC(hdc);
148 *lpBuf = (LPBYTE)GlobalAlloc(GPTR, sizeof(BITMAPINFO));
149 *lpDib = (LPBITMAPINFO)(*lpBuf);
150 memset(&(*lpDib)->bmiHeader, 0, sizeof(BITMAPINFOHEADER));
151 (*lpDib)->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
152 (*lpDib)->bmiHeader.biWidth = width;
153 (*lpDib)->bmiHeader.biHeight = height;
154 (*lpDib)->bmiHeader.biPlanes = 1;
156 (*lpDib)->bmiHeader.biBitCount = 16;
157 (*lpDib)->bmiHeader.biCompression = BI_RGB;
158 (*lpDib)->bmiHeader.biSizeImage = width * height * 2;
159 #elif defined(_RGB565)
160 (*lpDib)->bmiHeader.biBitCount = 16;
161 (*lpDib)->bmiHeader.biCompression = BI_BITFIELDS;
162 LPDWORD lpBf = (LPDWORD)*lpDib->bmiColors;
163 lpBf[0] = 0x1f << 11;
166 (*lpDib)->bmiHeader.biSizeImage = width * height * 2;
167 #elif defined(_RGB888)
168 (*lpDib)->bmiHeader.biBitCount = 32;
169 (*lpDib)->bmiHeader.biCompression = BI_RGB;
170 (*lpDib)->bmiHeader.biSizeImage = width * height * 4;
172 (*lpDib)->bmiHeader.biXPelsPerMeter = 0;
173 (*lpDib)->bmiHeader.biYPelsPerMeter = 0;
174 (*lpDib)->bmiHeader.biClrUsed = 0;
175 (*lpDib)->bmiHeader.biClrImportant = 0;
176 *hBmp = CreateDIBSection(hdc, *lpDib, DIB_RGB_COLORS, (PVOID*)&(*lpBmp), NULL, 0);
177 *hOldBmp = (HBITMAP)SelectObject(*hdcDib, *hBmp);
180 int EMU::get_window_width(int mode)
182 #ifdef USE_SCREEN_ROTATE
183 if(config.monitor_type) {
184 return window_height + screen_height_aspect * mode;
187 return window_width + screen_width_aspect * mode;
190 int EMU::get_window_height(int mode)
192 #ifdef USE_SCREEN_ROTATE
193 if(config.monitor_type) {
194 return window_width + screen_width_aspect * mode;
197 return window_height + screen_height_aspect * mode;
200 void EMU::set_display_size(int width, int height, bool window_mode)
203 bool display_size_changed = false;
204 bool stretch_changed = false;
205 int prev_stretched_width = stretched_width;
206 int prev_stretched_height = stretched_height;
208 if(width != -1 && (display_width != width || display_height != height)) {
209 display_width = width;
210 display_height = height;
211 display_size_changed = stretch_changed = true;
213 if(use_d3d9 != config.use_d3d9) {
214 if(!(use_d3d9 = config.use_d3d9)) {
217 display_size_changed = stretch_changed = true;
219 if(wait_vsync != config.wait_vsync) {
220 wait_vsync = config.wait_vsync;
221 display_size_changed = stretch_changed = true;
224 // virtual machine renders to d3d9 buffer directly???
225 render_to_d3d9Buffer = use_d3d9;
227 #ifdef USE_SCREEN_ROTATE
228 if(config.monitor_type) {
229 hdcDibSource = hdcDibRotate;
230 lpBmpSource = lpBmpRotate;
231 lpDibSource = lpDibRotate;
232 pbmInfoHeader = &lpDibRotate->bmiHeader;
234 stretch_changed |= (source_width != screen_height);
235 stretch_changed |= (source_height != screen_width);
236 stretch_changed |= (source_width_aspect != screen_height_aspect);
237 stretch_changed |= (source_height_aspect != screen_width_aspect);
239 source_width = screen_height;
240 source_height = screen_width;
241 source_width_aspect = screen_height_aspect;
242 source_height_aspect = screen_width_aspect;
244 render_to_d3d9Buffer = false;
247 hdcDibSource = hdcDib;
250 pbmInfoHeader = &lpDib->bmiHeader;
252 stretch_changed |= (source_width != screen_width);
253 stretch_changed |= (source_height != screen_height);
254 stretch_changed |= (source_width_aspect != screen_width_aspect);
255 stretch_changed |= (source_height_aspect != screen_height_aspect);
257 source_width = screen_width;
258 source_height = screen_height;
259 source_width_aspect = screen_width_aspect;
260 source_height_aspect = screen_height_aspect;
261 #ifdef USE_SCREEN_ROTATE
265 if(config.stretch_type == 1 && !window_mode) {
266 // fit to full screen (aspect)
267 stretched_width = (display_height * source_width_aspect) / source_height_aspect;
268 stretched_height = display_height;
269 if(stretched_width > display_width) {
270 stretched_width = display_width;
271 stretched_height = (display_width * source_height_aspect) / source_width_aspect;
273 } else if(config.stretch_type == 2 && !window_mode) {
274 // fit to full screen (fill)
275 stretched_width = display_width;
276 stretched_height = display_height;
279 int tmp_pow_x = display_width / source_width_aspect;
280 int tmp_pow_y = display_height / source_height_aspect;
282 if(tmp_pow_y >= tmp_pow_x && tmp_pow_x > 1) {
284 } else if(tmp_pow_x >= tmp_pow_y && tmp_pow_y > 1) {
287 stretched_width = source_width_aspect * tmp_pow;
288 stretched_height = source_height_aspect * tmp_pow;
290 screen_dest_x = (display_width - stretched_width) / 2;
291 screen_dest_y = (display_height - stretched_height) / 2;
293 stretch_changed |= (prev_stretched_width != stretched_width);
294 stretch_changed |= (prev_stretched_height != stretched_height);
296 int new_pow_x = 1, new_pow_y = 1;
297 while(stretched_width > source_width * new_pow_x) {
300 while(stretched_height > source_height * new_pow_y) {
303 // if(!use_d3d9 && new_pow_x > 1 && new_pow_y > 1) {
304 // // support high quality stretch only for x1 window size in gdi mode
305 // new_pow_x = new_pow_y = 1;
307 if(stretch_pow_x != new_pow_x || stretch_pow_y != new_pow_y) {
308 stretch_pow_x = new_pow_x;
309 stretch_pow_y = new_pow_y;
310 stretch_changed = true;
312 if(stretch_pow_x != 1 || stretch_pow_y != 1) {
313 render_to_d3d9Buffer = false;
316 if(stretch_changed) {
317 release_dib_section(hdcDibStretch1, hBmpStretch1, hOldBmpStretch1, lpBufStretch1);
318 release_dib_section(hdcDibStretch2, hBmpStretch2, hOldBmpStretch2, lpBufStretch2);
319 stretch_screen = false;
321 if(stretch_pow_x != 1 || stretch_pow_y != 1) {
322 HDC hdc = GetDC(main_window_handle);
323 create_dib_section(hdc, source_width * stretch_pow_x, source_height * stretch_pow_y, &hdcDibStretch1, &hBmpStretch1, &hOldBmpStretch1, &lpBufStretch1, &lpBmpStretch1, &lpDibStretch1);
324 SetStretchBltMode(hdcDibStretch1, COLORONCOLOR);
326 create_dib_section(hdc, stretched_width, stretched_height, &hdcDibStretch2, &hBmpStretch2, &hOldBmpStretch2, &lpBufStretch2, &lpBmpStretch2, &lpDibStretch2);
327 SetStretchBltMode(hdcDibStretch2, HALFTONE);
329 ReleaseDC(main_window_handle, hdc);
330 stretch_screen = true;
333 if(use_d3d9 && display_size_changed) {
334 // release and initialize d3d9
337 if((lpd3d9 = Direct3DCreate9(D3D_SDK_VERSION)) == NULL) {
338 MessageBox(main_window_handle, _T("Failed to initialize Direct3D9"), _T(DEVICE_NAME), MB_OK | MB_ICONWARNING);
339 config.use_d3d9 = false;
342 // initialize present params
343 D3DPRESENT_PARAMETERS d3dpp;
344 ZeroMemory(&d3dpp, sizeof(d3dpp));
345 d3dpp.BackBufferWidth = display_width;
346 d3dpp.BackBufferHeight = display_height;
347 d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;//D3DFMT_UNKNOWN;
348 d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
349 d3dpp.hDeviceWindow = main_window_handle;
350 d3dpp.Windowed = TRUE;
351 d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
352 d3dpp.PresentationInterval = config.wait_vsync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
354 // create d3d9 device
355 HRESULT hr = lpd3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, main_window_handle, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &lpd3d9Device);
357 hr = lpd3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, main_window_handle, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &lpd3d9Device);
359 hr = lpd3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, main_window_handle, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &lpd3d9Device);
363 MessageBox(main_window_handle, _T("Failed to create a Direct3D9 device"), _T(DEVICE_NAME), MB_OK | MB_ICONWARNING);
364 config.use_d3d9 = false;
369 if(use_d3d9 && lpd3d9Device != NULL) {
370 // release and create d3d9 surfaces
371 release_d3d9_surface();
373 HRESULT hr = lpd3d9Device->CreateOffscreenPlainSurface(source_width * stretch_pow_x, source_height * stretch_pow_y, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &lpd3d9Surface, NULL);
375 hr = lpd3d9Device->CreateOffscreenPlainSurface(source_width * stretch_pow_x, source_height * stretch_pow_y, D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &lpd3d9OffscreenSurface, NULL);
378 lpd3d9Device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 0.0, 0);
380 MessageBox(main_window_handle, _T("Failed to create a Direct3D9 offscreen surface"), _T(DEVICE_NAME), MB_OK | MB_ICONWARNING);
381 config.use_d3d9 = false;
386 render_to_d3d9Buffer = false;
390 first_draw_screen = false;
391 first_invalidate = true;
392 screen_size_changed = false;
394 #ifdef USE_CRT_FILTER
395 memset(r1, 0, sizeof(r1));
396 memset(g1, 0, sizeof(g1));
397 memset(b1, 0, sizeof(b1));
401 void EMU::change_screen_size(int sw, int sh, int swa, int sha, int ww, int wh)
403 // virtual machine changes the screen size
404 if(screen_width != sw || screen_height != sh) {
407 screen_width_aspect = (swa != -1) ? swa : sw;
408 screen_height_aspect = (sha != -1) ? sha : sh;
411 screen_size_changed = true;
413 // re-create dib sections
414 HDC hdc = GetDC(main_window_handle);
415 release_dib_section(hdcDib, hBmp, hOldBmp, lpBuf);
416 create_dib_section(hdc, screen_width, screen_height, &hdcDib, &hBmp, &hOldBmp, &lpBuf, &lpBmp, &lpDib);
417 #ifdef USE_SCREEN_ROTATE
418 release_dib_section(hdcDibRotate, hBmpRotate, hOldBmpRotate, lpBufRotate);
419 create_dib_section(hdc, screen_height, screen_width, &hdcDibRotate, &hBmpRotate, &hOldBmpRotate, &lpBufRotate, &lpBmpRotate, &lpDibRotate);
421 ReleaseDC(main_window_handle, hdc);
429 // change the window size
430 PostMessage(main_window_handle, WM_RESIZE, 0L, 0L);
434 int EMU::draw_screen()
436 // don't draw screen before new screen size is applied to buffers
437 if(screen_size_changed) {
441 // check avi file recording timing
442 if(now_rec_video && rec_video_run_frames <= 0) {
446 // lock offscreen surface
447 D3DLOCKED_RECT pLockedRect;
448 if(use_d3d9 && lpd3d9OffscreenSurface != NULL && lpd3d9OffscreenSurface->LockRect(&pLockedRect, NULL, 0) == D3D_OK) {
449 lpd3d9Buffer = (scrntype *)pLockedRect.pBits;
457 // screen size was changed in vm->draw_screen()
458 if(screen_size_changed) {
459 // unlock offscreen surface
460 if(use_d3d9 && lpd3d9Buffer != NULL) {
462 lpd3d9OffscreenSurface->UnlockRect();
467 #ifdef USE_SCREEN_ROTATE
469 if(config.monitor_type) {
470 for(int y = 0; y < screen_height; y++) {
471 scrntype* src = lpBmp + screen_width * (screen_height - y - 1);
472 scrntype* out = lpBmpRotate + screen_height * (screen_width - 1) + (screen_height - y - 1);
473 for(int x = 0; x < screen_width; x++) {
475 out -= screen_height;
483 scrntype* src = lpBmpSource + source_width * (source_height - 1);
484 scrntype* out = lpBmpStretch1 + source_width * stretch_pow_x * (source_height * stretch_pow_y - 1);
485 int data_len = source_width * stretch_pow_x;
486 #ifdef USE_CRT_FILTER
487 #define _3_8(v) (((((v) * 3) >> 3) * 180) >> 8)
488 #define _5_8(v) (((((v) * 3) >> 3) * 180) >> 8)
489 #define _8_8(v) (((v) * 180) >> 8)
491 if(config.crt_filter && stretch_pow_x == 3 && stretch_pow_y == 3) {
492 r1[0] = g1[0] = b1[0] = r1[source_width + 1] = g1[source_width + 1] = b1[source_width + 1] = 0;
494 if(!screen_skip_line) {
495 for(int y = 0; y < source_height; y++) {
496 for(int x = 1; x <= source_width; x++) {
497 uint32 c = src[x - 1];
498 t0[x] = (c >> 24) & 0xff;
499 r0[x] = (c >> 16) & 0xff;
500 g0[x] = (c >> 8) & 0xff;
502 r1[x] = (c >> 19) & 0x1f;
503 g1[x] = (c >> 11) & 0x1f;
504 b1[x] = (c >> 3) & 0x1f;
506 scrntype* out1 = out;
508 scrntype* out2 = out;
510 scrntype* out3 = out;
512 for(int x = 1, xx = 0; x <= source_width; x++, xx += 3) {
513 uint32 r = r1[x - 1] + r0[x] + r1[x + 1];
514 uint32 g = g1[x - 1] + g0[x] + g1[x + 1];
515 uint32 b = b1[x - 1] + b0[x] + b1[x + 1];
516 out1[xx ] = out2[xx ] = (32 + _8_8(r)) << 16;
517 out1[xx + 1] = out2[xx + 1] = (32 + _8_8(g)) << 8;
518 out1[xx + 2] = out2[xx + 2] = (32 + _8_8(b));
520 out3[xx ] = (32 + _8_8(r)) << 16;
521 out3[xx + 1] = (32 + _8_8(g)) << 8;
522 out3[xx + 2] = (32 + _8_8(b));
524 out3[xx ] = (32 + _5_8(r)) << 16;
525 out3[xx + 1] = (32 + _5_8(g)) << 8;
526 out3[xx + 2] = (32 + _5_8(b));
532 for(int y = 0; y < source_height; y += 2) {
533 for(int x = 1; x <= source_width; x++) {
534 uint32 c = src[x - 1];
535 t0[x] = (c >> 24) & 0xff;
536 r0[x] = (c >> 16) & 0xff;
537 g0[x] = (c >> 8) & 0xff;
539 r1[x] = (c >> 20) & 0x0f;
540 g1[x] = (c >> 12) & 0x0f;
541 b1[x] = (c >> 4) & 0x0f;
543 scrntype* out1 = out;
545 scrntype* out2 = out;
547 scrntype* out3 = out;
549 scrntype* out4 = out;
551 scrntype* out5 = out;
553 scrntype* out6 = out;
555 for(int x = 1, xx = 0; x <= source_width; x++, xx += 3) {
556 uint32 r = r1[x - 1] + r0[x] + r1[x + 1];
557 uint32 g = g1[x - 1] + g0[x] + g1[x + 1];
558 uint32 b = b1[x - 1] + b0[x] + b1[x + 1];
559 out1[xx ] = out2[xx ] = out3[xx ] = out4[xx ] = (32 + _8_8(r)) << 16;
560 out1[xx + 1] = out2[xx + 1] = out3[xx + 1] = out4[xx + 1] = (32 + _8_8(g)) << 8;
561 out1[xx + 2] = out2[xx + 2] = out3[xx + 2] = out4[xx + 2] = (32 + _8_8(b));
563 out5[xx ] = out6[xx ] = (32 + _8_8(r)) << 16;
564 out5[xx + 1] = out6[xx + 1] = (32 + _8_8(g)) << 8;
565 out5[xx + 2] = out6[xx + 2] = (32 + _8_8(b));
567 out5[xx ] = out6[xx ] = (32 + _5_8(r)) << 16;
568 out5[xx + 1] = out6[xx + 1] = (32 + _5_8(g)) << 8;
569 out5[xx + 2] = out6[xx + 2] = (32 + _5_8(b));
572 src -= source_width * 2;
575 } else if(config.crt_filter && stretch_pow_x == 2 && stretch_pow_y == 2) {
576 if(!screen_skip_line) {
577 for(int y = 0; y < source_height; y++) {
578 for(int x = 1; x <= source_width; x++) {
579 uint32 c = src[x - 1];
580 t0[x] = (c >> 24) & 0xff;
581 r0[x] = (c >> 16) & 0xff;
582 g0[x] = (c >> 8) & 0xff;
584 r1[x] = (c >> 19) & 0x1f;
585 g1[x] = (c >> 11) & 0x1f;
586 b1[x] = (c >> 3) & 0x1f;
588 scrntype* out1 = out;
590 scrntype* out2 = out;
592 for(int x = 1, xx = 0; x <= source_width; x++, xx += 2) {
593 uint32 r = r1[x - 1] + r0[x] + r1[x + 1];
594 uint32 g = g1[x - 1] + g0[x] + g1[x + 1];
595 uint32 b = b1[x - 1] + b0[x] + b1[x + 1];
596 out1[xx ] = RGB_COLOR(32 + _8_8(r), 32 + _8_8(g), 32 + _8_8(b));
597 out1[xx + 1] = RGB_COLOR(16 + _5_8(r), 16 + _5_8(g), 16 + _5_8(b));
599 out2[xx ] = RGB_COLOR(32 + _8_8(r), 32 + _8_8(g), 32 + _8_8(b));
600 out2[xx + 1] = RGB_COLOR(16 + _5_8(r), 16 + _5_8(g), 16 + _5_8(b));
602 out2[xx ] = RGB_COLOR(32 + _3_8(r), 32 + _3_8(g), 32 + _3_8(b));
603 out2[xx + 1] = RGB_COLOR(16 + _3_8(r), 16 + _3_8(g), 16 + _3_8(b));
609 for(int y = 0; y < source_height; y += 2) {
610 for(int x = 1; x <= source_width; x++) {
611 uint32 c = src[x - 1];
612 t0[x] = (c >> 24) & 0xff;
613 r0[x] = (c >> 16) & 0xff;
614 g0[x] = (c >> 8) & 0xff;
616 r1[x] = (c >> 19) & 0x1f;
617 g1[x] = (c >> 11) & 0x1f;
618 b1[x] = (c >> 3) & 0x1f;
620 scrntype* out1 = out;
622 scrntype* out2 = out;
624 scrntype* out3 = out;
626 scrntype* out4 = out;
628 for(int x = 1, xx = 0; x <= source_width; x++, xx += 2) {
629 uint32 r = r1[x - 1] + r0[x] + r1[x + 1];
630 uint32 g = g1[x - 1] + g0[x] + g1[x + 1];
631 uint32 b = b1[x - 1] + b0[x] + b1[x + 1];
632 out1[xx ] = out2[xx ] = out3[xx ] = RGB_COLOR(32 + _8_8(r), 32 + _8_8(g), 32 + _8_8(b));
633 out1[xx + 1] = out2[xx + 1] = out3[xx + 1] = RGB_COLOR(16 + _5_8(r), 16 + _5_8(g), 16 + _5_8(b));
635 out4[xx ] = RGB_COLOR(32 + _8_8(r), 32 + _8_8(g), 32 + _8_8(b));
636 out4[xx + 1] = RGB_COLOR(16 + _5_8(r), 16 + _5_8(g), 16 + _5_8(b));
638 out4[xx ] = RGB_COLOR(32 + _3_8(r), 32 + _3_8(g), 32 + _3_8(b));
639 out4[xx + 1] = RGB_COLOR(16 + _3_8(r), 16 + _3_8(g), 16 + _3_8(b));
642 src -= source_width * 2;
647 for(int y = 0; y < source_height; y++) {
648 if(stretch_pow_x != 1) {
649 scrntype* out_tmp = out;
650 for(int x = 0; x < source_width; x++) {
652 for(int px = 0; px < stretch_pow_x; px++) {
655 out_tmp += stretch_pow_x;
658 // faster than memcpy()
659 for(int x = 0; x < source_width; x++) {
663 if(stretch_pow_y != 1) {
664 scrntype* src_tmp = out;
665 for(int py = 1; py < stretch_pow_y; py++) {
667 // about 10% faster than memcpy()
668 for(int x = 0; x < data_len; x++) {
677 StretchBlt(hdcDibStretch2, 0, 0, stretched_width, stretched_height, hdcDibStretch1, 0, 0, source_width * stretch_pow_x, source_height * stretch_pow_y, SRCCOPY);
680 first_draw_screen = true;
682 // copy bitmap to d3d9 offscreen surface
683 if(use_d3d9 && lpd3d9Buffer != NULL) {
684 if(!(render_to_d3d9Buffer && !now_rec_video)) {
685 scrntype *src = stretch_screen ? lpBmpStretch1 : lpBmpSource;
686 src += source_width * stretch_pow_x * (source_height * stretch_pow_y - 1);
687 scrntype *out = lpd3d9Buffer;
688 int data_len = source_width * stretch_pow_x;
690 for(int y = 0; y < source_height * stretch_pow_y; y++) {
691 for(int i = 0; i < data_len; i++) {
698 // unlock offscreen surface
700 lpd3d9OffscreenSurface->UnlockRect();
704 InvalidateRect(main_window_handle, NULL, first_invalidate);
705 UpdateWindow(main_window_handle);
706 self_invalidate = true;
710 static double frames = 0;
711 static int prev_video_fps = -1;
712 #ifdef SUPPORT_VARIABLE_TIMING
713 static double prev_vm_fps = -1;
714 double vm_fps = vm->frame_rate();
715 if(prev_video_fps != rec_video_fps || prev_vm_fps != vm_fps) {
716 prev_video_fps = rec_video_fps;
717 prev_vm_fps = vm_fps;
718 frames = vm_fps / rec_video_fps;
721 if(prev_video_fps != rec_video_fps) {
722 prev_video_fps = rec_video_fps;
723 frames = FRAMES_PER_SEC / rec_video_fps;
727 if(use_video_thread) {
728 while(rec_video_run_frames > 0) {
729 rec_video_run_frames -= frames;
730 rec_video_frames += frames;
734 if(hVideoThread != (HANDLE)0) {
735 if(video_thread_param.result == 0) {
736 WaitForSingleObject(hVideoThread, INFINITE);
738 hVideoThread = (HANDLE)0;
740 if(video_thread_param.result == RESULT_FULL) {
742 if(!start_rec_video(-1)) {
745 } else if(video_thread_param.result == RESULT_ERROR) {
750 BitBlt(hdcDibRec, 0, 0, source_width, source_height, hdcDibSource, 0, 0, SRCCOPY);
751 video_thread_param.frames += counter;
752 video_thread_param.result = 0;
753 if((hVideoThread = (HANDLE)_beginthreadex(NULL, 0, rec_video_thread, &video_thread_param, 0, NULL)) == (HANDLE)0) {
759 while(rec_video_run_frames > 0) {
761 if(AVIStreamWrite(pAVICompressed, lAVIFrames++, 1, (LPBYTE)lpBmpSource, pbmInfoHeader->biSizeImage, AVIIF_KEYFRAME, NULL, &lBytesWritten) == AVIERR_OK) {
762 // if avi file size > (2GB - 16MB), create new avi file
763 if((dwAVIFileSize += lBytesWritten) >= 2130706432) {
765 if(!start_rec_video(-1)) {
769 rec_video_run_frames -= frames;
770 rec_video_frames += frames;
784 scrntype* EMU::screen_buffer(int y)
786 if(use_d3d9 && lpd3d9Buffer != NULL && render_to_d3d9Buffer && !now_rec_video) {
787 return lpd3d9Buffer + screen_width * y;
789 return lpBmp + screen_width * (screen_height - y - 1);
792 void EMU::update_screen(HDC hdc)
795 if(first_invalidate || !self_invalidate) {
796 HDC hmdc = CreateCompatibleDC(hdc);
797 HBITMAP hBitmap = LoadBitmap(instance_handle, _T("IDI_BITMAP1"));
799 GetObject(hBitmap, sizeof(BITMAP), &bmp);
800 int w = (int)bmp.bmWidth;
801 int h = (int)bmp.bmHeight;
802 HBITMAP hOldBitmap = (HBITMAP)SelectObject(hmdc, hBitmap);
803 BitBlt(hdc, 0, 0, w, h, hmdc, 0, 0, SRCCOPY);
804 SelectObject(hmdc, hOldBitmap);
805 DeleteObject(hBitmap);
809 if(first_draw_screen) {
812 for(int i = 0; i < MAX_LEDS; i++) {
815 int w = leds[i].width;
816 int h = leds[i].height;
817 BitBlt(hdc, x, y, w, h, hdcDib, x, y, SRCCOPY);
820 #ifdef USE_ACCESS_LAMP
821 // get access lamps status of drives
822 int status = vm->access_lamp() & 7;
823 static int prev_status = 0;
824 bool render_in = (status != 0);
825 bool render_out = (prev_status != status);
826 prev_status = status;
828 COLORREF crColor = RGB((status & 1) ? 255 : 0, (status & 2) ? 255 : 0, (status & 4) ? 255 : 0);
829 int right_bottom_x = screen_dest_x + stretched_width;
830 int right_bottom_y = screen_dest_y + stretched_height;
834 LPDIRECT3DSURFACE9 lpd3d9BackSurface = NULL;
835 if(lpd3d9Device != NULL && lpd3d9Device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &lpd3d9BackSurface) == D3D_OK && lpd3d9BackSurface != NULL) {
836 RECT rectSrc = { 0, 0, source_width * stretch_pow_x, source_height * stretch_pow_y };
837 RECT rectDst = { screen_dest_x, screen_dest_y, screen_dest_x + stretched_width, screen_dest_y + stretched_height };
839 lpd3d9Device->UpdateSurface(lpd3d9OffscreenSurface, NULL, lpd3d9Surface, NULL);
840 lpd3d9Device->StretchRect(lpd3d9Surface, &rectSrc, lpd3d9BackSurface, &rectDst, stretch_screen ? D3DTEXF_LINEAR : D3DTEXF_POINT);
841 #ifdef USE_ACCESS_LAMP
843 if(render_in || render_out) {
845 for(int y = display_height - 6; y < display_height; y++) {
846 for(int x = display_width - 6; x < display_width; x++) {
847 if((x < right_bottom_x && y < right_bottom_y) ? render_in : render_out) {
848 if(hDC == 0 && lpd3d9BackSurface->GetDC(&hDC) != D3D_OK) {
851 SetPixelV(hDC, x, y, crColor);
857 lpd3d9BackSurface->ReleaseDC(hDC);
861 lpd3d9BackSurface->Release();
862 lpd3d9Device->Present(NULL, NULL, NULL, NULL);
866 BitBlt(hdc, screen_dest_x, screen_dest_y, stretched_width, stretched_height, hdcDibStretch2, 0, 0, SRCCOPY);
867 } else if(stretched_width == source_width && stretched_height == source_height) {
868 BitBlt(hdc, screen_dest_x, screen_dest_y, stretched_width, stretched_height, hdcDibSource, 0, 0, SRCCOPY);
870 StretchBlt(hdc, screen_dest_x, screen_dest_y, stretched_width, stretched_height, hdcDibSource, 0, 0, source_width, source_height, SRCCOPY);
872 #ifdef USE_ACCESS_LAMP
874 if(render_in || render_out) {
875 for(int y = display_height - 6; y < display_height; y++) {
876 for(int x = display_width - 6; x < display_width; x++) {
877 if((x < right_bottom_x && y < right_bottom_y) ? render_in : render_out) {
878 SetPixelV(hdc, x, y, crColor);
886 first_invalidate = self_invalidate = false;
890 void EMU::capture_screen()
892 if(use_d3d9 && render_to_d3d9Buffer && !now_rec_video) {
893 // virtual machine may render screen to d3d9 buffer directly...
899 GetLocalTime(&sTime);
901 _TCHAR file_name[_MAX_PATH];
902 _stprintf_s(file_name, _MAX_PATH, _T("%d-%0.2d-%0.2d_%0.2d-%0.2d-%0.2d.bmp"), sTime.wYear, sTime.wMonth, sTime.wDay, sTime.wHour, sTime.wMinute, sTime.wSecond);
905 BITMAPFILEHEADER bmFileHeader = { (WORD)(TEXT('B') | TEXT('M') << 8) };
906 bmFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
907 bmFileHeader.bfSize = bmFileHeader.bfOffBits + pbmInfoHeader->biSizeImage;
910 HANDLE hFile = CreateFile(bios_path(file_name), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
911 WriteFile(hFile, &bmFileHeader, sizeof(BITMAPFILEHEADER), &dwSize, NULL);
912 WriteFile(hFile, lpDibSource, sizeof(BITMAPINFOHEADER), &dwSize, NULL);
913 WriteFile(hFile, lpBmpSource, pbmInfoHeader->biSizeImage, &dwSize, NULL);
917 bool EMU::start_rec_video(int fps)
921 rec_video_run_frames = rec_video_frames = 0;
925 bool show_dialog = (fps > 0);
929 GetLocalTime(&sTime);
931 _stprintf_s(video_file_name, _MAX_PATH, _T("%d-%0.2d-%0.2d_%0.2d-%0.2d-%0.2d.avi"), sTime.wYear, sTime.wMonth, sTime.wDay, sTime.wHour, sTime.wMinute, sTime.wSecond);
935 if(AVIFileOpen(&pAVIFile, bios_path(video_file_name), OF_WRITE | OF_CREATE, NULL) != AVIERR_OK) {
938 use_video_thread = false;
941 AVISTREAMINFO strhdr;
942 memset(&strhdr, 0, sizeof(strhdr));
943 strhdr.fccType = streamtypeVIDEO; // vids
944 strhdr.fccHandler = 0;
947 strhdr.dwSuggestedBufferSize = pbmInfoHeader->biSizeImage;
948 SetRect(&strhdr.rcFrame, 0, 0, source_width, source_height);
949 if(AVIFileCreateStream(pAVIFile, &pAVIStream, &strhdr) != AVIERR_OK) {
955 AVICOMPRESSOPTIONS FAR * pOpts[1];
957 if(show_dialog && !AVISaveOptions(main_window_handle, ICMF_CHOOSE_KEYFRAME | ICMF_CHOOSE_DATARATE, 1, &pAVIStream, (LPAVICOMPRESSOPTIONS FAR *)&pOpts)) {
958 AVISaveOptionsFree(1, (LPAVICOMPRESSOPTIONS FAR *)&pOpts);
962 if(AVIMakeCompressedStream(&pAVICompressed, pAVIStream, &opts, NULL) != AVIERR_OK) {
966 if(AVIStreamSetFormat(pAVICompressed, 0, &lpDibSource->bmiHeader, lpDibSource->bmiHeader.biSize + lpDibSource->bmiHeader.biClrUsed * sizeof(RGBQUAD)) != AVIERR_OK) {
974 GetSystemInfo(&info);
976 if(info.dwNumberOfProcessors > 1) {
977 use_video_thread = true;
978 hVideoThread = (HANDLE)0;
979 video_thread_param.pAVICompressed = pAVICompressed;
980 video_thread_param.lpBmpSource = lpBmpSource;
981 video_thread_param.pbmInfoHeader = pbmInfoHeader;
982 video_thread_param.dwAVIFileSize = 0;
983 video_thread_param.lAVIFrames = 0;
984 video_thread_param.frames = 0;
985 video_thread_param.result = 0;
987 HDC hdc = GetDC(main_window_handle);
988 create_dib_section(hdc, source_width, source_height, &hdcDibRec, &hBmpRec, &hOldBmpRec, &lpBufRec, &lpBmpRec, &lpDibRec);
989 ReleaseDC(main_window_handle, hdc);
991 now_rec_video = true;
995 void EMU::stop_rec_video()
998 if(use_video_thread) {
999 if(hVideoThread != (HANDLE)0) {
1000 WaitForSingleObject(hVideoThread, INFINITE);
1001 hVideoThread = (HANDLE)0;
1004 release_dib_section(hdcDibRec, hBmpRec, hOldBmpRec, lpBufRec);
1010 AVIStreamClose(pAVIStream);
1012 if(pAVICompressed) {
1013 AVIStreamClose(pAVICompressed);
1016 AVIFileClose(pAVIFile);
1020 pAVICompressed = NULL;
1026 if(_tfopen_s(&fp, bios_path(video_file_name), _T("r+b")) == 0) {
1029 fseek(fp, 0xbc, SEEK_SET);
1030 if(ftell(fp) == 0xbc) {
1031 fread(buf, 4, 1, fp);
1032 fseek(fp, 0x70, SEEK_SET);
1033 fwrite(buf, 4, 1, fp);
1038 now_rec_video = false;
1041 void EMU::restart_rec_video()
1043 bool tmp = now_rec_video;
1045 if(tmp) start_rec_video(-1);
1048 unsigned __stdcall rec_video_thread(void *lpx)
1050 volatile video_thread_t *p = (video_thread_t *)lpx;
1052 int result = RESULT_SUCCESS;
1054 while(p->frames > 0) {
1055 if(AVIStreamWrite(p->pAVICompressed, p->lAVIFrames++, 1, (LPBYTE)p->lpBmpSource, p->pbmInfoHeader->biSizeImage, AVIIF_KEYFRAME, NULL, &lBytesWritten) == AVIERR_OK) {
1057 // if avi file size > (2GB - 16MB), create new avi file
1058 if((p->dwAVIFileSize += lBytesWritten) >= 2130706432) {
1059 result = RESULT_FULL;
1063 result = RESULT_ERROR;