mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
d2b0070c5f
simon the sorcerer 1 & 2 support (non SCUMM games) svn-id: r3912
620 lines
12 KiB
C++
620 lines
12 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 "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;
|
|
SoundEngine 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
|