/* AWE - Another World Engine * Copyright (C) 2004 Gregory Montoir * Copyright (C) 2004 The ScummVM project * * 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. */ #include "stdafx.h" #include "common/util.h" #include #include "awe.h" #include "systemstub.h" #include "util.h" namespace Awe { struct SDLStub : SystemStub { typedef void (SDLStub::*ScaleProc)(uint16 *dst, uint16 dstPitch, const uint16 *src, uint16 srcPitch, uint16 w, uint16 h); enum { SCREEN_W = 320, SCREEN_H = 200 }; struct Scaler { const char *name; ScaleProc proc; uint8 factor; }; static const Scaler _scalers[]; uint8 *_offscreen; SDL_Surface *_screen; SDL_Surface *_sclscreen; bool _fullscreen; uint8 _scaler; uint16 _pal[16]; virtual ~SDLStub() {} virtual void init(const char *title); virtual void destroy(); virtual void setPalette(uint8 s, uint8 n, const uint8 *buf); virtual void copyRect(uint16 x, uint16 y, uint16 w, uint16 h, const uint8 *buf, uint32 pitch); virtual void processEvents(); virtual void sleep(uint32 duration); virtual uint32 getTimeStamp(); void prepareGfxMode(); void cleanupGfxMode(); void switchGfxMode(bool fullscreen, uint8 scaler); void point1x(uint16 *dst, uint16 dstPitch, const uint16 *src, uint16 srcPitch, uint16 w, uint16 h); void point2x(uint16 *dst, uint16 dstPitch, const uint16 *src, uint16 srcPitch, uint16 w, uint16 h); void point3x(uint16 *dst, uint16 dstPitch, const uint16 *src, uint16 srcPitch, uint16 w, uint16 h); }; const SDLStub::Scaler SDLStub::_scalers[] = { { "Point1x", &SDLStub::point1x, 1 }, { "Point2x", &SDLStub::point2x, 2 }, { "Point3x", &SDLStub::point3x, 3 } }; SystemStub *SystemStub_SDL_create() { return new SDLStub(); } void SDLStub::init(const char *title) { SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER); SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); SDL_ShowCursor(SDL_DISABLE); SDL_WM_SetCaption(title, NULL); memset(&_pi, 0, sizeof(_pi)); _offscreen = (uint8 *)malloc(SCREEN_W * SCREEN_H * 2); if (!_offscreen) { ::error("Unable to allocate offscreen buffer"); } _fullscreen = false; _scaler = 1; prepareGfxMode(); } void SDLStub::destroy() { cleanupGfxMode(); } void SDLStub::setPalette(uint8 s, uint8 n, const uint8 *buf) { assert(s + n <= 16); for (int i = s; i < s + n; ++i) { uint8 c[3]; for (int j = 0; j < 3; ++j) { uint8 col = buf[i * 3 + j]; c[j] = (col << 2) | (col & 3); } _pal[i] = SDL_MapRGB(_screen->format, c[0], c[1], c[2]); } } void SDLStub::copyRect(uint16 x, uint16 y, uint16 w, uint16 h, const uint8 *buf, uint32 pitch) { buf += y * pitch + x; uint16 *p = (uint16 *)_offscreen; while (h--) { for (int i = 0; i < w / 2; ++i) { uint8 p1 = *(buf + i) >> 4; uint8 p2 = *(buf + i) & 0xF; *(p + i * 2 + 0) = _pal[p1]; *(p + i * 2 + 1) = _pal[p2]; } p += SCREEN_W; buf += pitch; } SDL_LockSurface(_sclscreen); (this->*_scalers[_scaler].proc)((uint16 *)_sclscreen->pixels, _sclscreen->pitch, (uint16 *)_offscreen, SCREEN_W, SCREEN_W, SCREEN_H); SDL_UnlockSurface(_sclscreen); SDL_BlitSurface(_sclscreen, NULL, _screen, NULL); SDL_UpdateRect(_screen, 0, 0, 0, 0); } void SDLStub::processEvents() { SDL_Event ev; while(SDL_PollEvent(&ev)) { switch (ev.type) { case SDL_QUIT: exit(0); break; case SDL_KEYUP: switch(ev.key.keysym.sym) { case SDLK_LEFT: _pi.dirMask &= ~PlayerInput::DIR_LEFT; break; case SDLK_RIGHT: _pi.dirMask &= ~PlayerInput::DIR_RIGHT; break; case SDLK_UP: _pi.dirMask &= ~PlayerInput::DIR_UP; break; case SDLK_DOWN: _pi.dirMask &= ~PlayerInput::DIR_DOWN; break; case SDLK_SPACE: case SDLK_RETURN: _pi.button = false; break; default: break; } break; case SDL_KEYDOWN: if (ev.key.keysym.mod & KMOD_ALT) { if (ev.key.keysym.sym == SDLK_RETURN) { switchGfxMode(!_fullscreen, _scaler); } else if (ev.key.keysym.sym == SDLK_KP_PLUS) { uint8 s = _scaler + 1; if (s < ARRAYSIZE(_scalers)) { switchGfxMode(_fullscreen, s); } } else if (ev.key.keysym.sym == SDLK_KP_MINUS) { int8 s = _scaler - 1; if (_scaler > 0) { switchGfxMode(_fullscreen, s); } } else if (ev.key.keysym.sym == SDLK_x) { _pi.quit = true; } break; } else if (ev.key.keysym.mod & KMOD_CTRL) { if (ev.key.keysym.sym == SDLK_s) { _pi.save = true; } else if (ev.key.keysym.sym == SDLK_l) { _pi.load = true; } else if (ev.key.keysym.sym == SDLK_f) { _pi.fastMode = true; } else if (ev.key.keysym.sym == SDLK_KP_PLUS) { _pi.stateSlot = 1; } else if (ev.key.keysym.sym == SDLK_KP_MINUS) { _pi.stateSlot = -1; } break; } _pi.lastChar = ev.key.keysym.sym; switch(ev.key.keysym.sym) { case SDLK_LEFT: _pi.dirMask |= PlayerInput::DIR_LEFT; break; case SDLK_RIGHT: _pi.dirMask |= PlayerInput::DIR_RIGHT; break; case SDLK_UP: _pi.dirMask |= PlayerInput::DIR_UP; break; case SDLK_DOWN: _pi.dirMask |= PlayerInput::DIR_DOWN; break; case SDLK_SPACE: case SDLK_RETURN: _pi.button = true; break; case SDLK_c: _pi.code = true; break; case SDLK_p: _pi.pause = true; break; default: break; } break; default: break; } } } void SDLStub::sleep(uint32 duration) { SDL_Delay(duration); } uint32 SDLStub::getTimeStamp() { return SDL_GetTicks(); } void SDLStub::prepareGfxMode() { int w = SCREEN_W * _scalers[_scaler].factor; int h = SCREEN_H * _scalers[_scaler].factor; _screen = SDL_SetVideoMode(w, h, 16, _fullscreen ? (SDL_FULLSCREEN | SDL_HWSURFACE) : SDL_HWSURFACE); if (!_screen) { ::error("SDLStub::prepareGfxMode() unable to allocate _screen buffer"); } _sclscreen = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 16, _screen->format->Rmask, _screen->format->Gmask, _screen->format->Bmask, _screen->format->Amask); if (!_sclscreen) { ::error("SDLStub::prepareGfxMode() unable to allocate _sclscreen buffer"); } } void SDLStub::cleanupGfxMode() { if (_offscreen) { free(_offscreen); _offscreen = 0; } if (_sclscreen) { SDL_FreeSurface(_sclscreen); _sclscreen = 0; } if (_screen) { SDL_FreeSurface(_screen); _screen = 0; } } void SDLStub::switchGfxMode(bool fullscreen, uint8 scaler) { SDL_Surface *prev_sclscreen = _sclscreen; SDL_FreeSurface(_screen); _fullscreen = fullscreen; _scaler = scaler; prepareGfxMode(); SDL_BlitSurface(prev_sclscreen, NULL, _sclscreen, NULL); SDL_FreeSurface(prev_sclscreen); } void SDLStub::point1x(uint16 *dst, uint16 dstPitch, const uint16 *src, uint16 srcPitch, uint16 w, uint16 h) { dstPitch >>= 1; while (h--) { memcpy(dst, src, w * 2); dst += dstPitch; src += dstPitch; } } void SDLStub::point2x(uint16 *dst, uint16 dstPitch, const uint16 *src, uint16 srcPitch, uint16 w, uint16 h) { dstPitch >>= 1; while (h--) { uint16 *p = dst; for (int i = 0; i < w; ++i, p += 2) { uint16 c = *(src + i); *(p + 0) = c; *(p + 1) = c; *(p + 0 + dstPitch) = c; *(p + 1 + dstPitch) = c; } dst += dstPitch * 2; src += srcPitch; } } void SDLStub::point3x(uint16 *dst, uint16 dstPitch, const uint16 *src, uint16 srcPitch, uint16 w, uint16 h) { dstPitch >>= 1; while (h--) { uint16 *p = dst; for (int i = 0; i < w; ++i, p += 3) { uint16 c = *(src + i); *(p + 0) = c; *(p + 1) = c; *(p + 2) = c; *(p + 0 + dstPitch) = c; *(p + 1 + dstPitch) = c; *(p + 2 + dstPitch) = c; *(p + 0 + dstPitch * 2) = c; *(p + 1 + dstPitch * 2) = c; *(p + 2 + dstPitch * 2) = c; } dst += dstPitch * 3; src += srcPitch; } } }