Ticket #7931: windows.cpp

File windows.cpp, 12.6 KB (added by SF/mcleod2032, 22 years ago)
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.25 2002/02/06 18:12:30 mcleod 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
50static bool shutdown;
51
52#if USE_GDI
53typedef struct DIB
54{
55 HBITMAP hSect;
56 byte *buf;
57 RGBQUAD *pal;
58 bool new_pal;
59} DIB;
60#endif
61
62class WndMan
63{
64 HMODULE hInst;
65 bool terminated;
66
67#if USE_GDI
68public:
69 DIB dib;
70private:
71#endif
72
73public:
74 HWND hWnd;
75 bool fscreen;
76
77 byte *_vgabuf;
78
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 if (key>=VK_F1 && key<=VK_F9) {
126 return key - VK_F1 + 315;
127 }
128 return key;
129}
130
131static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
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 if (wParam=='F') {
156 wm->_scumm->_fastMode ^= 1;
157 }
158
159 if (wParam=='G') {
160 veryFastMode ^= 1;
161 }
162
163 if (wParam=='D') {
164 debugger.attach(wm->_scumm);
165 }
166
167 if (wParam=='S') {
168 wm->_scumm->resourceStats();
169 }
170 }
171
172 if ((GetAsyncKeyState(VK_MENU)<0) && (wParam == VK_RETURN))
173 {
174 wm->switchFullScreen(!wm->fscreen);
175 }
176
177 wm->_scumm->_keyPressed = mapKey(wParam);
178 break;
179
180 case WM_MOUSEMOVE:
181 GetClientRect(hWnd, &r);
182 wm->_scumm->mouse.x = (SRC_WIDTH * ((int16*)&lParam)[0]) / (r.right - r.left);
183 wm->_scumm->mouse.y = (SRC_HEIGHT * ((int16*)&lParam)[1]) / (r.bottom - r.top);
184 break;
185 case WM_LBUTTONDOWN:
186 wm->_scumm->_leftBtnPressed |= msClicked|msDown;
187 break;
188 case WM_LBUTTONUP:
189 wm->_scumm->_leftBtnPressed &= ~msDown;
190 break;
191 case WM_RBUTTONDOWN:
192 wm->_scumm->_rightBtnPressed |= msClicked|msDown;
193 break;
194 case WM_RBUTTONUP:
195 wm->_scumm->_rightBtnPressed &= ~msDown;
196 break;
197
198 case WM_SYSCOMMAND:
199
200 switch (wParam)
201 {
202 case SC_MAXIMIZE:
203 wm->switchFullScreen(true);
204 break;
205 case SC_RESTORE:
206 wm->switchFullScreen(!wm->fscreen);
207 break;
208 default:
209 return DefWindowProc(hWnd, message, wParam, lParam);
210 }
211 break;
212
213 default:
214 return DefWindowProc(hWnd, message, wParam, lParam);
215 }
216 return 0;
217}
218
219#if USE_GDI
220
221bool WndMan::allocateDIB(int w, int h)
222{
223 struct
224 {
225 BITMAPINFOHEADER bih;
226 RGBQUAD rgb[256];
227 } d;
228
229 if (dib.hSect) return true;
230
231 memset(&d.bih, 0, sizeof(d.bih));
232 d.bih.biSize = sizeof(d.bih);
233 d.bih.biWidth = w;
234 d.bih.biHeight = -h;
235 d.bih.biPlanes = 1;
236 d.bih.biBitCount = 8;
237 d.bih.biCompression = BI_RGB;
238
239 memcpy(d.rgb, dib.pal, 256*sizeof(RGBQUAD));
240 dib.new_pal=false;
241
242 dib.hSect = CreateDIBSection(0, (BITMAPINFO*)&d, DIB_RGB_COLORS, (void**)&dib.buf, NULL, NULL);
243
244 return dib.hSect != NULL;
245}
246
247void WndMan::writeToScreen() {
248 RECT r;
249 HDC dc,bmpdc;
250 HBITMAP bmpOld;
251
252 if (_vgabuf) for (int y=0; y<200; y++) memcpy(dib.buf + y*320,_vgabuf + y*320, 320);
253
254 GetClientRect(hWnd, &r);
255 dc = GetDC(hWnd);
256
257 bmpdc = CreateCompatibleDC(dc);
258 bmpOld = (HBITMAP)SelectObject(bmpdc, dib.hSect);
259
260 if (dib.new_pal)
261 {
262 dib.new_pal = false;
263 SetDIBColorTable(bmpdc, 0, 256, dib.pal);
264 }
265
266 SetStretchBltMode(dc, BLACKONWHITE);
267
268 StretchBlt(dc, r.left, r.top, r.right-r.left, r.bottom-r.top, bmpdc, 0, 0, SRC_WIDTH, SRC_HEIGHT, SRCCOPY);
269
270 SelectObject(bmpdc, bmpOld);
271 DeleteDC(bmpdc);
272 ReleaseDC(hWnd, dc);
273}
274
275void WndMan::setPalette(byte *ctab, int first, int num) {
276 int i;
277
278 for (i=0; i<256; i++)
279 {
280 dib.pal[i].rgbRed = ctab[i*3+0];
281 dib.pal[i].rgbGreen = ctab[i*3+1];
282 dib.pal[i].rgbBlue = ctab[i*3+2];
283 }
284
285 dib.new_pal = true;
286}
287
288#endif
289
290HWND globWnd;
291
292void WndMan::init() {
293
294 /* Retrieve the handle of this module */
295 hInst = GetModuleHandle(NULL);
296
297 /* Register the window class */
298 WNDCLASSEX wcex;
299 wcex.cbSize = sizeof(WNDCLASSEX);
300 wcex.style = CS_HREDRAW | CS_VREDRAW;
301 wcex.lpfnWndProc = (WNDPROC)WndProc;
302 wcex.cbClsExtra = 0;
303 wcex.cbWndExtra = 0;
304 wcex.hInstance = hInst;
305 wcex.hIcon = 0;
306 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
307 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
308 wcex.lpszMenuName = 0;
309 wcex.lpszClassName = "ScummVM";
310 wcex.hIconSm = 0;
311 if (!RegisterClassEx(&wcex)) Error("Cannot register window class!");
312
313#if USE_GDI
314 globWnd = hWnd = CreateWindow("ScummVM", "ScummVM", WS_SCUMMWINDOW,
315 CW_USEDEFAULT, CW_USEDEFAULT, DEST_WIDTH+10, DEST_HEIGHT+30,
316 NULL, NULL, hInst, NULL);
317 SetWindowLong(hWnd, GWL_USERDATA, (long)this);
318
319 dib.pal = (RGBQUAD*)calloc(sizeof(RGBQUAD),256);
320 dib.new_pal = false;
321
322 if (!allocateDIB(SRC_WIDTH, SRC_HEIGHT)) Error("allocateDIB failed!");
323
324 ShowWindow(hWnd, SW_SHOW);
325#endif
326
327}
328
329
330bool WndMan::handleMessage() {
331 MSG msg;
332
333 if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) return false;
334
335 if (msg.message == WM_QUIT)
336 {
337 terminated=true;
338 exit(1);
339 return true;
340 }
341
342 TranslateMessage(&msg);
343 DispatchMessage(&msg);
344
345 return true;
346}
347
348void WndMan::switchFullScreen(bool fs)
349{
350 if (fs == TRUE)
351 {
352 SetWindowLong(wm->hWnd, GWL_STYLE, WS_SCUMMFULLSCREEN);
353 ShowWindow(wm->hWnd, SW_MAXIMIZE);
354 }
355 else
356 {
357 SetWindowLong(wm->hWnd, GWL_STYLE, WS_SCUMMWINDOW);
358 ShowWindow(wm->hWnd, SW_RESTORE);
359 MoveWindow(wm->hWnd, 0, 0, DEST_WIDTH+10, DEST_HEIGHT+30, TRUE);
360 }
361 wm->fscreen = fs;
362}
363
364
365unsigned long rdtsc_timer;
366
367void _declspec(naked) beginpentiumtest() {
368 _asm {
369 rdtsc
370 mov rdtsc_timer,eax
371 ret
372 }
373}
374
375int _declspec(naked) endpentiumtest() {
376 _asm {
377 rdtsc
378 sub eax,rdtsc_timer
379 ret
380 }
381}
382
383
384void decompressMask(byte *d, byte *s, int w=320, int h=144)
385{
386 int x,y;
387
388 for (y=0; y<h; y++) {
389 byte *p = s+y*40;
390 byte *pd = d + y*320;
391 byte bits = 0x80, bdata = *p++;
392 for (x=0; x<w; x++) {
393 *pd++ = (bdata & bits) ? 128 : 0;
394 bits>>=1;
395 if (!bits) {
396 bdata = *p++;
397 bits=0x80;
398 }
399 }
400 }
401}
402
403void outputlittlemask(byte *mask, int w, int h)
404{
405 byte *old = wm->_vgabuf;
406 wm->_vgabuf = NULL;
407 decompressMask(wm->dib.buf, mask, w, h);
408 wm->writeToScreen();
409 wm->_vgabuf = old;
410}
411
412void outputdisplay2(Scumm *s, int disp)
413{
414 byte *old = wm->_vgabuf;
415
416 byte buf[64000];
417
418 switch(disp)
419 {
420 case 0:
421 wm->_vgabuf = buf;
422 memcpy(buf, wm->_vgabuf, 64000);
423 memcpy(buf,s->getResourceAddress(rtBuffer, 5),320*200);
424 break;
425 case 1:
426 wm->_vgabuf = buf;
427 memcpy(buf, wm->_vgabuf, 64000);
428 memcpy(buf,s->getResourceAddress(rtBuffer, 1),320*200);
429 break;
430 case 2:
431 wm->_vgabuf = NULL;
432 decompressMask(wm->dib.buf, s->getResourceAddress(rtBuffer, 9)+s->_screenStartStrip);
433 break;
434 case 3:
435 wm->_vgabuf = NULL;
436 decompressMask(wm->dib.buf, s->getResourceAddress(rtBuffer, 9)+8160+s->_screenStartStrip);
437 break;
438 case 4:
439 wm->_vgabuf = NULL;
440 decompressMask(wm->dib.buf, s->getResourceAddress(rtBuffer, 9)+8160*2+s->_screenStartStrip);
441 break;
442 case 5:
443 wm->_vgabuf = NULL;
444 decompressMask(wm->dib.buf, s->getResourceAddress(rtBuffer, 9)+8160*3+s->_screenStartStrip);
445 break;
446 }
447 wm->writeToScreen();
448 wm->_vgabuf = old;
449}
450
451void blitToScreen(Scumm *s, byte *src,int x, int y, int w, int h)
452{
453 byte *dst;
454 RECT *r;
455 int i;
456
457 dst = (byte*)wm->_vgabuf + y*320 + x;
458
459 do
460 {
461 memcpy(dst, src, w);
462 dst += 320;
463 src += 320;
464 } while (--h);
465
466}
467
468void setShakePos(Scumm *s, int shake_pos) {}
469
470
471int clock;
472
473void updateScreen(Scumm *s)
474{
475 if (s->_palDirtyMax != -1) {
476 wm->setPalette(s->_currentPalette, 0, 256);
477 s->_palDirtyMax = -1;
478 }
479
480 wm->writeToScreen();
481}
482
483void waitForTimer(Scumm *s, int delay)
484{
485 wm->handleMessage();
486 if (!veryFastMode)
487 {
488 assert(delay<5000);
489 if (s->_fastMode) delay=10;
490 Sleep(delay);
491 }
492}
493
494void initGraphics(Scumm *s, bool fullScreen)
495{
496 SetWindowText(wm->hWnd, s->getGameName());
497 wm->switchFullScreen(fullScreen);
498}
499
500
501void drawMouse(Scumm *s, int x, int y, int w, int h, byte *buf, bool visible)
502{
503}
504
505void fill_buffer(int16 *buf, int len) {
506 scumm.mixWaves(buf, len);
507}
508
509void WndMan::prepare_header(WAVEHDR *wh, int i) {
510 memset(wh, 0, sizeof(WAVEHDR));
511 wh->lpData = (char*)malloc(BUFFER_SIZE);
512 wh->dwBufferLength = BUFFER_SIZE;
513
514 waveOutPrepareHeader(_handle, wh, sizeof(WAVEHDR));
515
516 fill_buffer((int16*)wh->lpData, wh->dwBufferLength>>1);
517 waveOutWrite(_handle, wh, sizeof(WAVEHDR));
518}
519
520void WndMan::sound_init() {
521 WAVEFORMATEX wfx;
522
523 memset(&wfx, 0, sizeof(wfx));
524 wfx.wFormatTag = WAVE_FORMAT_PCM;
525 wfx.nChannels = 1;
526 wfx.nSamplesPerSec = SAMPLES_PER_SEC;
527 wfx.nAvgBytesPerSec = SAMPLES_PER_SEC * BITS_PER_SAMPLE / 8;
528 wfx.wBitsPerSample = BITS_PER_SAMPLE;
529 wfx.nBlockAlign = BITS_PER_SAMPLE * 1 / 8;
530
531 CreateThread(NULL, 0, (unsigned long (__stdcall *)(void *))&sound_thread, this, 0, &_threadId);
532 SetThreadPriority((void*)_threadId, THREAD_PRIORITY_HIGHEST);
533
534 _event = CreateEvent(NULL, false, false, NULL);
535
536 memset(_hdr,0,sizeof(_hdr));
537
538 waveOutOpen(&_handle, WAVE_MAPPER, &wfx, (long)_event, (long)this, CALLBACK_EVENT );
539
540 prepare_header(&_hdr[0], 0);
541 prepare_header(&_hdr[1], 1);
542}
543
544DWORD _stdcall WndMan::sound_thread(WndMan *wm) {
545 int i;
546 bool signaled;
547 int time = GetTickCount(), cur;
548
549 while (1) {
550
551 if (!snd_driv.wave_based()) {
552 cur = GetTickCount();
553 while (time < cur) {
554 sound.on_timer();
555 time += 10;
556 }
557 }
558
559 signaled = WaitForSingleObject(wm->_event, time - cur) == WAIT_OBJECT_0;
560
561 if (signaled) {
562 for(i=0; i<2; i++) {
563 WAVEHDR *hdr = &wm->_hdr[i];
564 if (hdr->dwFlags & WHDR_DONE) {
565 fill_buffer((int16*)hdr->lpData, hdr->dwBufferLength>>1);
566 waveOutWrite(wm->_handle, hdr, sizeof(WAVEHDR));
567 }
568 }
569 }
570 }
571}
572
573
574#undef main
575int main(int argc, char* argv[]) {
576 int delta;
577
578 wm->init();
579 wm->_vgabuf = (byte*)calloc(320,200);
580 wm->_scumm = &scumm;
581
582 sound.initialize(&scumm,&snd_driv);
583
584 wm->sound_init();
585
586 scumm._gui = &gui;
587 scumm.scummMain(argc, argv);
588 gui.init(&scumm);
589 delta = 0;
590 do {
591 updateScreen(&scumm);
592
593 waitForTimer(&scumm, delta*15);
594
595 if (gui._active) {
596 gui.loop();
597 delta = 3;
598 } else {
599 delta = scumm.scummLoop(delta);
600 }
601 } while(1);
602
603 return 0;
604}
605