/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/stream.h" #include "graphics/palette.h" #include "gob/gob.h" #include "gob/util.h" #include "gob/global.h" #include "gob/dataio.h" #include "gob/draw.h" #include "gob/game.h" #include "gob/video.h" #include "gob/videoplayer.h" #include "gob/sound/sound.h" namespace Gob { Util::Util(GobEngine *vm) : _vm(vm) { _mouseButtons = kMouseButtonsNone, _keyBufferHead = 0; _keyBufferTail = 0; _fastMode = 0; _frameRate = 12; _frameWaitTime = 0; _startFrameTime = 0; _keyState = 0; } uint32 Util::getTimeKey() { return g_system->getMillis() * _vm->_global->_speedFactor; } int16 Util::getRandom(int16 max) { if (max == 0) return 0; return _vm->_rnd.getRandomNumber(max - 1); } void Util::beep(int16 freq) { if (_vm->_global->_soundFlags == 0) return; _vm->_sound->speakerOn(freq, 50); } void Util::notifyPaused(uint32 duration) { _startFrameTime += duration; } void Util::delay(uint16 msecs) { g_system->delayMillis(msecs / _vm->_global->_speedFactor); } void Util::longDelay(uint16 msecs) { uint32 time = g_system->getMillis() * _vm->_global->_speedFactor + msecs; do { _vm->_video->waitRetrace(); processInput(); delay(15); } while (!_vm->shouldQuit() && ((g_system->getMillis() * _vm->_global->_speedFactor) < time)); } void Util::initInput() { _mouseButtons = kMouseButtonsNone; _keyBufferHead = _keyBufferTail = 0; } void Util::processInput(bool scroll) { Common::Event event; Common::EventManager *eventMan = g_system->getEventManager(); int16 x = 0, y = 0; bool hasMove = false; _vm->_vidPlayer->updateLive(); while (eventMan->pollEvent(event)) { switch (event.type) { case Common::EVENT_MOUSEMOVE: hasMove = true; x = event.mouse.x; y = event.mouse.y; break; case Common::EVENT_LBUTTONDOWN: _mouseButtons = (MouseButtons) (((uint32) _mouseButtons) | ((uint32) kMouseButtonsLeft)); break; case Common::EVENT_RBUTTONDOWN: _mouseButtons = (MouseButtons) (((uint32) _mouseButtons) | ((uint32) kMouseButtonsRight)); break; case Common::EVENT_LBUTTONUP: _mouseButtons = (MouseButtons) (((uint32) _mouseButtons) & ~((uint32) kMouseButtonsLeft)); break; case Common::EVENT_RBUTTONUP: _mouseButtons = (MouseButtons) (((uint32) _mouseButtons) & ~((uint32) kMouseButtonsRight)); break; case Common::EVENT_KEYDOWN: keyDown(event); if (event.kbd.hasFlags(Common::KBD_CTRL)) { if (event.kbd.keycode == Common::KEYCODE_f) _fastMode ^= 1; else if (event.kbd.keycode == Common::KEYCODE_g) _fastMode ^= 2; else if (event.kbd.keycode == Common::KEYCODE_p) _vm->pauseGame(); else if (event.kbd.keycode == Common::KEYCODE_d) { _vm->getDebugger()->attach(); _vm->getDebugger()->onFrame(); } break; } addKeyToBuffer(event.kbd); break; case Common::EVENT_KEYUP: keyUp(event); break; default: break; } } _vm->_global->_speedFactor = MIN(_fastMode + 1, 3); if (hasMove && scroll) { x = CLIP(x, _vm->_global->_mouseMinX, _vm->_global->_mouseMaxX); y = CLIP(y, _vm->_global->_mouseMinY, _vm->_global->_mouseMaxY); x -= _vm->_video->_screenDeltaX; y -= _vm->_video->_screenDeltaY; _vm->_util->setMousePos(x, y); _vm->_game->wantScroll(x, y); // WORKAROUND: // Force a check of the mouse in order to fix the sofa bug. This apply only for Gob3, and only // in the impacted TOT file so that the second screen animation is not broken. if ((_vm->getGameType() == kGameTypeGob3) && _vm->isCurrentTot("EMAP1008.TOT")) _vm->_game->evaluateScroll(); } } void Util::clearKeyBuf() { processInput(); _keyBufferHead = _keyBufferTail = 0; } bool Util::keyBufferEmpty() { return (_keyBufferHead == _keyBufferTail); } void Util::addKeyToBuffer(const Common::KeyState &key) { if ((_keyBufferHead + 1) % KEYBUFSIZE == _keyBufferTail) { warning("key buffer overflow"); return; } _keyBuffer[_keyBufferHead] = key; _keyBufferHead = (_keyBufferHead + 1) % KEYBUFSIZE; } bool Util::getKeyFromBuffer(Common::KeyState &key) { if (_keyBufferHead == _keyBufferTail) return false; key = _keyBuffer[_keyBufferTail]; _keyBufferTail = (_keyBufferTail + 1) % KEYBUFSIZE; return true; } static const uint16 kLatin1ToCP850[] = { 0xFF, 0xAD, 0xBD, 0x9C, 0xCF, 0xBE, 0xDD, 0xF5, 0xF9, 0xB8, 0xA6, 0xAE, 0xAA, 0xF0, 0xA9, 0xEE, 0xF8, 0xF1, 0xFD, 0xFC, 0xEF, 0xE6, 0xF4, 0xFA, 0xF7, 0xFB, 0xA7, 0xAF, 0xAC, 0xAB, 0xF3, 0xA8, 0xB7, 0xB5, 0xB6, 0xC7, 0x8E, 0x8F, 0x92, 0x80, 0xD4, 0x90, 0xD2, 0xD3, 0xDE, 0xD6, 0xD7, 0xD8, 0xD1, 0xA5, 0xE3, 0xE0, 0xE2, 0xE5, 0x99, 0x9E, 0x9D, 0xEB, 0xE9, 0xEA, 0x9A, 0xED, 0xE8, 0xE1, 0x85, 0xA0, 0x83, 0xC6, 0x84, 0x86, 0x91, 0x87, 0x8A, 0x82, 0x88, 0x89, 0x8D, 0xA1, 0x8C, 0x8B, 0xD0, 0xA4, 0x95, 0xA2, 0x93, 0xE4, 0x94, 0xF6, 0x9B, 0x97, 0xA3, 0x96, 0x81, 0xEC, 0xE7, 0x98 }; int16 Util::toCP850(uint16 latin1) { if ((latin1 < 0xA0) || ((latin1 - 0xA0) >= ARRAYSIZE(kLatin1ToCP850))) return 0; return kLatin1ToCP850[latin1 - 0xA0]; } int16 Util::translateKey(const Common::KeyState &key) { static struct keyS { int16 from; int16 to; } keys[] = { {Common::KEYCODE_BACKSPACE, kKeyBackspace}, {Common::KEYCODE_SPACE, kKeySpace }, {Common::KEYCODE_RETURN, kKeyReturn }, {Common::KEYCODE_ESCAPE, kKeyEscape }, {Common::KEYCODE_DELETE, kKeyDelete }, {Common::KEYCODE_UP, kKeyUp }, {Common::KEYCODE_DOWN, kKeyDown }, {Common::KEYCODE_RIGHT, kKeyRight }, {Common::KEYCODE_LEFT, kKeyLeft }, {Common::KEYCODE_F1, kKeyF1 }, {Common::KEYCODE_F2, kKeyF2 }, {Common::KEYCODE_F3, kKeyF3 }, {Common::KEYCODE_F4, kKeyF4 }, {Common::KEYCODE_F5, kKeyEscape }, {Common::KEYCODE_F6, kKeyF6 }, {Common::KEYCODE_F7, kKeyF7 }, {Common::KEYCODE_F8, kKeyF8 }, {Common::KEYCODE_F9, kKeyF9 }, {Common::KEYCODE_F10, kKeyF10 } }; // Translate special keys for (int i = 0; i < ARRAYSIZE(keys); i++) if (key.keycode == keys[i].from) return keys[i].to; // Return the ascii value, for text input if ((key.ascii >= 32) && (key.ascii <= 127)) return key.ascii; // Translate international characters into CP850 characters if ((key.ascii >= 160) && (key.ascii <= 255)) return toCP850(key.ascii); return 0; } static const uint8 kLowerToUpper[][2] = { {0x81, 0x9A}, {0x82, 0x90}, {0x83, 0xB6}, {0x84, 0x8E}, {0x85, 0xB7}, {0x86, 0x8F}, {0x87, 0x80}, {0x88, 0xD2}, {0x89, 0xD3}, {0x8A, 0xD4}, {0x8B, 0xD8}, {0x8C, 0xD7}, {0x8D, 0xDE}, {0x91, 0x92}, {0x93, 0xE2}, {0x94, 0x99}, {0x95, 0xE3}, {0x96, 0xEA}, {0x97, 0xEB}, {0x95, 0xE3}, {0x96, 0xEA}, {0x97, 0xEB}, {0x9B, 0x9D}, {0xA0, 0xB5}, {0xA1, 0xD6}, {0xA2, 0xE0}, {0xA3, 0xE9}, {0xA4, 0xA5}, {0xC6, 0xC7}, {0xD0, 0xD1}, {0xE4, 0xE5}, {0xE7, 0xE8}, {0xEC, 0xED} }; char Util::toCP850Lower(char cp850) { const uint8 cp = (unsigned char)cp850; if (cp <= 32) return cp850; if (cp <= 127) return tolower(cp850); for (uint i = 0; i < ARRAYSIZE(kLowerToUpper); i++) if (cp == kLowerToUpper[i][1]) return (char)kLowerToUpper[i][0]; return cp850; } char Util::toCP850Upper(char cp850) { const uint8 cp = (unsigned char)cp850; if (cp <= 32) return cp850; if (cp <= 127) return toupper(cp850); for (uint i = 0; i < ARRAYSIZE(kLowerToUpper); i++) if (cp == kLowerToUpper[i][0]) return (char)kLowerToUpper[i][1]; return cp850; } int16 Util::getKey() { Common::KeyState key; while (!getKeyFromBuffer(key)) { processInput(); if (keyBufferEmpty()) g_system->delayMillis(10 / _vm->_global->_speedFactor); } return translateKey(key); } int16 Util::checkKey() { Common::KeyState key; getKeyFromBuffer(key); return translateKey(key); } bool Util::checkKey(int16 &key) { Common::KeyState keyS; if (!getKeyFromBuffer(keyS)) return false; key = translateKey(keyS); return true; } bool Util::keyPressed() { int16 key = checkKey(); if (key) return true; int16 x, y; MouseButtons buttons; getMouseState(&x, &y, &buttons); return buttons != kMouseButtonsNone; } void Util::getMouseState(int16 *pX, int16 *pY, MouseButtons *pButtons) { Common::Point mouse = g_system->getEventManager()->getMousePos(); *pX = mouse.x + _vm->_video->_scrollOffsetX - _vm->_video->_screenDeltaX; *pY = mouse.y + _vm->_video->_scrollOffsetY - _vm->_video->_screenDeltaY; if (pButtons != 0) *pButtons = _mouseButtons; } void Util::setMousePos(int16 x, int16 y) { x = CLIP(x + _vm->_video->_screenDeltaX, 0, _vm->_width - 1); y = CLIP(y + _vm->_video->_screenDeltaY, 0, _vm->_height - 1); g_system->warpMouse(x, y); } void Util::waitMouseUp() { do { processInput(); if (_mouseButtons != kMouseButtonsNone) delay(10); } while (_mouseButtons != kMouseButtonsNone); } void Util::waitMouseDown() { int16 x; int16 y; MouseButtons buttons; do { processInput(); getMouseState(&x, &y, &buttons); if (buttons == 0) delay(10); } while (buttons == 0); } void Util::waitMouseRelease(char drawMouse) { MouseButtons buttons; int16 mouseX; int16 mouseY; _vm->_game->checkKeys(&mouseX, &mouseY, &buttons, drawMouse); while (buttons != 0) { if (drawMouse != 0) _vm->_draw->animateCursor(2); delay(10); _vm->_game->checkKeys(&mouseX, &mouseY, &buttons, drawMouse); } } void Util::forceMouseUp(bool onlyWhenSynced) { if (onlyWhenSynced && (_vm->_game->_mouseButtons != _mouseButtons)) return; _vm->_game->_mouseButtons = kMouseButtonsNone; _mouseButtons = kMouseButtonsNone; } void Util::clearPalette() { int16 i; byte colors[768]; _vm->validateVideoMode(_vm->_global->_videoMode); if (_vm->_global->_setAllPalette) { if (_vm->getPixelFormat().bytesPerPixel == 1) { memset(colors, 0, sizeof(colors)); g_system->getPaletteManager()->setPalette(colors, 0, 256); } return; } for (i = 0; i < 16; i++) _vm->_video->setPalElem(i, 0, 0, 0, 0, _vm->_global->_videoMode); } int16 Util::getFrameRate() { return _frameRate; } void Util::setFrameRate(int16 rate) { if (rate == 0) rate = 1; _frameRate = rate; _frameWaitTime = 1000 / rate; _startFrameTime = getTimeKey(); } void Util::notifyNewAnim() { _startFrameTime = getTimeKey(); } void Util::waitEndFrame(bool handleInput) { int32 time; time = getTimeKey() - _startFrameTime; if ((time > 1000) || (time < 0)) { _vm->_video->retrace(); _startFrameTime = getTimeKey(); return; } int32 toWait = 0; do { if (toWait > 0) delay(MIN(toWait, 10)); if (handleInput) processInput(); _vm->_video->retrace(); time = getTimeKey() - _startFrameTime; toWait = _frameWaitTime - time; } while (toWait > 0); _startFrameTime = getTimeKey(); } void Util::setScrollOffset(int16 x, int16 y) { processInput(); if (x >= 0) _vm->_video->_scrollOffsetX = x; else _vm->_video->_scrollOffsetX = _vm->_draw->_scrollOffsetX; if (y >= 0) _vm->_video->_scrollOffsetY = y; else _vm->_video->_scrollOffsetY = _vm->_draw->_scrollOffsetY; _vm->_video->waitRetrace(); } void Util::insertStr(const char *str1, char *str2, int16 pos) { int len1 = strlen(str1); int len2 = strlen(str2); int from = MIN((int) pos, len2); for (int i = len2; i >= from; i--) str2[len1 + i] = str2[i]; for (int i = 0; i < len1; i++) str2[i + from] = str1[i]; } void Util::cutFromStr(char *str, int16 from, int16 cutlen) { int len = strlen(str); if (from >= len) return; if ((from + cutlen) > len) { str[from] = 0; return; } int i = from; do { str[i] = str[i + cutlen]; i++; } while (str[i] != 0); } void Util::replaceChar(char *str, char c1, char c2) { while ((str = strchr(str, c1))) *str = c2; } static const char trStr1[] = " ' + - :0123456789: <=> abcdefghijklmnopqrstuvwxyz " "abcdefghijklmnopqrstuvwxyz "; static const char trStr2[] = " ueaaaaceeeiii ooouu aioun" " "; static const char trStr3[] = " "; void Util::cleanupStr(char *str) { char *start, *end; char buf[300]; strcpy(buf, trStr1); strcat(buf, trStr2); strcat(buf, trStr3); // Translating "wrong" characters for (size_t i = 0; i < strlen(str); i++) str[i] = buf[MIN(str[i] - 32, 32)]; // Trim spaces left while (str[0] == ' ') cutFromStr(str, 0, 1); // Trim spaces right while ((*str != '\0') && (str[strlen(str) - 1] == ' ')) cutFromStr(str, strlen(str) - 1, 1); // Merge double spaces start = strchr(str, ' '); while (start) { if (start[1] == ' ') { cutFromStr(str, start - str, 1); continue; } end = strchr(start + 1, ' '); start = end ? end + 1 : 0; } } void Util::listInsertFront(List *list, void *data) { ListNode *node; node = new ListNode; if (list->pHead) { node->pData = data; node->pNext = list->pHead; node->pPrev = 0; list->pHead->pPrev = node; list->pHead = node; } else { list->pHead = node; list->pTail = node; node->pData = data; node->pNext = 0; node->pPrev = 0; } } void Util::listInsertBack(List *list, void *data) { ListNode *node; if (list->pHead != 0) { if (list->pTail == 0) { list->pTail = list->pHead; warning("Util::listInsertBack(): Broken list"); } node = new ListNode; node->pData = data; node->pPrev = list->pTail; node->pNext = 0; list->pTail->pNext = node; list->pTail = node; } else listInsertFront(list, data); } void Util::listDropFront(List *list) { if (list->pHead->pNext == 0) { delete list->pHead; list->pHead = 0; list->pTail = 0; } else { list->pHead = list->pHead->pNext; delete list->pHead->pPrev; list->pHead->pPrev = 0; } } void Util::deleteList(List *list) { while (list->pHead) listDropFront(list); delete list; } char *Util::setExtension(char *str, const char *ext) { assert(str && ext); if (str[0] == '\0') return str; char *dot = strrchr(str, '.'); if (dot) *dot = '\0'; strcat(str, ext); return str; } Common::String Util::setExtension(const Common::String &str, const Common::String &ext) { if (str.empty()) return str; const char *dot = strrchr(str.c_str(), '.'); if (dot) return Common::String(str.c_str(), dot - str.c_str()) + ext; return str + ext; } Common::String Util::readString(Common::SeekableReadStream &stream, int n) { Common::String str; char c; while (n-- > 0) { if ((c = stream.readByte()) == '\0') break; str += c; } if (n > 0) stream.skip(n); return str; } /* NOT IMPLEMENTED */ void Util::checkJoystick() { _vm->_global->_useJoystick = 0; } uint32 Util::getKeyState() const { return _keyState; } void Util::keyDown(const Common::Event &event) { if (event.kbd.keycode == Common::KEYCODE_UP) _keyState |= 0x0001; else if (event.kbd.keycode == Common::KEYCODE_DOWN) _keyState |= 0x0002; else if (event.kbd.keycode == Common::KEYCODE_RIGHT) _keyState |= 0x0004; else if (event.kbd.keycode == Common::KEYCODE_LEFT) _keyState |= 0x0008; else if (event.kbd.keycode == Common::KEYCODE_SPACE) _keyState |= 0x0020; else if (event.kbd.keycode == Common::KEYCODE_ESCAPE) _keyState |= 0x0040; } void Util::keyUp(const Common::Event &event) { if (event.kbd.keycode == Common::KEYCODE_UP) _keyState &= ~0x0001; else if (event.kbd.keycode == Common::KEYCODE_DOWN) _keyState &= ~0x0002; else if (event.kbd.keycode == Common::KEYCODE_RIGHT) _keyState &= ~0x0004; else if (event.kbd.keycode == Common::KEYCODE_LEFT) _keyState &= ~0x0008; else if (event.kbd.keycode == Common::KEYCODE_SPACE) _keyState &= ~0x0020; else if (event.kbd.keycode == Common::KEYCODE_ESCAPE) _keyState &= ~0x0040; } } // End of namespace Gob