/* 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 #include "scumm.h" #include "cdmusic.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; IMuse sound; SOUND_DRIVER_TYPE snd_driv; WndMan wm[1]; byte veryFastMode; void modifyslot(int sel, int what); void launcherLoop() {; } int mapKey(int key) { if (key >= VK_F1 && key <= VK_F9) { return key - VK_F1 + 315; } return key; } // FIXME: CD Music Stubs void cd_playtrack(int track, int offset, int delay) {; } void cd_play(Scumm *s, int track, int num_loops, int start_frame) {; } void cd_stop() {; } int cd_is_running() { return 0; } 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; 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(int, int, int, byte *, bool) { } void drawMouse(int x, int y, int w, int h, byte *buf, bool visible) { } void fill_buffer(int16 * buf, int len) { scumm.mixWaves(buf, len); } 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) { cur = GetTickCount(); if (!snd_driv.wave_based()) { 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