scummvm/windows.cpp
2002-03-06 12:24:56 +00:00

570 lines
11 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2001 Ludvig Strigeus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*/
#include "stdafx.h"
#include <assert.h>
#include "scumm.h"
#include "sound.h"
#include "gui.h"
#if !defined(ALLOW_GDI)
#error The GDI driver is not as complete as the SDL driver. You need to define ALLOW_GDI to use this driver.
#endif
#define SRC_WIDTH 320
#define SRC_HEIGHT 200
#define SRC_PITCH (320)
#define DEST_WIDTH 320
#define DEST_HEIGHT 200
#define USE_DIRECTX 0
#define USE_DRAWDIB 0
#define USE_GDI 1
#define SAMPLES_PER_SEC 22050
#define BUFFER_SIZE (8192)
#define BITS_PER_SAMPLE 16
static bool shutdown;
static unsigned int scale;
#if USE_GDI
typedef struct DIB {
HBITMAP hSect;
byte *buf;
RGBQUAD *pal;
bool new_pal;
} DIB;
#endif
class WndMan {
HMODULE hInst;
HWND hWnd;
bool terminated;
#if USE_GDI
public:
DIB dib;
private:
#endif
public:
byte *_vgabuf;
Scumm *_scumm;
HANDLE _event;
DWORD _threadId;
HWAVEOUT _handle;
WAVEHDR _hdr[2];
public:
void init();
bool handleMessage();
void run();
void setPalette(byte *ctab, int first, int num);
void writeToScreen();
void prepare_header(WAVEHDR *wh, int i);
void sound_init();
static DWORD _stdcall sound_thread(WndMan *wm);
#if USE_GDI
bool allocateDIB(int w, int h);
#endif
};
void Error(const char *msg) {
OutputDebugString(msg);
MessageBoxA(0, msg, "Error", MB_ICONSTOP);
exit(1);
}
int sel;
Scumm scumm;
ScummDebugger debugger;
Gui gui;
SoundEngine sound;
SOUND_DRIVER_TYPE snd_driv;
WndMan wm[1];
byte veryFastMode;
void modifyslot(int sel, int what);
int mapKey(int key) {
if (key>=VK_F1 && key<=VK_F9) {
return key - VK_F1 + 315;
}
return key;
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
WndMan *wm = (WndMan*)GetWindowLong(hWnd, GWL_USERDATA);
switch (message)
{
case WM_DESTROY:
case WM_CLOSE:
exit(0);
break;
case WM_KEYDOWN:
if (wParam>='0' && wParam<='9') {
wm->_scumm->_saveLoadSlot = wParam - '0';
if (GetAsyncKeyState(VK_SHIFT)<0) {
sprintf(wm->_scumm->_saveLoadName, "Quicksave %d", wm->_scumm->_saveLoadSlot);
wm->_scumm->_saveLoadFlag = 1;
} else if (GetAsyncKeyState(VK_CONTROL)<0)
wm->_scumm->_saveLoadFlag = 2;
wm->_scumm->_saveLoadCompatible = false;
}
if (GetAsyncKeyState(VK_CONTROL)<0) {
if (wParam=='F') {
wm->_scumm->_fastMode ^= 1;
}
if (wParam=='G') {
veryFastMode ^= 1;
}
if (wParam=='D') {
debugger.attach(wm->_scumm);
}
if (wParam=='S') {
wm->_scumm->resourceStats();
}
}
wm->_scumm->_keyPressed = mapKey(wParam);
break;
case WM_MOUSEMOVE:
wm->_scumm->mouse.x = ((int16*)&lParam)[0];
wm->_scumm->mouse.y = ((int16*)&lParam)[1];
break;
case WM_LBUTTONDOWN:
wm->_scumm->_leftBtnPressed |= msClicked|msDown;
break;
case WM_LBUTTONUP:
wm->_scumm->_leftBtnPressed &= ~msDown;
break;
case WM_RBUTTONDOWN:
wm->_scumm->_rightBtnPressed |= msClicked|msDown;
break;
case WM_RBUTTONUP:
wm->_scumm->_rightBtnPressed &= ~msDown;
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
#if USE_GDI
bool WndMan::allocateDIB(int w, int h) {
struct {
BITMAPINFOHEADER bih;
RGBQUAD rgb[256];
} d;
if (dib.hSect)
return true;
memset(&d.bih, 0, sizeof(d.bih));
d.bih.biSize = sizeof(d.bih);
d.bih.biWidth = w;
d.bih.biHeight = -h;
d.bih.biPlanes = 1;
d.bih.biBitCount = 8;
d.bih.biCompression = BI_RGB;
memcpy(d.rgb, dib.pal, 256*sizeof(RGBQUAD));
dib.new_pal=false;
dib.hSect = CreateDIBSection(0, (BITMAPINFO*)&d, DIB_RGB_COLORS, (void**)&dib.buf,
NULL, NULL);
return dib.hSect != NULL;
}
void WndMan::writeToScreen() {
RECT r;
HDC dc,bmpdc;
HBITMAP bmpOld;
#if DEST_WIDTH==320
if (_vgabuf) {
for (int y=0; y<200; y++) {
memcpy(dib.buf + y*320,_vgabuf + y*320, 320);
}
}
#endif
r.left = r.top = 0;
r.right = DEST_WIDTH;
r.bottom = DEST_HEIGHT;
dc = GetDC(hWnd);
bmpdc = CreateCompatibleDC(dc);
bmpOld = (HBITMAP)SelectObject(bmpdc, dib.hSect);
if (dib.new_pal) {
dib.new_pal = false;
SetDIBColorTable(bmpdc, 0, 256, dib.pal);
}
SetStretchBltMode(dc, BLACKONWHITE);
#if DEST_WIDTH==320
StretchBlt(dc, r.left, r.top, r.right-r.left, r.bottom-r.top, bmpdc, 0, 0, 320,200, SRCCOPY);
#endif
SelectObject(bmpdc, bmpOld);
DeleteDC(bmpdc);
ReleaseDC(hWnd, dc);
}
void WndMan::setPalette(byte *ctab, int first, int num) {
int i;
for (i=0; i<256; i++) {
dib.pal[i].rgbRed = ctab[i*3+0];
dib.pal[i].rgbGreen = ctab[i*3+1];
dib.pal[i].rgbBlue = ctab[i*3+2];
}
dib.new_pal = true;
}
#endif
HWND globWnd;
void WndMan::init() {
/* Retrieve the handle of this module */
hInst = GetModuleHandle(NULL);
/* Register the window class */
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInst;
wcex.hIcon = 0;
wcex.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = 0;
wcex.lpszClassName = "ScummVM";
wcex.hIconSm = 0;
if (!RegisterClassEx(&wcex))
Error("Cannot register window class!");
#if USE_GDI
globWnd = hWnd = CreateWindow("ScummVM", "ScummVM", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, DEST_WIDTH+10, DEST_HEIGHT+30, NULL, NULL, hInst, NULL);
SetWindowLong(hWnd, GWL_USERDATA, (long)this);
dib.pal = (RGBQUAD*)calloc(sizeof(RGBQUAD),256);
dib.new_pal = false;
if (!allocateDIB(DEST_WIDTH, DEST_HEIGHT))
Error("allocateDIB failed!");
ShowWindow(hWnd, SW_SHOW);
#endif
}
bool WndMan::handleMessage() {
MSG msg;
if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
return false;
if (msg.message==WM_QUIT) {
terminated=true;
exit(1);
return true;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
return true;
}
unsigned long rdtsc_timer;
void _declspec(naked) beginpentiumtest() {
_asm {
rdtsc
mov rdtsc_timer,eax
ret
}
}
int _declspec(naked) endpentiumtest() {
_asm {
rdtsc
sub eax,rdtsc_timer
ret
}
}
void decompressMask(byte *d, byte *s, int w=320, int h=144) {
int x,y;
for (y=0; y<h; y++) {
byte *p = s+y*40;
byte *pd = d + y*320;
byte bits = 0x80, bdata = *p++;
for (x=0; x<w; x++) {
*pd++ = (bdata & bits) ? 128 : 0;
bits>>=1;
if (!bits) {
bdata = *p++;
bits=0x80;
}
}
}
}
void outputlittlemask(byte *mask, int w, int h) {
byte *old = wm->_vgabuf;
wm->_vgabuf = NULL;
decompressMask(wm->dib.buf, mask, w, h);
wm->writeToScreen();
wm->_vgabuf = old;
}
void outputdisplay2(Scumm *s, int disp) {
byte *old = wm->_vgabuf;
byte buf[64000];
switch(disp) {
case 0:
wm->_vgabuf = buf;
memcpy(buf, wm->_vgabuf, 64000);
memcpy(buf,s->getResourceAddress(rtBuffer, 5),320*200);
break;
case 1:
wm->_vgabuf = buf;
memcpy(buf, wm->_vgabuf, 64000);
memcpy(buf,s->getResourceAddress(rtBuffer, 1),320*200);
break;
case 2:
wm->_vgabuf = NULL;
decompressMask(wm->dib.buf, s->getResourceAddress(rtBuffer, 9)+s->_screenStartStrip);
break;
case 3:
wm->_vgabuf = NULL;
decompressMask(wm->dib.buf, s->getResourceAddress(rtBuffer, 9)+8160+s->_screenStartStrip);
break;
case 4:
wm->_vgabuf = NULL;
decompressMask(wm->dib.buf, s->getResourceAddress(rtBuffer, 9)+8160*2+s->_screenStartStrip);
break;
case 5:
wm->_vgabuf = NULL;
decompressMask(wm->dib.buf, s->getResourceAddress(rtBuffer, 9)+8160*3+s->_screenStartStrip);
break;
}
wm->writeToScreen();
wm->_vgabuf = old;
}
void blitToScreen(Scumm *s, byte *src,int x, int y, int w, int h) {
byte *dst;
SDL_Rect *r;
int i;
dst = (byte*)wm->_vgabuf + y*320 + x;
do {
memcpy(dst, src, w);
dst += 320;
src += 320;
} while (--h);
}
void setShakePos(Scumm *s, int shake_pos) {}
int clock;
void updateScreen(Scumm *s) {
if (s->_palDirtyMax != -1) {
wm->setPalette(s->_currentPalette, 0, 256);
s->_palDirtyMax = -1;
}
wm->writeToScreen();
}
void waitForTimer(Scumm *s, int delay) {
wm->handleMessage();
if (!veryFastMode) {
assert(delay<5000);
if (s->_fastMode)
delay=10;
Sleep(delay);
}
}
void initGraphics(Scumm *s, bool fullScreen, unsigned int scaleFactor) {
if(fullScreen)
warning("Use SDL for fullscreen support");
scale = scaleFactor; // not supported yet! ignored.
}
void drawMouse(Scumm *s, int, int, int, byte*, bool) {
}
void drawMouse(Scumm *s, int x, int y, int w, int h, byte *buf, bool visible) {
}
void fill_buffer(int16 *buf, int len) {
scumm.mixWaves(buf, len);
}
void cd_playtrack(int track, int offset, int delay) { // FIXME: Add for Win32
;
}
void WndMan::prepare_header(WAVEHDR *wh, int i) {
memset(wh, 0, sizeof(WAVEHDR));
wh->lpData = (char*)malloc(BUFFER_SIZE);
wh->dwBufferLength = BUFFER_SIZE;
waveOutPrepareHeader(_handle, wh, sizeof(WAVEHDR));
fill_buffer((int16*)wh->lpData, wh->dwBufferLength>>1);
waveOutWrite(_handle, wh, sizeof(WAVEHDR));
}
void WndMan::sound_init() {
WAVEFORMATEX wfx;
memset(&wfx, 0, sizeof(wfx));
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 1;
wfx.nSamplesPerSec = SAMPLES_PER_SEC;
wfx.nAvgBytesPerSec = SAMPLES_PER_SEC * BITS_PER_SAMPLE / 8;
wfx.wBitsPerSample = BITS_PER_SAMPLE;
wfx.nBlockAlign = BITS_PER_SAMPLE * 1 / 8;
CreateThread(NULL, 0, (unsigned long (__stdcall *)(void *))&sound_thread, this, 0, &_threadId);
SetThreadPriority((void*)_threadId, THREAD_PRIORITY_HIGHEST);
_event = CreateEvent(NULL, false, false, NULL);
memset(_hdr,0,sizeof(_hdr));
waveOutOpen(&_handle, WAVE_MAPPER, &wfx, (long)_event, (long)this, CALLBACK_EVENT );
prepare_header(&_hdr[0], 0);
prepare_header(&_hdr[1], 1);
}
DWORD _stdcall WndMan::sound_thread(WndMan *wm) {
int i;
bool signaled;
int time = GetTickCount(), cur;
while (1) {
if (!snd_driv.wave_based()) {
cur = GetTickCount();
while (time < cur) {
sound.on_timer();
time += 10;
}
}
signaled = WaitForSingleObject(wm->_event, time - cur) == WAIT_OBJECT_0;
if (signaled) {
for(i=0; i<2; i++) {
WAVEHDR *hdr = &wm->_hdr[i];
if (hdr->dwFlags & WHDR_DONE) {
fill_buffer((int16*)hdr->lpData, hdr->dwBufferLength>>1);
waveOutWrite(wm->_handle, hdr, sizeof(WAVEHDR));
}
}
}
}
}
#undef main
int main(int argc, char* argv[]) {
int delta;
wm->init();
wm->_vgabuf = (byte*)calloc(320,200);
wm->_scumm = &scumm;
sound.initialize(&scumm,&snd_driv);
wm->sound_init();
scumm._gui = &gui;
scumm.scummMain(argc, argv);
if (!(scumm._features & GF_SMALL_HEADER))
gui.init(&scumm);
delta = 0;
do {
updateScreen(&scumm);
waitForTimer(&scumm, delta*15);
if (gui._active) {
gui.loop();
delta = 3;
} else {
delta = scumm.scummLoop(delta);
}
} while(1);
return 0;
}
void BoxTest(int num) {;} // Test code