Ticket #7931: windows.2.cpp

File windows.2.cpp, 13.7 KB (added by SF/mcleod2032, 22 years ago)

GDI Port with CD Audio Support

Line 
1/* ScummVM - Scumm Interpreter
2 * Copyright (C) 2001 Ludvig Strigeus
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * $Header: /cvsroot/scummvm/scummvm/windows.cpp,v 1.28 2002/02/26 18:00:00 mcleod2032 Exp $
19 */
20
21#include "stdafx.h"
22#include <assert.h>
23
24#include "scumm.h"
25#include "sound.h"
26#include "gui.h"
27
28#if !defined(ALLOW_GDI)
29#error The GDI driver is not as complete as the SDL driver. You need to define ALLOW_GDI to use this driver.
30#endif
31
32#define SRC_WIDTH 320
33#define SRC_HEIGHT 200
34#define SRC_PITCH 320
35
36#define DEST_WIDTH 640
37#define DEST_HEIGHT 400
38
39#define USE_DIRECTX 0
40#define USE_DRAWDIB 0
41#define USE_GDI 1
42
43#define SAMPLES_PER_SEC 22050
44#define BUFFER_SIZE 8192
45#define BITS_PER_SAMPLE 16
46
47#define WS_SCUMMWINDOW (WS_OVERLAPPEDWINDOW)
48#define WS_SCUMMFULLSCREEN (WS_POPUPWINDOW | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
49
50
51static bool shutdown;
52
53#if USE_GDI
54typedef struct DIB
55{
56 HBITMAP hSect;
57 byte *buf;
58 RGBQUAD *pal;
59 bool new_pal;
60} DIB;
61#endif
62
63class WndMan
64{
65 HMODULE hInst;
66 bool terminated;
67
68#if USE_GDI
69public:
70 DIB dib;
71private:
72#endif
73
74public:
75 HWND hWnd;
76 bool fscreen;
77
78 byte *_vgabuf;
79 Scumm *_scumm;
80
81 HANDLE _event;
82 DWORD _threadId;
83 HWAVEOUT _handle;
84 WAVEHDR _hdr[2];
85
86public:
87 void init();
88
89 bool handleMessage();
90 void run();
91 void setPalette(byte *ctab, int first, int num);
92 void writeToScreen();
93 void switchFullScreen(bool fs);
94
95 void prepare_header(WAVEHDR *wh, int i);
96 void sound_init();
97 static DWORD _stdcall sound_thread(WndMan *wm);
98
99#if USE_GDI
100 bool allocateDIB(int w, int h);
101#endif
102};
103
104
105void Error(const char *msg)
106{
107 OutputDebugString(msg);
108 MessageBoxA(0, msg, "Error", MB_ICONSTOP);
109 exit(1);
110}
111
112int sel;
113Scumm scumm;
114ScummDebugger debugger;
115Gui gui;
116SoundEngine sound;
117SOUND_DRIVER_TYPE snd_driv;
118
119WndMan wm[1];
120byte veryFastMode;
121
122void modifyslot(int sel, int what);
123
124int mapKey(int key)
125{
126 if (key>=VK_F1 && key<=VK_F9) return key - VK_F1 + 315;
127 return key;
128}
129
130static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
131{
132 WndMan *wm = (WndMan*)GetWindowLong(hWnd, GWL_USERDATA);
133 RECT r;
134
135 switch (message)
136 {
137 case WM_DESTROY:
138 case WM_CLOSE:
139 exit(0);
140 break;
141 case WM_KEYDOWN:
142 if (wParam>='0' && wParam<='9')
143 {
144 wm->_scumm->_saveLoadSlot = wParam - '0';
145 if (GetAsyncKeyState(VK_SHIFT)<0)
146 {
147 sprintf(wm->_scumm->_saveLoadName, "Quicksave %d", wm->_scumm->_saveLoadSlot);
148 wm->_scumm->_saveLoadFlag = 1;
149 }
150 else if (GetAsyncKeyState(VK_CONTROL)<0) wm->_scumm->_saveLoadFlag = 2;
151 wm->_scumm->_saveLoadCompatible = false;
152 }
153
154 if (GetAsyncKeyState(VK_CONTROL)<0)
155 {
156 if (wParam=='F') wm->_scumm->_fastMode ^= 1;
157 if (wParam=='G') veryFastMode ^= 1;
158 if (wParam=='D') debugger.attach(wm->_scumm);
159 if (wParam=='S') wm->_scumm->resourceStats();
160 }
161
162 if ((GetAsyncKeyState(VK_MENU)<0) && (wParam == VK_RETURN)) wm->switchFullScreen(!wm->fscreen);
163
164 wm->_scumm->_keyPressed = mapKey(wParam);
165 break;
166
167 case WM_MOUSEMOVE:
168 GetClientRect(hWnd, &r);
169 wm->_scumm->mouse.x = (SRC_WIDTH * ((int16*)&lParam)[0]) / (r.right - r.left);
170 wm->_scumm->mouse.y = (SRC_HEIGHT * ((int16*)&lParam)[1]) / (r.bottom - r.top);
171 break;
172 case WM_LBUTTONDOWN:
173 wm->_scumm->_leftBtnPressed |= msClicked|msDown;
174 break;
175 case WM_LBUTTONUP:
176 wm->_scumm->_leftBtnPressed &= ~msDown;
177 break;
178 case WM_RBUTTONDOWN:
179 wm->_scumm->_rightBtnPressed |= msClicked|msDown;
180 break;
181 case WM_RBUTTONUP:
182 wm->_scumm->_rightBtnPressed &= ~msDown;
183 break;
184
185 case WM_SYSCOMMAND:
186 switch (wParam)
187 {
188 case SC_MAXIMIZE:
189 wm->switchFullScreen(true);
190 break;
191 case SC_RESTORE:
192 wm->switchFullScreen(!wm->fscreen);
193 break;
194 default:
195 return DefWindowProc(hWnd, message, wParam, lParam);
196 }
197 break;
198
199 default:
200 return DefWindowProc(hWnd, message, wParam, lParam);
201 }
202 return 0;
203}
204
205#if USE_GDI
206
207bool WndMan::allocateDIB(int w, int h)
208{
209 struct
210 {
211 BITMAPINFOHEADER bih;
212 RGBQUAD rgb[256];
213 } d;
214
215 if (dib.hSect) return true;
216
217 memset(&d.bih, 0, sizeof(d.bih));
218 d.bih.biSize = sizeof(d.bih);
219 d.bih.biWidth = w;
220 d.bih.biHeight = -h;
221 d.bih.biPlanes = 1;
222 d.bih.biBitCount = 8;
223 d.bih.biCompression = BI_RGB;
224
225 memcpy(d.rgb, dib.pal, 256*sizeof(RGBQUAD));
226 dib.new_pal=false;
227
228 dib.hSect = CreateDIBSection(0, (BITMAPINFO*)&d, DIB_RGB_COLORS, (void**)&dib.buf, NULL, NULL);
229
230 return dib.hSect != NULL;
231}
232
233void WndMan::writeToScreen() {
234 RECT r;
235 HDC dc,bmpdc;
236 HBITMAP bmpOld;
237
238 if (_vgabuf) for (int y=0; y<200; y++) memcpy(dib.buf + y*320,_vgabuf + y*320, 320);
239
240 GetClientRect(hWnd, &r);
241 dc = GetDC(hWnd);
242
243 bmpdc = CreateCompatibleDC(dc);
244 bmpOld = (HBITMAP)SelectObject(bmpdc, dib.hSect);
245
246 if (dib.new_pal)
247 {
248 dib.new_pal = false;
249 SetDIBColorTable(bmpdc, 0, 256, dib.pal);
250 }
251
252 SetStretchBltMode(dc, BLACKONWHITE);
253
254 StretchBlt(dc, r.left, r.top, r.right-r.left, r.bottom-r.top, bmpdc, 0, 0, SRC_WIDTH, SRC_HEIGHT, SRCCOPY);
255
256 SelectObject(bmpdc, bmpOld);
257 DeleteDC(bmpdc);
258 ReleaseDC(hWnd, dc);
259}
260
261void WndMan::setPalette(byte *ctab, int first, int num)
262{
263 int i;
264
265 for (i=0; i<256; i++)
266 {
267 dib.pal[i].rgbRed = ctab[i*3+0];
268 dib.pal[i].rgbGreen = ctab[i*3+1];
269 dib.pal[i].rgbBlue = ctab[i*3+2];
270 }
271
272 dib.new_pal = true;
273}
274
275#endif
276
277HWND globWnd;
278
279void WndMan::init()
280{
281
282 /* Retrieve the handle of this module */
283 hInst = GetModuleHandle(NULL);
284
285 /* Register the window class */
286 WNDCLASSEX wcex;
287 wcex.cbSize = sizeof(WNDCLASSEX);
288 wcex.style = CS_HREDRAW | CS_VREDRAW;
289 wcex.lpfnWndProc = (WNDPROC)WndProc;
290 wcex.cbClsExtra = 0;
291 wcex.cbWndExtra = 0;
292 wcex.hInstance = hInst;
293 wcex.hIcon = 0;
294 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
295 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
296 wcex.lpszMenuName = 0;
297 wcex.lpszClassName = "ScummVM";
298 wcex.hIconSm = 0;
299 if (!RegisterClassEx(&wcex)) Error("Cannot register window class!");
300
301#if USE_GDI
302 globWnd = hWnd = CreateWindow("ScummVM", "ScummVM", WS_SCUMMWINDOW,
303 CW_USEDEFAULT, CW_USEDEFAULT, DEST_WIDTH+10, DEST_HEIGHT+30,
304 NULL, NULL, hInst, NULL);
305 SetWindowLong(hWnd, GWL_USERDATA, (long)this);
306
307 dib.pal = (RGBQUAD*)calloc(sizeof(RGBQUAD),256);
308 dib.new_pal = false;
309
310 if (!allocateDIB(SRC_WIDTH, SRC_HEIGHT)) Error("allocateDIB failed!");
311
312 ShowWindow(hWnd, SW_SHOW);
313// FreeConsole();
314#endif
315
316}
317
318
319bool WndMan::handleMessage()
320{
321 MSG msg;
322
323 if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) return false;
324
325 if (msg.message == WM_QUIT)
326 {
327 terminated=true;
328 exit(1);
329 return true;
330 }
331
332 TranslateMessage(&msg);
333 DispatchMessage(&msg);
334
335 return true;
336}
337
338void WndMan::switchFullScreen(bool fs)
339{
340 if (fs == TRUE)
341 {
342 SetWindowLong(wm->hWnd, GWL_STYLE, WS_SCUMMFULLSCREEN);
343 ShowWindow(wm->hWnd, SW_MAXIMIZE);
344 }
345 else
346 {
347 SetWindowLong(wm->hWnd, GWL_STYLE, WS_SCUMMWINDOW);
348 ShowWindow(wm->hWnd, SW_RESTORE);
349 MoveWindow(wm->hWnd, 0, 0, DEST_WIDTH+10, DEST_HEIGHT+30, TRUE);
350 }
351 wm->fscreen = fs;
352}
353
354
355unsigned long rdtsc_timer;
356
357void _declspec(naked) beginpentiumtest() {
358 _asm {
359 rdtsc
360 mov rdtsc_timer,eax
361 ret
362 }
363}
364
365int _declspec(naked) endpentiumtest() {
366 _asm {
367 rdtsc
368 sub eax,rdtsc_timer
369 ret
370 }
371}
372
373
374void decompressMask(byte *d, byte *s, int w=320, int h=144)
375{
376 int x,y;
377
378 for (y=0; y<h; y++) {
379 byte *p = s+y*40;
380 byte *pd = d + y*320;
381 byte bits = 0x80, bdata = *p++;
382 for (x=0; x<w; x++) {
383 *pd++ = (bdata & bits) ? 128 : 0;
384 bits>>=1;
385 if (!bits) {
386 bdata = *p++;
387 bits=0x80;
388 }
389 }
390 }
391}
392
393void outputlittlemask(byte *mask, int w, int h)
394{
395 byte *old = wm->_vgabuf;
396 wm->_vgabuf = NULL;
397 decompressMask(wm->dib.buf, mask, w, h);
398 wm->writeToScreen();
399 wm->_vgabuf = old;
400}
401
402void outputdisplay2(Scumm *s, int disp)
403{
404 byte *old = wm->_vgabuf;
405
406 byte buf[64000];
407
408 switch(disp)
409 {
410 case 0:
411 wm->_vgabuf = buf;
412 memcpy(buf, wm->_vgabuf, 64000);
413 memcpy(buf,s->getResourceAddress(rtBuffer, 5),320*200);
414 break;
415 case 1:
416 wm->_vgabuf = buf;
417 memcpy(buf, wm->_vgabuf, 64000);
418 memcpy(buf,s->getResourceAddress(rtBuffer, 1),320*200);
419 break;
420 case 2:
421 wm->_vgabuf = NULL;
422 decompressMask(wm->dib.buf, s->getResourceAddress(rtBuffer, 9)+s->_screenStartStrip);
423 break;
424 case 3:
425 wm->_vgabuf = NULL;
426 decompressMask(wm->dib.buf, s->getResourceAddress(rtBuffer, 9)+8160+s->_screenStartStrip);
427 break;
428 case 4:
429 wm->_vgabuf = NULL;
430 decompressMask(wm->dib.buf, s->getResourceAddress(rtBuffer, 9)+8160*2+s->_screenStartStrip);
431 break;
432 case 5:
433 wm->_vgabuf = NULL;
434 decompressMask(wm->dib.buf, s->getResourceAddress(rtBuffer, 9)+8160*3+s->_screenStartStrip);
435 break;
436 }
437 wm->writeToScreen();
438 wm->_vgabuf = old;
439}
440
441void blitToScreen(Scumm *s, byte *src,int x, int y, int w, int h)
442{
443 byte *dst;
444 RECT *r;
445 int i;
446
447 dst = (byte*)wm->_vgabuf + y*320 + x;
448
449 do
450 {
451 memcpy(dst, src, w);
452 dst += 320;
453 src += 320;
454 } while (--h);
455
456}
457
458void setShakePos(Scumm *s, int shake_pos) {}
459
460int clock;
461
462void updateScreen(Scumm *s)
463{
464 if (s->_palDirtyMax != -1)
465 {
466 wm->setPalette(s->_currentPalette, 0, 256);
467 s->_palDirtyMax = -1;
468 }
469 wm->writeToScreen();
470}
471
472void waitForTimer(Scumm *s, int delay)
473{
474 wm->handleMessage();
475 if (!veryFastMode)
476 {
477 assert(delay<5000);
478 if (s->_fastMode) delay=10;
479 Sleep(delay);
480 }
481}
482
483void initGraphics(Scumm *s, bool fullScreen)
484{
485 SetWindowText(wm->hWnd, s->getGameName());
486 wm->switchFullScreen(fullScreen);
487}
488
489
490void drawMouse(Scumm *s, int x, int y, int w, int h, byte *buf, bool visible) {}
491
492void fill_buffer(int16 *buf, int len) { scumm.mixWaves(buf, len); }
493
494void cd_playtrack(int track, int offset, int delay)
495{
496 UINT wDeviceID;
497 DWORD dwReturn;
498 MCI_OPEN_PARMS mciOpenParms;
499 MCI_SET_PARMS mciSetParms;
500 MCI_PLAY_PARMS mciPlayParms;
501 int start, end;
502
503 start = (offset / 24);
504 end = (start + delay / 24);
505
506 mciOpenParms.lpstrDeviceType = "cdaudio";
507 if (dwReturn = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE, (DWORD)(LPVOID) &mciOpenParms)) return;
508
509 wDeviceID = mciOpenParms.wDeviceID;
510 mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF;
511 if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID) &mciSetParms))
512 {
513 mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
514 return;
515 }
516
517 mciPlayParms.dwFrom = MCI_MAKE_TMSF(track, start / 60, start % 60, 0);
518 mciPlayParms.dwTo = MCI_MAKE_TMSF(track, end / 60, end % 60, 0);
519
520 if (dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_FROM | MCI_TO | MCI_NOTIFY | MCI_TRACK, (DWORD)(LPVOID) &mciPlayParms))
521 mciSendCommand(wDeviceID, MCI_CLOSE, 0, NULL);
522}
523
524void WndMan::prepare_header(WAVEHDR *wh, int i)
525{
526 memset(wh, 0, sizeof(WAVEHDR));
527 wh->lpData = (char*)malloc(BUFFER_SIZE);
528 wh->dwBufferLength = BUFFER_SIZE;
529
530 waveOutPrepareHeader(_handle, wh, sizeof(WAVEHDR));
531
532 fill_buffer((int16*)wh->lpData, wh->dwBufferLength>>1);
533 waveOutWrite(_handle, wh, sizeof(WAVEHDR));
534}
535
536void WndMan::sound_init()
537{
538 WAVEFORMATEX wfx;
539
540 memset(&wfx, 0, sizeof(wfx));
541 wfx.wFormatTag = WAVE_FORMAT_PCM;
542 wfx.nChannels = 1;
543 wfx.nSamplesPerSec = SAMPLES_PER_SEC;
544 wfx.nAvgBytesPerSec = SAMPLES_PER_SEC * BITS_PER_SAMPLE / 8;
545 wfx.wBitsPerSample = BITS_PER_SAMPLE;
546 wfx.nBlockAlign = BITS_PER_SAMPLE * 1 / 8;
547
548 CreateThread(NULL, 0, (unsigned long (__stdcall *)(void *))&sound_thread, this, 0, &_threadId);
549 SetThreadPriority((void*)_threadId, THREAD_PRIORITY_HIGHEST);
550
551 _event = CreateEvent(NULL, false, false, NULL);
552
553 memset(_hdr,0,sizeof(_hdr));
554
555 waveOutOpen(&_handle, WAVE_MAPPER, &wfx, (long)_event, (long)this, CALLBACK_EVENT);
556
557 prepare_header(&_hdr[0], 0);
558 prepare_header(&_hdr[1], 1);
559}
560
561DWORD _stdcall WndMan::sound_thread(WndMan *wm)
562{
563 int i;
564 bool signaled;
565 int time = GetTickCount(), cur;
566
567 while (1)
568 {
569
570 if (!snd_driv.wave_based())
571 {
572 cur = GetTickCount();
573 while (time < cur)
574 {
575 sound.on_timer();
576 time += 10;
577 }
578 }
579
580 signaled = WaitForSingleObject(wm->_event, time - cur) == WAIT_OBJECT_0;
581
582 if (signaled)
583 {
584 for(i=0; i<2; i++)
585 {
586 WAVEHDR *hdr = &wm->_hdr[i];
587 if (hdr->dwFlags & WHDR_DONE)
588 {
589 fill_buffer((int16*)hdr->lpData, hdr->dwBufferLength>>1);
590 waveOutWrite(wm->_handle, hdr, sizeof(WAVEHDR));
591 }
592 }
593 }
594 }
595}
596
597
598#undef main
599int main(int argc, char* argv[])
600{
601 int delta;
602
603 wm->init();
604 wm->_vgabuf = (byte*)calloc(320,200);
605 wm->_scumm = &scumm;
606
607 sound.initialize(&scumm,&snd_driv);
608
609 wm->sound_init();
610
611 scumm._gui = &gui;
612 scumm.scummMain(argc, argv);
613
614 if (!(scumm._features & GF_SMALL_HEADER)) gui.init(&scumm);
615
616 delta = 0;
617
618 while(1)
619 {
620 updateScreen(&scumm);
621 waitForTimer(&scumm, delta*15);
622
623 if (gui._active)
624 {
625 gui.loop();
626 delta = 3;
627 }
628 else delta = scumm.scummLoop(delta);
629 };
630
631 return 0;
632}
633
634void BoxTest(int num) {;} // Test code
635
636